Skip to content

Commit dc4a446

Browse files
committed
Improve ocean shader - foam intensity, real depth, refraction
1 parent 6fba61e commit dc4a446

File tree

4 files changed

+83
-76
lines changed

4 files changed

+83
-76
lines changed

project/addons/terrain_3d/extras/shaders/M_ocean.tres

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,23 @@ seamless = true
1919
render_priority = 0
2020
shader = ExtResource("1_t7ju5")
2121
shader_parameter/sea_level = 5.000000819565997
22-
shader_parameter/albedo_color = Color(0.1058, 0.1568, 0.2117, 1)
22+
shader_parameter/water_color = Color(0.1058, 0.1568, 0.2117, 1)
2323
shader_parameter/visible_depth = 256.0
24-
shader_parameter/depth_curve = 0.2
24+
shader_parameter/depth_curve = 0.1
2525
shader_parameter/shore_blend = 0.999999977648
26-
shader_parameter/refraction_strength = 0.02000000095
26+
shader_parameter/refraction_strength = 0.02
2727
shader_parameter/time_scale = 8.0
2828
shader_parameter/height_scale = 3.0
2929
shader_parameter/ocean_scale = 2.0
3030
shader_parameter/fresnel_scale = 0.5
3131
shader_parameter/fresnel_power = 1.0
3232
shader_parameter/specular = 1.0
3333
shader_parameter/foam_enabled = true
34-
shader_parameter/foam_height = 1.34000015865
35-
shader_parameter/foam_distance = 3071.994145916455
34+
shader_parameter/foam_height = 1.350000064125
35+
shader_parameter/foam_top_intensity = 2.0
36+
shader_parameter/foam_distance = 3072.00014591674
3637
shader_parameter/foam_shore_enabled = true
37-
shader_parameter/foam_shore_intensity = 1.96100008988762
38+
shader_parameter/foam_shore_intensity = 1.3000000584901201
3839
shader_parameter/foam_noise_texture = SubResource("NoiseTexture2D_i2r35")
3940
shader_parameter/foam_noise_scale = 1.5
4041
shader_parameter/light_scattering_enabled = true
@@ -43,7 +44,6 @@ shader_parameter/height_threshold = 0.1
4344
shader_parameter/height_angle_threshold = 0.21
4445
shader_parameter/fersnel_scattering = 20.0
4546
shader_parameter/direct_scattering = 10.0
46-
shader_parameter/depth_absorption = 1.0
4747
shader_parameter/_mesh_size = 32.0
4848
shader_parameter/_subdiv = 1.0
4949
shader_parameter/_vertex_spacing = 4.0

project/addons/terrain_3d/extras/shaders/ocean_shader.gdshader

Lines changed: 71 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
shader_type spatial;
2-
render_mode cull_back, depth_draw_always, diffuse_burley, specular_schlick_ggx, skip_vertex_transform;
2+
render_mode cull_back, depth_draw_always, diffuse_burley, specular_schlick_ggx, shadows_disabled, skip_vertex_transform;
33

44
//////////////////////////////
55
// Constants
@@ -11,19 +11,19 @@ const int ITERATIONS_FRAGMENT = 3;
1111
// Uniforms
1212
//////////////////////////////
1313
group_uniforms water;
14-
uniform float sea_level : hint_range(-50.0, 50.0, 0.1) = 1.0;
15-
uniform vec3 albedo_color : source_color = vec3(.1058, .1568, .2117); // This is a common deep blue
16-
//uniform vec3 albedo_color : source_color = vec3(0.102, 0.212, 0.235); // This is a more green ocean
14+
uniform float sea_level : hint_range(-50.0, 50.0, 0.001) = 1.0;
15+
uniform vec3 water_color : source_color = vec3(.1058, .1568, .2117); // This is a common deep blue
16+
//uniform vec3 water_color : source_color = vec3(0.102, 0.212, 0.235); // This is a more green ocean
1717
uniform float visible_depth : hint_range(0.001, 10000., .1) = 256.;
18-
uniform float depth_curve : hint_range(0., 1.) = .2;
19-
uniform float shore_blend : hint_range(0.0, 10.0, 0.01) = 1.;
20-
uniform float refraction_strength : hint_range(0.0, 0.1, 0.001) = 0.02;
18+
uniform float depth_curve : hint_range(0.001, 5.0) = .1;
19+
uniform float shore_blend : hint_range(0.0, 10.0, 0.001) = 1.;
20+
uniform float refraction_strength : hint_range(0.0, .2, 0.001) = 0.02;
2121
group_uniforms;
2222

2323
group_uniforms waves;
2424
uniform float time_scale : hint_range(0.0, 64.0) = 8.0;
2525
uniform float height_scale : hint_range(0.0, 16.0) = 3.0;
26-
uniform float ocean_scale : hint_range(0.1, 8.0) = 2.0;
26+
uniform float ocean_scale : hint_range(0.1, 16.0) = 2.0;
2727
group_uniforms;
2828

2929
group_uniforms reflections;
@@ -34,7 +34,8 @@ group_uniforms;
3434

3535
group_uniforms foam;
3636
uniform bool foam_enabled = true;
37-
uniform float foam_height : hint_range(-2.0, 5.0) = 2.5;
37+
uniform float foam_height : hint_range(0., 5.0) = 2.5;
38+
uniform float foam_top_intensity : hint_range(.1, 100.0) = 2.0;
3839
uniform float foam_distance : hint_range(0.1, 16384.0) = 2048.;
3940
uniform bool foam_shore_enabled = true;
4041
uniform float foam_shore_intensity : hint_range(0.1, 100.0) = 2.;
@@ -49,10 +50,8 @@ uniform float height_threshold : hint_range(0.0, 1.0) = 0.1;
4950
uniform float height_angle_threshold : hint_range(0.0, 1.0) = 0.21;
5051
uniform float fersnel_scattering : hint_range(0.0, 70.0) = 20.0;
5152
uniform float direct_scattering : hint_range(0.0, 70.0) = 10.0;
52-
uniform float depth_absorption : hint_range(0.0, 10.0) = 1.0;
5353
group_uniforms;
5454

55-
5655
// Uniforms set by C++
5756
group_uniforms private_set_by_terrain;
5857
uniform float _mesh_size = 32.;
@@ -121,12 +120,11 @@ float ocean_height(vec2 pos, int iterations) {
121120

122121
//////////////////////////////
123122
// Vertex
124-
//////////////////////////////
125-
126-
123+
//
127124
// vertex() positions the vertices of the clipmap mesh across the geomorphed lods. You'll need the
128125
// first two blocks for any other water shader, from the start to setting v_vertex.xz. After that
129126
// your own shader can set the UV and v_vertex.Y however you like.
127+
//////////////////////////////
130128

131129
void vertex() {
132130
// Get vertex of flat plane in world coordinates and set world UV
@@ -164,92 +162,98 @@ void vertex() {
164162

165163
void fragment() {
166164
// Normals
167-
float h, u, v;
165+
float wave_height, u, v;
168166
// Adjust normal domain as screen space distance increases to reduce high frequency values across pixels
169167
float dv = max(0.25, length(fwidth(VERTEX)));
170168
vec2 n_uv = UV + dv * -0.66;
171-
h = ocean_height(n_uv, ITERATIONS_FRAGMENT);
169+
wave_height = ocean_height(n_uv, ITERATIONS_FRAGMENT);
172170
u = ocean_height(n_uv + vec2(dv, 0.0), ITERATIONS_FRAGMENT);
173171
v = ocean_height(n_uv + vec2(0.0, dv), ITERATIONS_FRAGMENT);
174172
const float wave_distance = 512.;
175-
vec3 world_normal = normalize(vec3(h - u, max(.1, v_vertex_distance/wave_distance), h - v));
173+
vec3 world_normal = normalize(vec3(wave_height - u, max(.1, v_vertex_distance/wave_distance), wave_height - v));
176174
NORMAL = mat3(VIEW_MATRIX) * world_normal;
177175
TANGENT = normalize(cross(NORMAL, VIEW_MATRIX[2].xyz));
178176
BINORMAL = normalize(cross(NORMAL, TANGENT));
179177

180-
// Horizon mask - 1.0 when light is above horizon w/ fade at edge
178+
// Horizon mask: 1.0 when light is above horizon w/ fade at edge
181179
float daytime_mask = smoothstep(0., .1, _light_direction.y);
182180

183181
// Reflections
184-
float cos_theta = max(0.0, dot(NORMAL, VIEW)); // 1.0 when perpendicular (looking straight down), ~0.0 grazing
182+
float cos_theta = max(0.0, dot(NORMAL, VIEW)); // 1.0 when perpendicular (looking straight down), ~0.0 grazing
185183
float fresnel_base = pow(1.0 - cos_theta, fresnel_power); // high at grazing, low straight down
186184
float fresnel = clamp(fresnel_scale * fresnel_base, 0.0, 1.0);
187185
ROUGHNESS = mix(0.02, 0.8, fresnel) * daytime_mask;
188186
SPECULAR = specular * fresnel;
189-
SPECULAR = clamp ( SPECULAR - .5 * (1. - daytime_mask), 0.15, 1.); // Mask far distance when sun is below horizon
187+
SPECULAR = clamp (SPECULAR - .5 * (1. - daytime_mask), 0.15, 1.); // Mask far distance when sun is below horizon
190188

191-
// Shore mask - 1.0 near the shore line
192-
float proximity_depth_tex = textureLod(depth_texture, SCREEN_UV, 0.0).r;
193-
vec4 proximity_view_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, proximity_depth_tex, 1.0);
194-
proximity_view_pos.xyz /= proximity_view_pos.w;
195-
float shore_mask = 1.0 - clamp(1.0 - smoothstep(proximity_view_pos.z + 2. * shore_blend, proximity_view_pos.z, VERTEX.z), 0.0, 1.0);
196-
ALPHA = 1.0 - shore_mask;
189+
// Terrain pixel position
190+
float depth_raw = textureLod(depth_texture, SCREEN_UV, 0.0).r;
191+
vec4 clip_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, depth_raw, 1.0);
192+
vec3 view_pos = clip_pos.xyz / clip_pos.w;
193+
vec4 world_pos_hom = INV_VIEW_MATRIX * vec4(view_pos, 1.0);
194+
vec3 terrain_position = world_pos_hom.xyz / world_pos_hom.w;
197195

198-
// Depth fade - based on vertex height rather than camera distance
199-
float depth_fade = 1. - depth_curve * (1. + smoothstep(0., 1., abs(sea_level - v_vertex.y) / visible_depth));
196+
// Shore mask: 1.0 near the shore line
197+
float shore_mask = 1.0 - clamp(1.0 - smoothstep(view_pos.z + 2. * shore_blend, view_pos.z, VERTEX.z), 0.0, 1.0);
198+
ALPHA = 1.0 - shore_mask;
200199

201-
// Albedo - start accumulation w/ sunlight color
200+
// Albedo accumulation: starting with water and sunlight colors
202201
vec3 light_mult = light_scattering_enabled ? _light_color : vec3(1.0);
203-
vec3 final_color = albedo_color * light_mult;
204-
205-
// Add foam
202+
ALBEDO = water_color * light_mult;
203+
204+
// Refraction offset
205+
vec2 refract_offset = NORMAL.xz * refraction_strength;
206+
float vp_y_mask = 1.0 - smoothstep(0.0, length(refract_offset) * 2.0, 1.0 - SCREEN_UV.y);
207+
refract_offset = mix(refract_offset, vec2(0.0), max(shore_mask, vp_y_mask));
208+
vec2 refract_uv = SCREEN_UV + refract_offset;
209+
refract_uv = clamp(refract_uv, vec2(0.001, 0.001), vec2(0.999, 0.999));
210+
211+
// Sample refracted terrain depth & pixel position
212+
float refract_depth_raw = textureLod(depth_texture, refract_uv, 0.0).r;
213+
vec4 refract_clip_pos = INV_PROJECTION_MATRIX * vec4(refract_uv * 2.0 - 1.0, refract_depth_raw, 1.0);
214+
vec3 refract_view_pos = refract_clip_pos.xyz / refract_clip_pos.w;
215+
vec4 refract_world_hom = INV_VIEW_MATRIX * vec4(refract_view_pos, 1.0);
216+
vec3 refract_world_pos = refract_world_hom.xyz / refract_world_hom.w;
217+
218+
// Filter out pixels above the water surface (ie player's head), or beyond the visible depth
219+
bool pixel_above_water = refract_world_pos.y >= v_vertex.y;
220+
bool in_refraction_depth = VERTEX.z < refract_view_pos.z + 2. * visible_depth;
221+
bool use_refracted = !pixel_above_water && in_refraction_depth;
222+
vec2 final_refract_uv = use_refracted ? refract_uv : SCREEN_UV;
223+
224+
// Sample undersea meshes
225+
vec3 background_color = texture(screen_texture, final_refract_uv).rgb;
226+
227+
// Depth fade based on vertical depth, with curve
228+
// The minimum amount helps to avoid ghosting around the player under the water
229+
float water_depth = max(20., sea_level + v_vertex.y - terrain_position.y);
230+
float normalized_depth = water_depth / visible_depth;
231+
float depth_fade = pow(clamp(normalized_depth, 0.0, 1.0), depth_curve);
232+
ALBEDO = mix(background_color, ALBEDO, depth_fade);
233+
234+
// Foam
206235
float foam_mask = 0.0;
207236
if (foam_enabled) {
208-
float detail_wave = h * 0.005 ;
237+
float detail_wave = wave_height * 0.005 ;
209238
float foam_noise = texture(foam_noise_texture, UV * .1 * foam_noise_scale).r;
210-
foam_mask = clamp(h * 0.5 + (detail_wave * 2.0 + shore_mask * foam_shore_intensity * float(foam_shore_enabled)) * foam_noise * 1.5 - foam_height, 0.0, 1.0);
239+
foam_mask = clamp(wave_height * 0.5 + (detail_wave * foam_top_intensity + float(foam_shore_enabled) * shore_mask * foam_shore_intensity) * foam_noise * 1.5 - foam_height, 0.0, 1.0);
211240
float dist_fade = 1. - smoothstep(0., 1., v_vertex_distance / foam_distance);
212241
foam_mask *= dist_fade;
213-
final_color = mix(final_color, vec3(0.8), foam_mask);
242+
ALBEDO = mix(ALBEDO, vec3(0.8), foam_mask);
214243
}
215244

216-
// Refraction: Distort screen UVs using the normal (tangent-space projection)
217-
{
218-
vec2 refract_offset = NORMAL.xz * refraction_strength;
219-
// limit refraction at the bottom edge of the screen as normals push away in view direction.
220-
float y_limit = 1.0 - smoothstep(.0, length(refract_offset) * 4.0, 1.-SCREEN_UV.y);
221-
refract_offset = mix(refract_offset, vec2(0.), max(shore_mask, y_limit));
222-
vec2 refract_uv = SCREEN_UV + refract_offset;
223-
refract_uv = clamp(refract_uv, vec2(0.001, 0.001), vec2(0.999, 0.999)); // slight inset to avoid exact edge artifacts
224-
225-
// Invert refract if target screenspace pixel is above ocean surface, replaces major artifact with minor one.
226-
proximity_depth_tex = textureLod(depth_texture, refract_uv, 0.0).r;
227-
proximity_view_pos = INV_PROJECTION_MATRIX * vec4(refract_uv * 2.0 - 1.0, proximity_depth_tex, 1.0);
228-
proximity_view_pos.xyz /= proximity_view_pos.w;
229-
refract_uv = SCREEN_UV + mix(refract_offset * (float(proximity_view_pos.z < VERTEX.z) * 2.0 - 1.0), vec2(0.), max(shore_mask, y_limit));
230-
refract_uv = clamp(refract_uv, vec2(0.001, 0.001), vec2(0.999, 0.999)); // slight inset to avoid exact edge artifacts
231-
232-
// Increase alpha with depth as more water means more light absorbed
233-
depth_fade = clamp(depth_fade + min(abs(VERTEX.z - proximity_view_pos.z) * 0.001 * depth_absorption, 1.0), 0.0, 1.0);
234-
235-
vec3 background = texture(screen_texture, refract_uv).rgb;
236-
final_color = mix(background, final_color, depth_fade * depth_fade);
237-
}
238-
239-
ALBEDO = final_color;
240-
245+
// Light scattering
241246
if (light_scattering_enabled) {
242-
// Approximate light scattering
243247
vec3 ray_dir = normalize(mat3(INV_VIEW_MATRIX) * VIEW);
244-
// light scattering based on height and sun angle
245-
vec3 light_factor = height_scattering * max(h, height_threshold)
248+
// Light scattering based on height and sun angle
249+
vec3 light_factor = height_scattering * max(wave_height, height_threshold)
246250
* max(pow(dot(_light_direction, -ray_dir), 4.0), height_angle_threshold)
247251
* _light_color
248252
* pow(0.5 - 0.5 * dot(_light_direction, world_normal), 3.0);
249-
// light scattering based on view angle
250-
light_factor += fersnel_scattering * pow(dot(ray_dir, world_normal), 2.0) * _light_color * albedo_color;
251-
// direct light scattering
252-
light_factor += direct_scattering * dot(_light_direction, world_normal) * albedo_color * _light_color;
253+
// Light scattering based on view angle
254+
light_factor += fersnel_scattering * pow(dot(ray_dir, world_normal), 2.0) * _light_color * water_color;
255+
// Direct light scattering
256+
light_factor += direct_scattering * dot(_light_direction, world_normal) * water_color * _light_color;
253257
// Apply horizon fade to the whole scattering
254258
BACKLIGHT = light_factor * 4.0 * clamp(depth_fade, 0.4, 1.0) * max(1.0 - foam_mask, 0.5) * daytime_mask;
255259
}

project/demo/components/Player.tscn

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
[gd_scene load_steps=7 format=3 uid="uid://domhm87hbhbg1"]
1+
[gd_scene load_steps=8 format=3 uid="uid://domhm87hbhbg1"]
22

33
[ext_resource type="Script" uid="uid://dajlr3n5wjwmb" path="res://demo/src/Player.gd" id="1_nm1yx"]
44
[ext_resource type="Script" uid="uid://b62ppvc03a6b1" path="res://demo/src/CameraManager.gd" id="2_loos7"]
@@ -12,6 +12,8 @@ height = 1.5
1212

1313
[sub_resource type="CapsuleMesh" id="CapsuleMesh_lsqiy"]
1414

15+
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_mox7b"]
16+
1517
[node name="Player" type="CharacterBody3D"]
1618
collision_layer = 2
1719
script = ExtResource("1_nm1yx")
@@ -42,3 +44,4 @@ shape = SubResource("SeparationRayShape3D_twc2s")
4244
[node name="Body" type="MeshInstance3D" parent="."]
4345
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
4446
mesh = SubResource("CapsuleMesh_lsqiy")
47+
surface_material_override/0 = SubResource("StandardMaterial3D_mox7b")

project/demo/src/Player.gd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func _physics_process(p_delta) -> void:
3838
move_and_slide()
3939
# Allow player to walk on waves in the ocean
4040
if get_parent().terrain.ocean_enabled:
41-
position.y = max(4.5, position.y)
41+
position.y = max(3, position.y)
4242

4343

4444
# Returns the input vector relative to the camera. Forward is always the direction the camera is facing

0 commit comments

Comments
 (0)