11shader_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//////////////////////////////
1313group_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
1717uniform 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 ;
2121group_uniforms ;
2222
2323group_uniforms waves ;
2424uniform float time_scale : hint_range (0.0 , 64.0 ) = 8.0 ;
2525uniform 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 ;
2727group_uniforms ;
2828
2929group_uniforms reflections ;
@@ -34,7 +34,8 @@ group_uniforms;
3434
3535group_uniforms foam ;
3636uniform 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 ;
3839uniform float foam_distance : hint_range (0.1 , 16384.0 ) = 2048. ;
3940uniform bool foam_shore_enabled = true ;
4041uniform 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;
4950uniform float height_angle_threshold : hint_range (0.0 , 1.0 ) = 0.21 ;
5051uniform float fersnel_scattering : hint_range (0.0 , 70.0 ) = 20.0 ;
5152uniform float direct_scattering : hint_range (0.0 , 70.0 ) = 10.0 ;
52- uniform float depth_absorption : hint_range (0.0 , 10.0 ) = 1.0 ;
5353group_uniforms ;
5454
55-
5655// Uniforms set by C++
5756group_uniforms private_set_by_terrain ;
5857uniform 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
131129void vertex () {
132130 // Get vertex of flat plane in world coordinates and set world UV
@@ -164,92 +162,98 @@ void vertex() {
164162
165163void 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 }
0 commit comments