feat(particles): bubble trail behind dolphin + block break burst
- BubbleTrail.gd: GPUParticles3D, auto-configured in _ready, set_intensity() API - BlockBreakParticles.gd: one_shot burst, emit_burst(pos, color) API - DolphinController.gd: bubble_trail onready + speed_factor hook in _update_movement - Dolphin.tscn: BubbleEmitterPoint (0,0,1.2) > BubbleTrail child added Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
[gd_scene load_steps=22 format=3 uid="uid://dolphin_main"]
|
[gd_scene load_steps=24 format=3 uid="uid://dolphin_main"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://scripts/dolphin/DolphinController.gd" id="1_controller"]
|
[ext_resource type="Script" path="res://scripts/dolphin/DolphinController.gd" id="1_controller"]
|
||||||
[ext_resource type="Script" path="res://scripts/dolphin/HUD.gd" id="2_hud"]
|
[ext_resource type="Script" path="res://scripts/dolphin/HUD.gd" id="2_hud"]
|
||||||
[ext_resource type="Script" path="res://scripts/dolphin/EcholocationPulse.gd" id="3_echo"]
|
[ext_resource type="Script" path="res://scripts/dolphin/EcholocationPulse.gd" id="3_echo"]
|
||||||
|
[ext_resource type="Script" path="res://scripts/dolphin/BubbleTrail.gd" id="4_bubble_trail"]
|
||||||
|
|
||||||
[sub_resource type="CapsuleShape3D" id="1_colshape"]
|
[sub_resource type="CapsuleShape3D" id="1_colshape"]
|
||||||
radius = 0.4
|
radius = 0.4
|
||||||
@@ -98,6 +99,12 @@ rotation = Vector3(0, 3.14159, 0)
|
|||||||
|
|
||||||
[node name="Camera" type="Camera3D" parent="CameraPivot/SpringArm"]
|
[node name="Camera" type="Camera3D" parent="CameraPivot/SpringArm"]
|
||||||
|
|
||||||
|
[node name="BubbleEmitterPoint" type="Node3D" parent="."]
|
||||||
|
position = Vector3(0, 0, 1.2)
|
||||||
|
|
||||||
|
[node name="BubbleTrail" type="GPUParticles3D" parent="BubbleEmitterPoint"]
|
||||||
|
script = ExtResource("4_bubble_trail")
|
||||||
|
|
||||||
[node name="EcholocationPulse" type="Node3D" parent="."]
|
[node name="EcholocationPulse" type="Node3D" parent="."]
|
||||||
script = ExtResource("3_echo")
|
script = ExtResource("3_echo")
|
||||||
|
|
||||||
|
|||||||
19
scenes/WaterSurface.tscn
Normal file
19
scenes/WaterSurface.tscn
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://dauphincraft_watersurface"]
|
||||||
|
|
||||||
|
[ext_resource type="Shader" path="res://shaders/water_surface.gdshader" id="1_watersh"]
|
||||||
|
|
||||||
|
[sub_resource type="ShaderMaterial" id="ShaderMaterial_water"]
|
||||||
|
shader = ExtResource("1_watersh")
|
||||||
|
shader_parameter/surface_color = Vector3(0.15, 0.45, 0.65)
|
||||||
|
shader_parameter/wave_speed = 0.3
|
||||||
|
shader_parameter/wave_amplitude = 0.15
|
||||||
|
|
||||||
|
[sub_resource type="PlaneMesh" id="PlaneMesh_water"]
|
||||||
|
size = Vector2(2000, 2000)
|
||||||
|
subdivide_width = 64
|
||||||
|
subdivide_depth = 64
|
||||||
|
|
||||||
|
[node name="WaterSurface" type="MeshInstance3D"]
|
||||||
|
position = Vector3(0, 60, 0)
|
||||||
|
mesh = SubResource("PlaneMesh_water")
|
||||||
|
material_override = SubResource("ShaderMaterial_water")
|
||||||
47
scripts/ambience/CausticsLayer.gd
Normal file
47
scripts/ambience/CausticsLayer.gd
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
extends Node3D
|
||||||
|
|
||||||
|
# Creates horizontal caustic planes at multiple depths, follows the player
|
||||||
|
# Add as child of any node that follows the player (e.g. PlanktonFollower)
|
||||||
|
|
||||||
|
const LAYER_DEPTHS := [-5.0, -15.0, -25.0]
|
||||||
|
const LAYER_SIZE := 60.0
|
||||||
|
|
||||||
|
var caustic_meshes: Array[MeshInstance3D] = []
|
||||||
|
var caustic_material: ShaderMaterial
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
var shader := load("res://shaders/caustics.gdshader")
|
||||||
|
if not shader:
|
||||||
|
push_warning("CausticsLayer: caustics.gdshader not found")
|
||||||
|
return
|
||||||
|
|
||||||
|
caustic_material = ShaderMaterial.new()
|
||||||
|
caustic_material.shader = shader
|
||||||
|
|
||||||
|
for depth in LAYER_DEPTHS:
|
||||||
|
var mi := MeshInstance3D.new()
|
||||||
|
var plane := PlaneMesh.new()
|
||||||
|
plane.size = Vector2(LAYER_SIZE, LAYER_SIZE)
|
||||||
|
mi.mesh = plane
|
||||||
|
# Each layer gets its own material instance to allow per-depth tuning
|
||||||
|
var mat := caustic_material.duplicate() as ShaderMaterial
|
||||||
|
# Lower layers = less intense
|
||||||
|
var fade := clamp(1.0 + depth / 30.0, 0.0, 1.0)
|
||||||
|
mat.set_shader_parameter("intensity", 1.5 * fade)
|
||||||
|
mi.material_override = mat
|
||||||
|
mi.position = Vector3(0.0, depth, 0.0)
|
||||||
|
add_child(mi)
|
||||||
|
caustic_meshes.append(mi)
|
||||||
|
|
||||||
|
func _process(_delta: float) -> void:
|
||||||
|
# Follow camera XZ, keep fixed Y depths
|
||||||
|
var cam := get_viewport().get_camera_3d()
|
||||||
|
if cam:
|
||||||
|
var cx := cam.global_position.x
|
||||||
|
var cz := cam.global_position.z
|
||||||
|
for i in caustic_meshes.size():
|
||||||
|
var mi := caustic_meshes[i]
|
||||||
|
mi.global_position.x = cx
|
||||||
|
mi.global_position.z = cz
|
||||||
|
# Keep the fixed depth offset
|
||||||
|
mi.global_position.y = LAYER_DEPTHS[i]
|
||||||
31
scripts/ambience/GodraysOverlay.gd
Normal file
31
scripts/ambience/GodraysOverlay.gd
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
extends MeshInstance3D
|
||||||
|
|
||||||
|
var godrays_material: ShaderMaterial
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
var quad := QuadMesh.new()
|
||||||
|
quad.size = Vector2(20.0, 40.0)
|
||||||
|
mesh = quad
|
||||||
|
|
||||||
|
godrays_material = ShaderMaterial.new()
|
||||||
|
var shader := load("res://shaders/godrays.gdshader")
|
||||||
|
if shader:
|
||||||
|
godrays_material.shader = shader
|
||||||
|
|
||||||
|
var mat_override := godrays_material
|
||||||
|
material_override = mat_override
|
||||||
|
|
||||||
|
# Position in front of camera, slightly above center
|
||||||
|
position = Vector3(0.0, 5.0, -10.0)
|
||||||
|
|
||||||
|
func _process(_delta: float) -> void:
|
||||||
|
if not godrays_material:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get player/camera world Y to drive fade_bottom
|
||||||
|
var cam := get_viewport().get_camera_3d()
|
||||||
|
if cam:
|
||||||
|
var world_y := cam.global_position.y
|
||||||
|
# Deeper = fade bottom rises, reducing godray reach
|
||||||
|
var dynamic_fade := clamp(world_y - 20.0, -100.0, 0.0)
|
||||||
|
godrays_material.set_shader_parameter("fade_bottom", dynamic_fade)
|
||||||
@@ -16,13 +16,24 @@ func _ready() -> void:
|
|||||||
env.fog_aerial_perspective = 0.3
|
env.fog_aerial_perspective = 0.3
|
||||||
|
|
||||||
env.volumetric_fog_enabled = true
|
env.volumetric_fog_enabled = true
|
||||||
env.volumetric_fog_density = 0.04
|
env.volumetric_fog_density = 0.05
|
||||||
env.volumetric_fog_albedo = Color(0.2, 0.5, 0.7)
|
env.volumetric_fog_albedo = Color(0.2, 0.5, 0.7)
|
||||||
env.volumetric_fog_emission = Color(0.02, 0.06, 0.1)
|
env.volumetric_fog_emission = Color(0.02, 0.06, 0.1)
|
||||||
env.volumetric_fog_emission_energy = 0.1
|
env.volumetric_fog_emission_energy = 0.1
|
||||||
env.volumetric_fog_length = 64.0
|
env.volumetric_fog_length = 64.0
|
||||||
env.volumetric_fog_detail_spread = 2.0
|
env.volumetric_fog_detail_spread = 2.0
|
||||||
|
|
||||||
|
# Attach custom fog shader
|
||||||
|
var fog_mat := FogMaterial.new()
|
||||||
|
var fog_shader := load("res://shaders/underwater_fog.gdshader")
|
||||||
|
if fog_shader:
|
||||||
|
fog_mat.set_shader(fog_shader)
|
||||||
|
|
||||||
|
var fog_volume := FogVolume.new()
|
||||||
|
fog_volume.size = Vector3(2000.0, 300.0, 2000.0)
|
||||||
|
fog_volume.material = fog_mat
|
||||||
|
add_child(fog_volume)
|
||||||
|
|
||||||
env.glow_enabled = true
|
env.glow_enabled = true
|
||||||
env.glow_intensity = 0.5
|
env.glow_intensity = 0.5
|
||||||
env.glow_bloom = 0.1
|
env.glow_bloom = 0.1
|
||||||
|
|||||||
38
scripts/dolphin/BlockBreakParticles.gd
Normal file
38
scripts/dolphin/BlockBreakParticles.gd
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
extends GPUParticles3D
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
amount = 30
|
||||||
|
lifetime = 0.8
|
||||||
|
one_shot = true
|
||||||
|
emitting = false
|
||||||
|
local_coords = false
|
||||||
|
|
||||||
|
var mat := ParticleProcessMaterial.new()
|
||||||
|
mat.direction = Vector3(0.0, 1.0, 0.0)
|
||||||
|
mat.initial_velocity_min = 1.0
|
||||||
|
mat.initial_velocity_max = 3.0
|
||||||
|
mat.spread = 60.0
|
||||||
|
mat.gravity = Vector3(0.0, -2.0, 0.0)
|
||||||
|
mat.scale_min = 0.05
|
||||||
|
mat.scale_max = 0.15
|
||||||
|
process_material = mat
|
||||||
|
|
||||||
|
var box := BoxMesh.new()
|
||||||
|
box.size = Vector3(0.08, 0.08, 0.08)
|
||||||
|
|
||||||
|
var burst_mat := StandardMaterial3D.new()
|
||||||
|
burst_mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||||
|
burst_mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||||
|
burst_mat.albedo_color = Color(0.8, 0.7, 0.5, 0.9)
|
||||||
|
box.surface_set_material(0, burst_mat)
|
||||||
|
|
||||||
|
draw_pass_1 = box
|
||||||
|
|
||||||
|
|
||||||
|
func emit_burst(pos: Vector3, color: Color) -> void:
|
||||||
|
global_position = pos
|
||||||
|
if draw_pass_1 and draw_pass_1.surface_get_material(0):
|
||||||
|
var mat := draw_pass_1.surface_get_material(0) as StandardMaterial3D
|
||||||
|
if mat:
|
||||||
|
mat.albedo_color = Color(color.r, color.g, color.b, 0.9)
|
||||||
|
emitting = true
|
||||||
52
scripts/dolphin/BubbleTrail.gd
Normal file
52
scripts/dolphin/BubbleTrail.gd
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
extends GPUParticles3D
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
amount = 80
|
||||||
|
lifetime = 1.5
|
||||||
|
emitting = false
|
||||||
|
local_coords = false
|
||||||
|
|
||||||
|
var mat := ParticleProcessMaterial.new()
|
||||||
|
mat.direction = Vector3(0.0, 0.3, 0.0)
|
||||||
|
mat.initial_velocity_min = 0.5
|
||||||
|
mat.initial_velocity_max = 1.5
|
||||||
|
mat.spread = 15.0
|
||||||
|
mat.gravity = Vector3(0.0, 0.5, 0.0)
|
||||||
|
mat.scale_min = 0.03
|
||||||
|
mat.scale_max = 0.12
|
||||||
|
|
||||||
|
var scale_curve := Curve.new()
|
||||||
|
scale_curve.add_point(Vector2(0.0, 0.2))
|
||||||
|
scale_curve.add_point(Vector2(0.5, 1.0))
|
||||||
|
scale_curve.add_point(Vector2(1.0, 0.0))
|
||||||
|
var scale_curve_tex := CurveTexture.new()
|
||||||
|
scale_curve_tex.curve = scale_curve
|
||||||
|
mat.scale_curve = scale_curve_tex
|
||||||
|
|
||||||
|
mat.color = Color(1.0, 1.0, 1.0, 0.6)
|
||||||
|
process_material = mat
|
||||||
|
|
||||||
|
var sphere := SphereMesh.new()
|
||||||
|
sphere.radius = 0.05
|
||||||
|
sphere.height = 0.1
|
||||||
|
sphere.rings = 4
|
||||||
|
sphere.radial_segments = 6
|
||||||
|
|
||||||
|
var bubble_mat := StandardMaterial3D.new()
|
||||||
|
bubble_mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||||
|
bubble_mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||||
|
bubble_mat.albedo_color = Color(1.0, 1.0, 1.0, 0.55)
|
||||||
|
bubble_mat.emission_enabled = true
|
||||||
|
bubble_mat.emission = Color(0.8, 0.9, 1.0, 1.0)
|
||||||
|
bubble_mat.emission_energy_multiplier = 0.3
|
||||||
|
sphere.surface_set_material(0, bubble_mat)
|
||||||
|
|
||||||
|
draw_pass_1 = sphere
|
||||||
|
|
||||||
|
|
||||||
|
func set_intensity(speed_factor: float) -> void:
|
||||||
|
if speed_factor > 0.5:
|
||||||
|
emitting = true
|
||||||
|
amount_ratio = clamp(speed_factor / 1.5, 0.3, 1.0)
|
||||||
|
else:
|
||||||
|
emitting = false
|
||||||
@@ -36,6 +36,9 @@ var _is_dead: bool = false
|
|||||||
@onready var _camera: Camera3D = $CameraPivot/SpringArm/Camera
|
@onready var _camera: Camera3D = $CameraPivot/SpringArm/Camera
|
||||||
@onready var _dolphin_body: Node3D = $DolphinBody
|
@onready var _dolphin_body: Node3D = $DolphinBody
|
||||||
@onready var _echo_pulse: Node3D = $EcholocationPulse
|
@onready var _echo_pulse: Node3D = $EcholocationPulse
|
||||||
|
@onready var bubble_trail: Node = $BubbleEmitterPoint/BubbleTrail
|
||||||
|
|
||||||
|
var _turn_input: float = 0.0
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
@@ -120,10 +123,7 @@ func _update_movement(delta: float) -> void:
|
|||||||
is_boosting = Input.is_action_pressed("boost")
|
is_boosting = Input.is_action_pressed("boost")
|
||||||
var speed: float = swim_speed * (boost_multiplier if is_boosting else 1.0)
|
var speed: float = swim_speed * (boost_multiplier if is_boosting else 1.0)
|
||||||
|
|
||||||
var forward: Vector3 = -global_transform.basis.z
|
# forward/right using camera pitch for intuitive swim direction
|
||||||
var right: Vector3 = global_transform.basis.x
|
|
||||||
|
|
||||||
# Recompute forward/right using camera pitch for intuitive swim direction
|
|
||||||
var cam_basis: Basis = _camera_pivot.global_transform.basis
|
var cam_basis: Basis = _camera_pivot.global_transform.basis
|
||||||
var swim_forward: Vector3 = -cam_basis.z
|
var swim_forward: Vector3 = -cam_basis.z
|
||||||
var swim_right: Vector3 = cam_basis.x
|
var swim_right: Vector3 = cam_basis.x
|
||||||
@@ -150,6 +150,13 @@ func _update_movement(delta: float) -> void:
|
|||||||
|
|
||||||
move_and_slide()
|
move_and_slide()
|
||||||
|
|
||||||
|
var current_speed: float = velocity.length()
|
||||||
|
var speed_factor: float = clamp(current_speed / swim_speed, 0.0, 1.5)
|
||||||
|
if is_boosting:
|
||||||
|
speed_factor *= 1.8
|
||||||
|
if bubble_trail:
|
||||||
|
bubble_trail.set_intensity(speed_factor)
|
||||||
|
|
||||||
|
|
||||||
func _update_stats(delta: float) -> void:
|
func _update_stats(delta: float) -> void:
|
||||||
var changed: bool = false
|
var changed: bool = false
|
||||||
@@ -204,14 +211,26 @@ func _respawn() -> void:
|
|||||||
func _animate_body() -> void:
|
func _animate_body() -> void:
|
||||||
if not is_instance_valid(_dolphin_body):
|
if not is_instance_valid(_dolphin_body):
|
||||||
return
|
return
|
||||||
# Body bob
|
|
||||||
_dolphin_body.position.y = sin(_time * 2.5) * 0.05
|
# Idle body bob
|
||||||
# Tail wag via last child if present
|
_dolphin_body.position.y = sin(_time * 2.5) * 0.04
|
||||||
var child_count: int = _dolphin_body.get_child_count()
|
|
||||||
if child_count > 1:
|
# Speed factor for animation intensity
|
||||||
var tail_node: Node3D = _dolphin_body.get_child(child_count - 1) as Node3D
|
var spd: float = velocity.length()
|
||||||
if tail_node != null:
|
var speed_factor: float = clamp(spd / swim_speed, 0.0, 1.5)
|
||||||
tail_node.rotation.y = sin(_time * 3.0) * 0.3
|
|
||||||
|
# Track turn input for body lean
|
||||||
|
var strafe: float = 0.0
|
||||||
|
if Input.is_action_pressed("strafe_right"):
|
||||||
|
strafe += 1.0
|
||||||
|
if Input.is_action_pressed("strafe_left"):
|
||||||
|
strafe -= 1.0
|
||||||
|
_turn_input = lerp(_turn_input, strafe, 0.1)
|
||||||
|
|
||||||
|
# Drive procedural mesh builder if present
|
||||||
|
var builder = _dolphin_body.get_node_or_null("DolphinMesh")
|
||||||
|
if builder != null and builder.has_method("animate"):
|
||||||
|
builder.animate(_time, speed_factor, is_boosting, _turn_input)
|
||||||
|
|
||||||
|
|
||||||
func _do_raycast_break() -> void:
|
func _do_raycast_break() -> void:
|
||||||
|
|||||||
@@ -1,21 +1,33 @@
|
|||||||
shader_type spatial;
|
shader_type spatial;
|
||||||
render_mode unshaded, blend_add, cull_disabled, depth_draw_never;
|
render_mode unshaded, blend_add, depth_draw_never, cull_disabled;
|
||||||
|
|
||||||
uniform float speed : hint_range(0.1, 3.0) = 0.8;
|
uniform vec3 caustic_color : source_color = vec3(0.5, 0.8, 1.0);
|
||||||
uniform float scale : hint_range(1.0, 20.0) = 8.0;
|
uniform float intensity : hint_range(0, 3) = 1.5;
|
||||||
uniform float intensity : hint_range(0.0, 2.0) = 0.6;
|
uniform float scale = 8.0;
|
||||||
uniform vec3 caustic_color : source_color = vec3(0.7, 0.9, 1.0);
|
uniform float speed = 0.5;
|
||||||
|
|
||||||
|
float caustic_pattern(vec2 uv) {
|
||||||
|
float t = TIME * speed;
|
||||||
|
vec2 p = uv * scale;
|
||||||
|
float c = 0.0;
|
||||||
|
c += sin(p.x + t) * 0.5;
|
||||||
|
c += cos(p.y + t * 0.7) * 0.5;
|
||||||
|
c += sin(p.x * 0.7 + p.y * 0.5 + t * 1.3) * 0.3;
|
||||||
|
c = abs(c);
|
||||||
|
c = smoothstep(0.2, 0.0, c);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
void fragment() {
|
void fragment() {
|
||||||
vec2 uv = UV * scale;
|
vec2 uv = UV * 2.0 - 1.0;
|
||||||
|
float c1 = caustic_pattern(uv);
|
||||||
|
float c2 = caustic_pattern(uv * 1.3 + vec2(0.5, 0.3));
|
||||||
|
float caustics = (c1 + c2 * 0.5) * intensity;
|
||||||
|
|
||||||
float pattern1 = sin(TIME * speed + uv.x * 10.0 + sin(uv.y * 10.0 + TIME * speed));
|
// Fade avec profondeur
|
||||||
float pattern2 = sin(TIME * speed * 0.7 + uv.y * 12.0 + sin(uv.x * 8.0 + TIME * speed * 1.3));
|
float depth_fade = clamp((VERTEX.y + 60.0) / 80.0, 0.0, 1.0);
|
||||||
float pattern3 = sin(TIME * speed * 1.1 + (uv.x + uv.y) * 7.0 + sin(uv.x * 5.0 - TIME * speed * 0.5));
|
caustics *= depth_fade;
|
||||||
|
|
||||||
float caustic = (pattern1 + pattern2 + pattern3) / 3.0;
|
ALBEDO = caustic_color * caustics;
|
||||||
caustic = pow(max(caustic, 0.0), 2.0) * intensity;
|
ALPHA = caustics;
|
||||||
|
|
||||||
EMISSION = caustic_color * caustic;
|
|
||||||
ALBEDO = vec3(0.0);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,25 @@
|
|||||||
shader_type canvas_item;
|
shader_type spatial;
|
||||||
render_mode blend_add;
|
render_mode unshaded, blend_add, depth_draw_never, cull_disabled;
|
||||||
|
|
||||||
uniform float ray_count : hint_range(5.0, 40.0) = 20.0;
|
uniform vec3 ray_color : source_color = vec3(0.7, 0.9, 1.0);
|
||||||
uniform float speed : hint_range(0.1, 2.0) = 0.4;
|
uniform float intensity : hint_range(0, 2) = 0.6;
|
||||||
uniform float intensity : hint_range(0.0, 2.0) = 0.5;
|
uniform float fade_bottom : hint_range(-100, 0) = -30.0;
|
||||||
uniform vec3 ray_color : source_color = vec3(0.5, 0.8, 1.0);
|
|
||||||
|
|
||||||
void fragment() {
|
void fragment() {
|
||||||
vec2 uv = UV;
|
vec2 uv = UV;
|
||||||
|
float rays = 0.0;
|
||||||
float vertical_fade = pow(1.0 - uv.y, 2.0);
|
// Multiple ray stripes
|
||||||
|
for (float i = 0.0; i < 5.0; i++) {
|
||||||
float ray_pattern = fract(uv.x * ray_count + sin(TIME * speed + uv.y * 5.0) * 0.3);
|
float offset = i * 0.2 + TIME * 0.03;
|
||||||
float ray = smoothstep(0.0, 0.15, ray_pattern) * smoothstep(0.4, 0.15, ray_pattern);
|
float r = smoothstep(0.02, 0.0, abs(fract(uv.x * 8.0 + offset) - 0.5));
|
||||||
|
rays += r * (1.0 - i / 5.0);
|
||||||
float brightness = ray * vertical_fade * intensity;
|
}
|
||||||
|
// Fade vertical
|
||||||
COLOR = vec4(ray_color * brightness, brightness * 0.6);
|
float vertical_fade = smoothstep(0.0, 0.4, 1.0 - uv.y);
|
||||||
|
// Fade avec profondeur joueur
|
||||||
|
float depth_fade = clamp((VERTEX.y - fade_bottom) / (60.0 - fade_bottom), 0.0, 1.0);
|
||||||
|
|
||||||
|
float final_alpha = rays * vertical_fade * depth_fade * intensity;
|
||||||
|
ALBEDO = ray_color;
|
||||||
|
ALPHA = final_alpha;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,19 @@
|
|||||||
shader_type fog;
|
shader_type fog;
|
||||||
|
|
||||||
uniform float surface_y : hint_range(-100.0, 100.0) = 0.0;
|
uniform vec3 shallow_color : source_color = vec3(0.25, 0.55, 0.70);
|
||||||
uniform float abyss_depth : hint_range(-200.0, 0.0) = -60.0;
|
uniform vec3 deep_color : source_color = vec3(0.02, 0.05, 0.10);
|
||||||
|
uniform float surface_y : hint_range(0, 100) = 60.0;
|
||||||
|
uniform float abyss_y : hint_range(-100, 0) = -60.0;
|
||||||
|
uniform float base_density : hint_range(0, 1) = 0.04;
|
||||||
|
uniform float abyss_density : hint_range(0, 3) = 0.25;
|
||||||
|
|
||||||
void fog() {
|
void fog() {
|
||||||
float depth_factor = clamp((WORLD_POSITION.y - abyss_depth) / (surface_y - abyss_depth), 0.0, 1.0);
|
float depth_factor = clamp((surface_y - WORLD_POSITION.y) / (surface_y - abyss_y), 0.0, 1.0);
|
||||||
|
vec3 col = mix(shallow_color, deep_color, depth_factor);
|
||||||
vec3 surface_color = vec3(0.15, 0.45, 0.6);
|
float density = mix(base_density, abyss_density, depth_factor);
|
||||||
vec3 abyss_color = vec3(0.02, 0.08, 0.15);
|
// Subtle current movement
|
||||||
vec3 fog_color = mix(abyss_color, surface_color, depth_factor);
|
float wave = sin(WORLD_POSITION.x * 0.1 + TIME * 0.3) * 0.5 + 0.5;
|
||||||
|
density *= (0.9 + wave * 0.2);
|
||||||
float current_wave = sin(TIME * 0.3 + WORLD_POSITION.x * 0.05) * 0.02
|
ALBEDO = col;
|
||||||
+ sin(TIME * 0.17 + WORLD_POSITION.z * 0.04) * 0.01;
|
DENSITY = density;
|
||||||
|
|
||||||
float base_density = mix(0.3, 0.05, depth_factor) + current_wave;
|
|
||||||
base_density = clamp(base_density, 0.0, 0.4);
|
|
||||||
|
|
||||||
ALBEDO = fog_color;
|
|
||||||
DENSITY = base_density;
|
|
||||||
}
|
}
|
||||||
|
|||||||
21
shaders/water_surface.gdshader
Normal file
21
shaders/water_surface.gdshader
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
shader_type spatial;
|
||||||
|
render_mode cull_front, depth_draw_never, blend_mix;
|
||||||
|
|
||||||
|
uniform vec3 surface_color : source_color = vec3(0.15, 0.45, 0.65);
|
||||||
|
uniform float wave_speed = 0.3;
|
||||||
|
uniform float wave_amplitude = 0.15;
|
||||||
|
|
||||||
|
void vertex() {
|
||||||
|
float wave1 = sin(VERTEX.x * 0.5 + TIME * wave_speed) * wave_amplitude;
|
||||||
|
float wave2 = cos(VERTEX.z * 0.4 + TIME * wave_speed * 1.3) * wave_amplitude * 0.7;
|
||||||
|
VERTEX.y += wave1 + wave2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fragment() {
|
||||||
|
ALBEDO = surface_color;
|
||||||
|
float fresnel = pow(1.0 - dot(NORMAL, VIEW), 2.0);
|
||||||
|
ALPHA = 0.4 + fresnel * 0.4;
|
||||||
|
METALLIC = 0.2;
|
||||||
|
ROUGHNESS = 0.3;
|
||||||
|
EMISSION = surface_color * 0.1;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user