add testing module; integrate holographic effects into vial_fill shader with noise texture, sum-of-sines water surface, top-down lighting, foam line, and font outlines for readability
This commit is contained in:
parent
308cf26a77
commit
7cfbf72ca5
10 changed files with 250 additions and 28 deletions
12
AGENTS.md
12
AGENTS.md
|
|
@ -18,6 +18,15 @@ V Panel is a Godot Engine project that builds a fancy real-time status monitor.
|
||||||
- Signals: past tense verb (e.g., `value_changed`, `panel_resized`).
|
- Signals: past tense verb (e.g., `value_changed`, `panel_resized`).
|
||||||
- Scene files: PascalCase matching their main script (e.g., `CpuPanel.tscn`).
|
- Scene files: PascalCase matching their main script (e.g., `CpuPanel.tscn`).
|
||||||
|
|
||||||
|
### Shaders
|
||||||
|
- `vial_fill.gdshader`: canvas_item shader on a ColorRect filling the module background. Uses rounded-rect SDF (`rr_sdf`), sum-of-sines water surface with edge damping, and layered effects (wave distortion, ripple rings, swirl, HSV shift, top-down lighting, sparkle, foam line).
|
||||||
|
- Noise texture: a static 100×100 PNG (`assets/textures/noise_100.png`) loaded at runtime and passed as `noise_tex` sampler2D uniform. Used for organic distortion modulation.
|
||||||
|
- Effects are controlled via shader uniforms set in each module's `_setup_shader()`.
|
||||||
|
|
||||||
|
### Labels / Readability
|
||||||
|
- All percentage labels get a 3px black outline (`outline_size` / `font_outline_color`) for legibility against the liquid shader background.
|
||||||
|
- Title labels get a 2px outline.
|
||||||
|
|
||||||
### Project Structure
|
### Project Structure
|
||||||
```
|
```
|
||||||
res://
|
res://
|
||||||
|
|
@ -31,7 +40,8 @@ res://
|
||||||
│ ├── cpu/
|
│ ├── cpu/
|
||||||
│ ├── memory/
|
│ ├── memory/
|
||||||
│ ├── network/
|
│ ├── network/
|
||||||
│ └── disk/
|
│ ├── disk/
|
||||||
|
│ └── testing/
|
||||||
├── scenes/ # Root scenes (dashboard, etc.)
|
├── scenes/ # Root scenes (dashboard, etc.)
|
||||||
├── scripts/ # Shared utility scripts
|
├── scripts/ # Shared utility scripts
|
||||||
├── themes/ # Theme definitions and style resources
|
├── themes/ # Theme definitions and style resources
|
||||||
|
|
|
||||||
20
README.md
20
README.md
|
|
@ -6,16 +6,24 @@ A fancy status monitor built with the Godot Engine. V stands for the Roman numer
|
||||||
|
|
||||||
V Panel is a visually rich, real-time status monitoring dashboard built entirely in Godot. Designed to be both functional and aesthetically polished, it serves as a showcase for Godot's UI capabilities outside of gaming — custom shaders, smooth animations, reactive layouts, and system integration.
|
V Panel is a visually rich, real-time status monitoring dashboard built entirely in Godot. Designed to be both functional and aesthetically polished, it serves as a showcase for Godot's UI capabilities outside of gaming — custom shaders, smooth animations, reactive layouts, and system integration.
|
||||||
|
|
||||||
## Goals
|
## Features
|
||||||
|
|
||||||
- **Beautiful UI** — Smooth animations, custom shaders, stylish typography, and a cohesive dark theme.
|
- **Responsive grid dashboard** — Modules auto-arrange in a dynamic grid that adapts to window size. Drag-and-drop to rearrange.
|
||||||
- **Real-time data** — Display live system metrics (CPU, memory, disk, network, processes).
|
- **Shader-based vial fill** — Each module uses a custom `vial_fill.gdshader` instead of progress bars. Features sum-of-sines water surface animation, edge-damped meniscus, wave distortion, ripple rings, swirl, HSV colour shifting, top-down lighting, sparkle effects, and a foam surface line.
|
||||||
- **Modular panels** — Each metric or subsystem gets its own panel widget. Easy to add, remove, or rearrange.
|
- **Live system monitoring** — Reads CPU usage from `/proc/stat` and memory usage from `/proc/meminfo` on Linux. Extensible module system for adding new collectors.
|
||||||
- **Godot showcase** — Demonstrate what Godot can do for non-game applications.
|
- **Smooth animations** — All fill level transitions use tweens with cubic easing.
|
||||||
|
|
||||||
|
## Modules
|
||||||
|
|
||||||
|
| Module | Data Source | Description |
|
||||||
|
|--------|-------------|-------------|
|
||||||
|
| CPU | `/proc/stat` | Real-time CPU usage percentage |
|
||||||
|
| Memory | `/proc/meminfo` | Real-time memory usage percentage |
|
||||||
|
| Testing | N/A (cycles 0–100%) | Shader visual testing with stepped fill levels |
|
||||||
|
|
||||||
## Project Status
|
## Project Status
|
||||||
|
|
||||||
Early development. The framework and initial modules are being designed now.
|
Active development. Core framework, drag-and-drop, CPU/memory collectors, and shader-based vial fill are implemented. More system modules (disk, network) are planned.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
|
||||||
BIN
assets/textures/noise_100.png
Normal file
BIN
assets/textures/noise_100.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
0
panels/.gitkeep
Normal file
0
panels/.gitkeep
Normal file
|
|
@ -46,6 +46,7 @@ func _setup_shader() -> void:
|
||||||
mat.shader = preload("res://shaders/vial_fill.gdshader")
|
mat.shader = preload("res://shaders/vial_fill.gdshader")
|
||||||
mat.set_shader_parameter("liquid_color", Color(0.2, 0.5, 0.8, 1.0))
|
mat.set_shader_parameter("liquid_color", Color(0.2, 0.5, 0.8, 1.0))
|
||||||
mat.set_shader_parameter("fill", 0.0)
|
mat.set_shader_parameter("fill", 0.0)
|
||||||
|
mat.set_shader_parameter("noise_tex", load("res://assets/textures/noise_100.png"))
|
||||||
vial_fill.material = mat
|
vial_fill.material = mat
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -61,5 +62,9 @@ func _style() -> void:
|
||||||
add_theme_stylebox_override("panel_focused", panel)
|
add_theme_stylebox_override("panel_focused", panel)
|
||||||
|
|
||||||
title_label.add_theme_color_override("font_color", Color(0.7, 0.7, 0.8, 1.0))
|
title_label.add_theme_color_override("font_color", Color(0.7, 0.7, 0.8, 1.0))
|
||||||
|
title_label.add_theme_constant_override("outline_size", 2)
|
||||||
|
title_label.add_theme_color_override("font_outline_color", Color(0.0, 0.0, 0.0, 0.5))
|
||||||
label.add_theme_color_override("font_color", Color(0.9, 0.9, 1.0, 1.0))
|
label.add_theme_color_override("font_color", Color(0.9, 0.9, 1.0, 1.0))
|
||||||
label.add_theme_font_size_override("font_size", 48)
|
label.add_theme_font_size_override("font_size", 48)
|
||||||
|
label.add_theme_constant_override("outline_size", 3)
|
||||||
|
label.add_theme_color_override("font_outline_color", Color(0.0, 0.0, 0.0, 0.7))
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ func _setup_shader() -> void:
|
||||||
mat.shader = preload("res://shaders/vial_fill.gdshader")
|
mat.shader = preload("res://shaders/vial_fill.gdshader")
|
||||||
mat.set_shader_parameter("liquid_color", Color(0.2, 0.7, 0.4, 1.0))
|
mat.set_shader_parameter("liquid_color", Color(0.2, 0.7, 0.4, 1.0))
|
||||||
mat.set_shader_parameter("fill", 0.0)
|
mat.set_shader_parameter("fill", 0.0)
|
||||||
|
mat.set_shader_parameter("noise_tex", load("res://assets/textures/noise_100.png"))
|
||||||
vial_fill.material = mat
|
vial_fill.material = mat
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -61,5 +62,9 @@ func _style() -> void:
|
||||||
add_theme_stylebox_override("panel_focused", panel)
|
add_theme_stylebox_override("panel_focused", panel)
|
||||||
|
|
||||||
title_label.add_theme_color_override("font_color", Color(0.7, 0.7, 0.8, 1.0))
|
title_label.add_theme_color_override("font_color", Color(0.7, 0.7, 0.8, 1.0))
|
||||||
|
title_label.add_theme_constant_override("outline_size", 2)
|
||||||
|
title_label.add_theme_color_override("font_outline_color", Color(0.0, 0.0, 0.0, 0.5))
|
||||||
label.add_theme_color_override("font_color", Color(0.9, 0.9, 1.0, 1.0))
|
label.add_theme_color_override("font_color", Color(0.9, 0.9, 1.0, 1.0))
|
||||||
label.add_theme_font_size_override("font_size", 48)
|
label.add_theme_font_size_override("font_size", 48)
|
||||||
|
label.add_theme_constant_override("outline_size", 3)
|
||||||
|
label.add_theme_color_override("font_outline_color", Color(0.0, 0.0, 0.0, 0.7))
|
||||||
|
|
|
||||||
65
panels/testing/testing_module.gd
Normal file
65
panels/testing/testing_module.gd
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
extends ModuleBase
|
||||||
|
class_name TestingModule
|
||||||
|
|
||||||
|
|
||||||
|
@onready var title_label: Label = %Title
|
||||||
|
@onready var label: Label = %Label
|
||||||
|
@onready var vial_fill: ColorRect = %VialFill
|
||||||
|
|
||||||
|
var _displayed_fill: float = 0.0
|
||||||
|
var _fill_tween: Tween
|
||||||
|
var _cycle: int = 0
|
||||||
|
var _levels: Array[float] = [0.0, 0.25, 0.50, 0.75, 1.0]
|
||||||
|
|
||||||
|
|
||||||
|
func initialize() -> void:
|
||||||
|
module_title = "Testing"
|
||||||
|
_setup_shader()
|
||||||
|
_style()
|
||||||
|
|
||||||
|
|
||||||
|
func refresh(data: Dictionary) -> void:
|
||||||
|
_cycle = (_cycle + 1) % _levels.size()
|
||||||
|
var target: float = _levels[_cycle]
|
||||||
|
|
||||||
|
label.text = "%d%%" % roundi(target * 100.0)
|
||||||
|
|
||||||
|
if _fill_tween and _fill_tween.is_valid():
|
||||||
|
_fill_tween.kill()
|
||||||
|
_fill_tween = create_tween()
|
||||||
|
_fill_tween.tween_method(_set_fill, _displayed_fill, target, 0.6).set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_CUBIC)
|
||||||
|
|
||||||
|
|
||||||
|
func _set_fill(value: float) -> void:
|
||||||
|
_displayed_fill = value
|
||||||
|
var mat := vial_fill.material as ShaderMaterial
|
||||||
|
if mat:
|
||||||
|
mat.set_shader_parameter("fill", value)
|
||||||
|
|
||||||
|
|
||||||
|
func _setup_shader() -> void:
|
||||||
|
var mat := ShaderMaterial.new()
|
||||||
|
mat.shader = preload("res://shaders/vial_fill.gdshader")
|
||||||
|
mat.set_shader_parameter("liquid_color", Color(0.6, 0.3, 0.8, 1.0))
|
||||||
|
mat.set_shader_parameter("fill", 0.0)
|
||||||
|
mat.set_shader_parameter("noise_tex", load("res://assets/textures/noise_100.png"))
|
||||||
|
vial_fill.material = mat
|
||||||
|
|
||||||
|
|
||||||
|
func _style() -> void:
|
||||||
|
var panel := StyleBoxFlat.new()
|
||||||
|
panel.bg_color = Color.TRANSPARENT
|
||||||
|
panel.corner_radius_top_left = 12
|
||||||
|
panel.corner_radius_top_right = 12
|
||||||
|
panel.corner_radius_bottom_right = 12
|
||||||
|
panel.corner_radius_bottom_left = 12
|
||||||
|
add_theme_stylebox_override("panel", panel)
|
||||||
|
add_theme_stylebox_override("panel_focused", panel)
|
||||||
|
|
||||||
|
title_label.add_theme_color_override("font_color", Color(0.7, 0.7, 0.8, 1.0))
|
||||||
|
title_label.add_theme_constant_override("outline_size", 2)
|
||||||
|
title_label.add_theme_color_override("font_outline_color", Color(0.0, 0.0, 0.0, 0.5))
|
||||||
|
label.add_theme_color_override("font_color", Color(0.9, 0.9, 1.0, 1.0))
|
||||||
|
label.add_theme_font_size_override("font_size", 48)
|
||||||
|
label.add_theme_constant_override("outline_size", 3)
|
||||||
|
label.add_theme_color_override("font_outline_color", Color(0.0, 0.0, 0.0, 0.7))
|
||||||
41
panels/testing/testing_module.tscn
Normal file
41
panels/testing/testing_module.tscn
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
[gd_scene format=3]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://panels/testing/testing_module.gd" id="1"]
|
||||||
|
[ext_resource type="Shader" path="res://shaders/vial_fill.gdshader" id="2"]
|
||||||
|
|
||||||
|
[node name="TestingModule" type="PanelContainer"]
|
||||||
|
anchors_preset = 0
|
||||||
|
script = ExtResource("1")
|
||||||
|
|
||||||
|
[node name="VialFill" type="ColorRect" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
mouse_filter = 2
|
||||||
|
|
||||||
|
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
theme_constant_overrides/margin_left = 12
|
||||||
|
theme_constant_overrides/margin_top = 12
|
||||||
|
theme_constant_overrides/margin_right = 12
|
||||||
|
theme_constant_overrides/margin_bottom = 12
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
|
||||||
|
[node name="Title" type="Label" parent="MarginContainer/VBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
text = "Testing"
|
||||||
|
|
||||||
|
[node name="Spacer" type="Control" parent="MarginContainer/VBoxContainer"]
|
||||||
|
size_flags_vertical = 3
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="MarginContainer/VBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
horizontal_alignment = 1
|
||||||
|
size_flags_horizontal = 4
|
||||||
|
|
@ -31,6 +31,10 @@ func _add_modules() -> void:
|
||||||
grid.place_module(mem, 1, 0)
|
grid.place_module(mem, 1, 0)
|
||||||
_modules.append(mem)
|
_modules.append(mem)
|
||||||
|
|
||||||
|
var test := preload("res://panels/testing/testing_module.tscn").instantiate()
|
||||||
|
grid.place_module(test, 2, 0)
|
||||||
|
_modules.append(test)
|
||||||
|
|
||||||
var timer := Timer.new()
|
var timer := Timer.new()
|
||||||
timer.timeout.connect(_refresh_modules)
|
timer.timeout.connect(_refresh_modules)
|
||||||
timer.autostart = true
|
timer.autostart = true
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,57 @@
|
||||||
shader_type canvas_item;
|
shader_type canvas_item;
|
||||||
|
|
||||||
|
// --- Fill control ---
|
||||||
uniform float fill : hint_range(0.0, 1.0) = 0.0;
|
uniform float fill : hint_range(0.0, 1.0) = 0.0;
|
||||||
|
|
||||||
uniform vec4 liquid_color : source_color = vec4(0.2, 0.5, 0.8, 1.0);
|
|
||||||
uniform vec4 bg_color : source_color = vec4(0.08, 0.08, 0.12, 1.0);
|
uniform vec4 bg_color : source_color = vec4(0.08, 0.08, 0.12, 1.0);
|
||||||
uniform vec4 border_color : source_color = vec4(0.2, 0.2, 0.28, 1.0);
|
uniform vec4 border_color : source_color = vec4(0.2, 0.2, 0.28, 1.0);
|
||||||
|
uniform float corner_radius : hint_range(0.0, 0.5) = 0.05;
|
||||||
uniform float corner_radius : hint_range(0.0, 0.5) = 0.06;
|
|
||||||
uniform float border_width : hint_range(0.0, 0.1) = 0.008;
|
uniform float border_width : hint_range(0.0, 0.1) = 0.008;
|
||||||
|
|
||||||
uniform float wave_amp : hint_range(0.0, 0.05) = 0.012;
|
uniform float wave_amp : hint_range(0.0, 0.05) = 0.012;
|
||||||
uniform float wave_freq : hint_range(0.0, 15.0) = 4.0;
|
uniform float wave_freq : hint_range(0.0, 15.0) = 4.0;
|
||||||
|
|
||||||
|
// --- Liquid base color (modulated by effects below) ---
|
||||||
|
uniform vec4 liquid_color : source_color = vec4(0.2, 0.5, 0.8, 1.0);
|
||||||
|
|
||||||
|
// --- Animation controls ---
|
||||||
|
uniform float time_factor : hint_range(0.0, 5.0) = 1.0;
|
||||||
|
uniform float wave_strength : hint_range(0.0, 0.5) = 0.05;
|
||||||
|
uniform float ripple_speed : hint_range(0.0, 5.0) = 1.0;
|
||||||
|
|
||||||
|
// --- Glow / edge ---
|
||||||
|
uniform vec4 edge_color : source_color = vec4(0.3, 0.6, 1.0, 1.0);
|
||||||
|
uniform float glow_intensity : hint_range(0.0, 5.0) = 1.5;
|
||||||
|
uniform float edge_pulse : hint_range(0.0, 2.0) = 1.0;
|
||||||
|
|
||||||
|
// --- Distortion ---
|
||||||
|
uniform sampler2D noise_tex : repeat_enable;
|
||||||
|
uniform float noise_scale : hint_range(0.0, 5.0) = 1.0;
|
||||||
|
uniform float swirl_strength : hint_range(-2.0, 2.0) = 0.5;
|
||||||
|
uniform float hue_shift_speed : hint_range(0.0, 5.0) = 0.0;
|
||||||
|
|
||||||
|
|
||||||
|
// ---------- helpers ----------
|
||||||
|
|
||||||
|
vec3 rgb2hsv(vec3 c) {
|
||||||
|
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
|
||||||
|
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
|
||||||
|
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
|
||||||
|
float d = q.x - min(q.w, q.y);
|
||||||
|
float e = 1e-10;
|
||||||
|
return vec3(abs((q.w - q.y) / (6.0 * d + e) + q.z), d / (q.x + e), q.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 hsv2rgb(vec3 c) {
|
||||||
|
vec3 p = abs(fract(c.xxx + vec3(0.0, 1.0 / 3.0, 2.0 / 3.0)) * 6.0 - 3.0);
|
||||||
|
return c.z * mix(vec3(1.0), clamp(p - 1.0, 0.0, 1.0), c.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 get_swirl_uv(vec2 uv, float strength) {
|
||||||
|
vec2 center = vec2(0.5);
|
||||||
|
vec2 diff = uv - center;
|
||||||
|
float dist = length(diff);
|
||||||
|
float angle = atan(diff.y, diff.x) + strength * (0.5 - dist) * sin(TIME * 0.5);
|
||||||
|
return center + vec2(cos(angle), sin(angle)) * dist;
|
||||||
|
}
|
||||||
|
|
||||||
float rr_sdf(vec2 p, vec2 half_size, float r) {
|
float rr_sdf(vec2 p, vec2 half_size, float r) {
|
||||||
vec2 d = abs(p) - half_size + r;
|
vec2 d = abs(p) - half_size + r;
|
||||||
|
|
@ -19,42 +59,86 @@ float rr_sdf(vec2 p, vec2 half_size, float r) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------- main ----------
|
||||||
|
|
||||||
void fragment() {
|
void fragment() {
|
||||||
vec2 half_size = vec2(0.5, 0.5);
|
vec2 half_size = vec2(0.5, 0.5);
|
||||||
vec2 uv_centered = UV - half_size;
|
vec2 uv_centered = UV - half_size;
|
||||||
|
float t = TIME * time_factor;
|
||||||
|
|
||||||
|
// --- rounded-rect mask ---
|
||||||
float max_r = min(corner_radius, half_size.x);
|
float max_r = min(corner_radius, half_size.x);
|
||||||
|
|
||||||
// Outer rounded rect
|
|
||||||
float outer_d = rr_sdf(uv_centered, half_size, max_r);
|
float outer_d = rr_sdf(uv_centered, half_size, max_r);
|
||||||
float outer = 1.0 - smoothstep(0.0, max(0.001, fwidth(outer_d)), max(outer_d, 0.0));
|
float outer = 1.0 - smoothstep(0.0, max(0.001, fwidth(outer_d)), max(outer_d, 0.0));
|
||||||
|
|
||||||
// Inner (inset by border_width)
|
|
||||||
float inner_r = max(0.0, max_r - border_width);
|
float inner_r = max(0.0, max_r - border_width);
|
||||||
float inner_hw = max(0.0, half_size.x - border_width);
|
float inner_hw = max(0.0, half_size.x - border_width);
|
||||||
float inner_hh = max(0.0, half_size.y - border_width);
|
float inner_hh = max(0.0, half_size.y - border_width);
|
||||||
float inner_d = rr_sdf(uv_centered, vec2(inner_hw, inner_hh), inner_r);
|
float inner_d = rr_sdf(uv_centered, vec2(inner_hw, inner_hh), inner_r);
|
||||||
float inner = 1.0 - smoothstep(0.0, max(0.001, fwidth(inner_d)), max(inner_d, 0.0));
|
float inner = 1.0 - smoothstep(0.0, max(0.001, fwidth(inner_d)), max(inner_d, 0.0));
|
||||||
|
|
||||||
// Liquid fill with a gentle animated wave at the surface.
|
// --- liquid fill line (sum-of-sines + edge damping) ---
|
||||||
// UV.y = 0 is top, UV.y = 1 is bottom. fill = 0 → empty, fill = 1 → full.
|
float wf = wave_freq * 6.283;
|
||||||
float wave = sin(UV.x * wave_freq * 6.283 + TIME * 1.5) * wave_amp;
|
float w1 = sin(UV.x * wf + TIME * 5.0);
|
||||||
float fill_line = 1.0 - fill + wave;
|
float w2 = sin(UV.x * wf * 1.7 + TIME * 7.0 + 1.3) * 0.5;
|
||||||
|
float w3 = sin(UV.x * wf * 0.6 + TIME * 2.5 + 2.9) * 0.3;
|
||||||
|
float wave_sum = (w1 + w2 + w3) / 1.8;
|
||||||
|
|
||||||
|
// damp wave near left/right walls for meniscus effect
|
||||||
|
float edge_damp = smoothstep(0.0, 0.08, UV.x) * smoothstep(0.0, 0.08, 1.0 - UV.x);
|
||||||
|
wave_sum *= edge_damp;
|
||||||
|
|
||||||
|
float fill_line = 1.0 - fill + wave_sum * wave_amp;
|
||||||
float liquid = smoothstep(fill_line - 0.001, fill_line + 0.001, UV.y);
|
float liquid = smoothstep(fill_line - 0.001, fill_line + 0.001, UV.y);
|
||||||
liquid *= inner;
|
liquid *= inner;
|
||||||
|
|
||||||
// Base background
|
// --- build effect colour for the liquid region ---
|
||||||
|
vec2 uv = UV;
|
||||||
|
float noise_val = texture(noise_tex, uv * noise_scale + t * 0.05).r;
|
||||||
|
|
||||||
|
// wave distortion
|
||||||
|
uv.y += sin(uv.x * 10.0 + t) * wave_strength * noise_val;
|
||||||
|
uv.x += cos(uv.y * 10.0 + t * 0.5) * wave_strength * noise_val;
|
||||||
|
|
||||||
|
// ripple rings
|
||||||
|
float dist_center = distance(uv, vec2(0.5));
|
||||||
|
uv += sin(dist_center * 20.0 - t * ripple_speed) * (wave_strength * 0.5 * noise_val);
|
||||||
|
|
||||||
|
// swirl
|
||||||
|
uv = get_swirl_uv(uv, swirl_strength);
|
||||||
|
|
||||||
|
// sample the ColorRect texture (water image if assigned, else 1x1 white)
|
||||||
|
vec4 tex = texture(TEXTURE, uv);
|
||||||
|
|
||||||
|
// hue-shift the base liquid colour, modulated by the texture
|
||||||
|
vec3 base_rgb = tex.rgb * liquid_color.rgb;
|
||||||
|
vec3 hsv = rgb2hsv(base_rgb);
|
||||||
|
hsv.x = fract(hsv.x + t * 0.1 * hue_shift_speed);
|
||||||
|
hsv.y = clamp(hsv.y + 0.2 * noise_val, 0.0, 1.0);
|
||||||
|
vec3 effect_rgb = hsv2rgb(hsv);
|
||||||
|
|
||||||
|
// top-down lighting — subtle brightening at the very top
|
||||||
|
float top_light = smoothstep(0.0, 0.3, 1.0 - UV.y);
|
||||||
|
effect_rgb *= 1.0 + top_light * 0.2;
|
||||||
|
|
||||||
|
// overall pulse
|
||||||
|
float pulse = 0.8 + 0.2 * sin(t);
|
||||||
|
effect_rgb *= (glow_intensity * pulse);
|
||||||
|
|
||||||
|
// tiny sparkle
|
||||||
|
effect_rgb += 0.03 * vec3(sin(t * 3.0), cos(t * 2.0), noise_val * 0.1);
|
||||||
|
|
||||||
|
// --- composite ---
|
||||||
vec3 col = bg_color.rgb;
|
vec3 col = bg_color.rgb;
|
||||||
|
col = mix(col, effect_rgb, liquid);
|
||||||
|
|
||||||
// Liquid with subtle depth gradient
|
// surface foam line (wider glow + bright core)
|
||||||
vec3 liq = mix(liquid_color.rgb, liquid_color.rgb * 1.35, 1.0 - UV.y);
|
float foam_glow = smoothstep(fill_line - 0.02, fill_line, UV.y) - smoothstep(fill_line, fill_line + 0.004, UV.y);
|
||||||
col = mix(col, liq, liquid);
|
col += foam_glow * vec3(0.5, 0.65, 0.9) * 0.6 * liquid;
|
||||||
|
float foam_core = smoothstep(fill_line - 0.006, fill_line, UV.y) - smoothstep(fill_line, fill_line + 0.001, UV.y);
|
||||||
|
col += foam_core * vec3(0.7, 0.85, 1.0) * liquid;
|
||||||
|
|
||||||
// Surface highlight line at the top of the liquid column
|
// border
|
||||||
float surface = smoothstep(fill_line - 0.008, fill_line, UV.y) - smoothstep(fill_line, fill_line + 0.002, UV.y);
|
|
||||||
col += surface * vec3(0.3, 0.4, 0.5) * liquid;
|
|
||||||
|
|
||||||
// Border
|
|
||||||
col = mix(col, border_color.rgb, outer - inner);
|
col = mix(col, border_color.rgb, outer - inner);
|
||||||
|
|
||||||
COLOR = vec4(col, outer);
|
COLOR = vec4(col, outer);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue