Skip to content

Conversation

@aidandavey
Copy link
Contributor

@aidandavey aidandavey commented Dec 4, 2025

Adds a secondary clipmap dedicated to the ocean. The ocean now uses an independent mesher/clipmap so ocean LOD, mesh size and tessellation can be configured and updated separately from terrain.

--
Partially addresses #298

@TokisanGames
Copy link
Owner

The other PR could have taken this new branch had you just changed the target and wanted to keep the old.

Anyway, I spent some time playing with it and the shader today. I think the functionality and organization are good. I'm not convinced we need tessellation for the ocean, but I suppose it's free to offer the option. We may wish to add vertex spacing.

This shader seems a bit expensive. One view showed cutting the FPS in half. 300fps to 150fps. I have a lot of tweaks for the shader to improve the look a lot, but we need to look at the performance first. Looking up a noise texture might be a lot cheaper than calculating it.

We should test some other popular ocean shaders and make sure they work just by dropping them in, though we may need to add the code snippet for geomorphing the lods that Xtarsia gave. That should be added here.
https://discord.com/channels/691957978680786944/1065519581013229578/1445856250523947070

@TokisanGames
Copy link
Owner

TokisanGames commented Dec 5, 2025

I added a fix that I'll cherry-pick out. It will go away after a rebase. I also added some updates to the shader and demo that we'll continue to refine or replace.

@aidandavey
Copy link
Contributor Author

OK thanks, I have some more updates based on the review I'm about to push

@TokisanGames
Copy link
Owner

Oh, also we should pass the heightmap into the ocean shader, so that vertex() can discard itself if it is lower than the terrain height minus a margin.

@aidandavey
Copy link
Contributor Author

Oh, also we should pass the heightmap into the ocean shader, so that vertex() can discard itself if it is lower than the terrain height minus a margin.

Yes, i cant help but think this could also be used for shoreline effects, breaking waves rolling towards the land mass. Etc...

@TokisanGames TokisanGames marked this pull request as draft December 6, 2025 02:06
@TokisanGames TokisanGames added enhancement New feature or request important High priority labels Dec 6, 2025
@TokisanGames TokisanGames moved this from 1.1 to In Progress in Terrain3D Roadmap Dec 6, 2025
@TokisanGames TokisanGames added this to the 1.1 milestone Dec 6, 2025
@Saul2022
Copy link

Saul2022 commented Dec 6, 2025

The shadee works well in the mobile renderer, with some optimizations ( size and lod) it doesnt cut that much fps ,though there might be some room of improvement there.

I would also add some way to tint the color of water and shoreline might be good , but cost a lot of fps from the shadwr i tested.

@Xtarsia
Copy link
Collaborator

Xtarsia commented Dec 7, 2025

I pushed some changes to the shader to get it working (as long as the mesh_size / vertex_spacing etc, are correct)

@aidandavey aidandavey force-pushed the Ocean4 branch 4 times, most recently from a4d53a0 to d96b25b Compare December 8, 2025 23:01
@aidandavey aidandavey marked this pull request as ready for review December 8, 2025 23:03
Copy link
Owner

@TokisanGames TokisanGames left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting _mesh_size in the ocean shader is a bit broken. First the shader should default to 48 which is the C++ default. But that isn't the cause of this issue.

The ocean handles LODs seamlessly in the editor:

Image

However in game, LODs break apart

Image

And this is weird, in game:

Image

It happens with both meshes, it's just if tessellation is enabled it's really small.

Image

In editor it looks fine:

Image

If in game, you continuously print the _mesh_size shader parameter it shows 48.0. Change the shader parameter to 47.9, the LODs seams and the vertices around 0 are fixed. Reset it back to 48 and it remains fixed.


Every shader we make is everyone we have to maintain. I'm looking at combining the two ocean shaders into one with #defines separating the code for the terrain occlusion. I started on it, but ran into a problem. I should wrap it up tomorrow.

@Xtarsia
Copy link
Collaborator

Xtarsia commented Dec 19, 2025

@TokisanGames @aidandavey , I have some thoughts:
I feel like this would benefit from its own seperate "Terrain3DOceanMaterial" class? currently the ocean mesh feels a bit invasive/convoluted reading through some of the changes to get it working.

Or even, a potentially hot take: this would be better suited as a seperate plugin, giving us Ocean3D, Terrain3D, & Sky3D.

Outside of the clipmap, there isnt much linking terrain3D to the ocean mesh. The culling based on terrain height is slower from some quick testing too, simply due to extra bandwidth incured by reading the heightmaps for each ocean vertex.

Also it seems in this PRs current state, displacement is non-functional, though I didnt look into which change was breaking yet.

Maybe some further discusion needed. This is a pretty big feature in its own right and I feel it shouldnt be rushed without abit more planning & thought.

@TokisanGames
Copy link
Owner

It's not just the ocean mesh that has seam issues in game. I'm sure it's just this refactor missed a step that needs to push some shader parameters at startup. Changing them in the remote debugger fixes the issue.

{53D3D6C4-130C-44AC-A162-664FDA8FFA44}

When switching scenes the ocean mesh appears above the camera in the sky for a second before snapping in place. I think that's a clue.

I spent some time reworking the shader. It now reflects light more accurately, fades the water's edge, smooths out normals over distance, and adds refraction.

{CC9B1971-E786-4FA2-A3FB-19EB5327047A} {1243EC6B-6EEF-4B4E-A729-3BC934487513} {E6ECE30C-D5EA-49C7-B2F5-169E51D4D19D}

@mikest
Copy link

mikest commented Dec 23, 2025

Anyway, I spent some time playing with it and the shader today. I think the functionality and organization are good. I'm not convinced we need tessellation for the ocean, but I suppose it's free to offer the option. We may wish to add vertex spacing.

This is something I would definitely make use of. I have an ocean shader which uses decaling to produce water effects like waves and prop wash from boats and it has a relatively high density mesh around the camera location to accomplish that.

Seen here, compared to the density of the Terrain3D mesh, I'm using 1cm resolution vertex spacing (radial, which uh... isn't great) in comparison to 1m with the terrain mesh.

image

@mikest
Copy link

mikest commented Dec 24, 2025

Did some testing and this looks pretty good! I get that the water shader is a demo, and that quite a bit will want to be done on it in the fullness of time, but I did see one problem that might want to be addressed.

There's some height tearing at the mesh LOD edges that is quite noticeable for certain parameter ranges:
image

I got this with Ocean Shader "Height Scale" > 10 and "UV scale" < 30, and ocean mesh at default settings.

@Xtarsia
Copy link
Collaborator

Xtarsia commented Dec 24, 2025

Adding depth_draw_always to the render mode flags should fix that.

@mikest
Copy link

mikest commented Dec 24, 2025

Adding depth_draw_always to the render mode flags should fix that.

I added the flag to the shader and it appears to change the problem into a different problem :)

I'm not enough of a glsl wizard to be able to know what the root of this problem is, but it does seem like the blending tessellations between the LODs having something off about their normals?

Regardless, this artifact is preferrable to the previous artifact.

image

@Xtarsia
Copy link
Collaborator

Xtarsia commented Dec 27, 2025

I added the flag to the shader and it appears to change the problem into a different problem :)

cull_disabled -> cull_back should fix that, though for an ocean, this may be undesirable if the camera is ever to be under water.

@TokisanGames TokisanGames self-assigned this Dec 28, 2025
@DeepCanionStudio
Copy link

DeepCanionStudio commented Dec 29, 2025

i copied the current water shader and modified it to add varius features from my own water shader
image

the shader adds a light scattering approximation
and depth based alpha on top of the fresnel alpha that was already in the shader

the only thing that could be a problem currently is that you need to pass the light direction to the shader

here's the shader code:

shader_type spatial;
render_mode cull_back, depth_draw_always, diffuse_burley, specular_schlick_ggx, skip_vertex_transform;

// Shader adapted from https://docs.godotengine.org/en/4.4/tutorials/shaders/your_first_shader/your_second_3d_shader.html

//////////////////////////////
// Constants
//////////////////////////////
const int ITERATIONS_VERTEX = 3;
const int ITERATIONS_FRAGMENT = 4;

//////////////////////////////
// Uniforms
//////////////////////////////
group_uniforms water;
uniform float time_scale : hint_range(0.0, 16.0) = 3.0;
uniform float height_scale : hint_range(0.0, 16.0) = 3.0;
uniform float ocean_scale : hint_range(0.1, 64.0, 0.1) = 4.0;
uniform float sea_level : hint_range(-50.0, 50.0, 0.1) = 12.0;
uniform vec3 albedo_color : source_color = vec3(.102, .212, .318);
uniform float visible_depth : hint_range(0., 10000., .1) = 256.;
uniform float depth_curve : hint_range(0., 1.) = .2;
uniform float edge_fade_distance : hint_range(0.0, 10.0, 0.01) = 1.;
uniform float refraction_strength : hint_range(0.0, 0.1, 0.001) = 0.07;

group_uniforms light_scattering;

uniform vec3 light_direction;
uniform vec3 light_color : source_color = vec3(1.0, 1.0, .735);
uniform float height_scattering : hint_range(0.0, 15.0) = 4.0;
uniform float height_threshold : hint_range(0.0, 1.0) = 0.1;
uniform float height_angle_threshold : hint_range(0.0, 1.0) = 0.21;

uniform float fersnel_scattering : hint_range(0.0, 70.0) = 15.0;
uniform float direct_scattering : hint_range(0.0, 70.0) = 10.0;

uniform float depth_absorbtion : hint_range(0.0, 10.0) = 1.0;

group_uniforms;
group_uniforms reflections;
uniform float fresnel_scale : hint_range(0., 1.) = .065;
uniform float fresnel_power = 1.;
uniform float specular : hint_range(0., 1.) = .5;
group_uniforms;

// Uniforms set by C++
group_uniforms private_dont_touch;
uniform float _mesh_size = 32.;
uniform float _subdiv = 1.;
uniform float _vertex_spacing = 1.;
uniform float _vertex_density = 1.;
uniform vec3 _target_pos = vec3(0.,0.,0.);
group_uniforms;

uniform sampler2D depth_texture : hint_depth_texture, repeat_disable, filter_nearest;
uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;

varying float vertex_distance;

//////////////////////////////
// Ocean Noise
//////////////////////////////

float hash(vec2 p) {
    return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
}

float noise(vec2 p) {
    vec2 i = floor(p);
    vec2 f = fract(p);
    vec2 u = smoothstep(0., 1., f);
    vec2 i1 = i + 1.0;

    float a = hash(i);
    float b = hash(vec2(i1.x, i.y));
    float c = hash(vec2(i.x, i1.y));
    float d = hash(i1);

    return 2.0 * mix(mix(a, b, u.x), mix(c, d, u.x), u.y) - 1.0;
}

float octave(vec2 uv) {
	uv += noise(uv);
	vec2 wave = 1.0 - abs(sin(uv));
	// Shapeing
	float v = wave.x * wave.y + 1e-6;
	float s = v * inversesqrt(v);
	return 1.0 - 2.0 * s + v;
}

float ocean_height(vec2 pos, int iterations) {
	float freq = 0.03 * ocean_scale;
	float amp = 4.0;
	vec2 uv = pos;
	uv.x *= 0.7;
	float h = 0.0;
	float t = TIME * time_scale;

	for (int i = 0; i < iterations; i++) {
	    h += (octave((uv + t) * freq) + octave((uv - t) * freq)) * amp;
	    uv *= mat2(vec2(1.8, 0.8), vec2(-0.8, 1.8));
	    freq *= 1.9;
	    amp *= 0.2;
	}
    return h * 0.1 * height_scale;
}

//////////////////////////////
// Vertex
//////////////////////////////

void vertex() {
    // Get vertex of flat plane in world coordinates and set world UV
    vec3 vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
    float scale = MODEL_MATRIX[0][0];
    float inv_scale = 1.0 / scale;
    float vertex_xz_distance = max(abs(vertex.x - _target_pos.x), abs(vertex.z - _target_pos.z));
    float vertex_lerp = smoothstep(0.0, 1.0, (vertex_xz_distance * inv_scale - _mesh_size - 4.0) / (_mesh_size - 4.0));
    vec2 v_fract = fract(VERTEX.xz * 0.5) * 2.0;
    // For LOD0 morph from a regular grid to an alternating grid to align with LOD1+
    vec2 shift = (scale < _vertex_spacing / _subdiv + 1e-6) ? // LOD0 or not
        // Shift from regular to symmetric
        mix(v_fract, vec2(v_fract.x, -v_fract.y),
            round(fract(round(mod(vertex.z * inv_scale, 4.0)) *
            round(mod(vertex.x * inv_scale, 4.0)) * 0.25))) :
        // symmetric shift
        v_fract * round((fract(vertex.xz * 0.25 * inv_scale) - 0.5) * 4.0);
    vertex.xz -= shift * scale * vertex_lerp;

    UV = (vertex.xz);

    vertex.y = sea_level + ocean_height(UV, ITERATIONS_VERTEX);

    VERTEX = (VIEW_MATRIX * vec4(vertex, 1.0)).xyz;
	vertex_distance = length(vertex - _target_pos);
}

//////////////////////////////
// Fragment
//////////////////////////////

void fragment() {
	float h, u, v;
	// adjust normal domain as screen space distance increases to reduce high frequency values across pixels
	float dv = max(0.25, length(fwidth(VERTEX)));
	h = ocean_height(UV, ITERATIONS_FRAGMENT);
	u = ocean_height(UV + vec2(dv, 0.0), ITERATIONS_FRAGMENT);
	v = ocean_height(UV + vec2(0.0, dv), ITERATIONS_FRAGMENT);
	vec3 world_normal = normalize(vec3(h - u, dv * _vertex_density, h - v));
	NORMAL = mat3(VIEW_MATRIX) * world_normal;
	TANGENT = normalize(cross(NORMAL, VIEW_MATRIX[2].xyz));
	BINORMAL = normalize(cross(NORMAL, TANGENT));

	float fresnel = clamp(fresnel_scale * pow(1.0 + dot(VIEW, NORMAL), fresnel_power), 0., 1.);
	ROUGHNESS = fresnel;
	SPECULAR = specular;

	// Depth fade
	float alpha = 1. - depth_curve + depth_curve * smoothstep(.0, 1., vertex_distance/visible_depth);

	// Edge fade
	float proximity_depth_tex = textureLod(depth_texture, SCREEN_UV, 0.0).r;
	vec4 proximity_view_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, proximity_depth_tex, 1.0);
	proximity_view_pos.xyz /= proximity_view_pos.w;
	float edge_mask = 1.0 - clamp(1.0 - smoothstep(proximity_view_pos.z + 2. * edge_fade_distance, proximity_view_pos.z, VERTEX.z), 0.0, 1.0);
	alpha = clamp(alpha - edge_mask, 0., 1.);
	
	// increase alpha with depth as more water means more light absorbed
	alpha = clamp(alpha+ min((VERTEX.z - proximity_view_pos.z) * 0.001 * depth_absorbtion, 1.0), 0.0, 1.0);
	
	// Refraction: Distort screen UVs using the normal (tangent-space projection)
	vec2 refract_offset = NORMAL.xz * refraction_strength;
	// limit refraction at the bottom edge of the screen as normals push away in view direction.
	float y_limit = 1.0 - smoothstep(.0, .25, 1.-SCREEN_UV.y);
	refract_offset = mix(refract_offset, vec2(0.), max(edge_mask, y_limit));
	vec2 refract_uv = SCREEN_UV + refract_offset;
	
	// Invert refract if target screenspace pixel is above ocean surface, replaces major artifact with minor one.
	proximity_depth_tex = textureLod(depth_texture, refract_uv, 0.0).r;
	proximity_view_pos = INV_PROJECTION_MATRIX * vec4(refract_uv * 2.0 - 1.0, proximity_depth_tex, 1.0);
	
	proximity_view_pos.xyz /= proximity_view_pos.w;
	
	refract_uv = SCREEN_UV + mix(refract_offset * (float(proximity_view_pos.z < VERTEX.z) * 2.0 - 1.0), vec2(0.), max(edge_mask, y_limit));
	
	vec3 background = texture(screen_texture, refract_uv).rgb;
	
	// Approximate light scattering 
	vec3 ray_dir = normalize(mat3(INV_VIEW_MATRIX) * VIEW);
	// light scattering based on height and sun angle
	vec3 light_factor = height_scattering * max(h, height_threshold) * max(pow(dot(normalize(light_direction), -ray_dir), 4.0), height_angle_threshold)* light_color * pow(0.5 - 0.5 * dot(normalize(light_direction), world_normal), 3.0);
	// light scattering based on view angle
	light_factor += fersnel_scattering *pow(dot(ray_dir, world_normal), 2.0) * light_color * albedo_color;
	// direct light scattering
	light_factor += direct_scattering * dot(normalize(light_direction), world_normal) * albedo_color* light_color;
	
	ALBEDO = mix(background, albedo_color * light_color, alpha * alpha);
	BACKLIGHT = light_factor * 4.0 * clamp(alpha, 0.4, 1.0);
	ALPHA = clamp(1.0 - edge_mask, 0., 1.);

}

@TokisanGames TokisanGames force-pushed the Ocean4 branch 2 times, most recently from 4ff0bec to b9d014d Compare December 29, 2025 16:39
@DeepCanionStudio
Copy link

added edge and height foam, made a new height function that almost removes tiling
image

i'll paste only the parts of the code that changed

changed some defaults in the uniforms and constants and added foam controls:

//////////////////////////////
// Constants
//////////////////////////////
const int ITERATIONS_VERTEX = 1;
const int ITERATIONS_FRAGMENT =3;

//////////////////////////////
// Uniforms
//////////////////////////////
group_uniforms water;
uniform float time_scale : hint_range(0.0, 16.0) = 3.0;
uniform float height_scale : hint_range(0.0, 16.0) = 3.0;
uniform float ocean_scale : hint_range(0.1, 64.0, 0.1) = 4.0;
uniform float sea_level : hint_range(-50.0, 50.0, 0.1) = 12.0;
uniform vec3 albedo_color : source_color = vec3(.102, .212, .318);
uniform float visible_depth : hint_range(0., 10000., .1) = 256.;
uniform float depth_curve : hint_range(0., 1.) = .15;
uniform float edge_fade_distance : hint_range(0.0, 10.0, 0.01) = 2.;
uniform float refraction_strength : hint_range(0.0, 0.1, 0.001) = 0.07;
uniform sampler2D water_noise : filter_linear_mipmap, repeat_enable;

group_uniforms light_scattering;

uniform bool _light_scattering_enabled = true;

uniform vec3 _light_direction;
uniform vec3 _light_color : source_color = vec3(1.0, 1.0, .735);
uniform float height_scattering : hint_range(0.0, 15.0) = 3.0;
uniform float height_threshold : hint_range(0.0, 1.0) = 0.1;
uniform float height_angle_threshold : hint_range(0.0, 1.0) = 0.21;

uniform float fersnel_scattering : hint_range(0.0, 70.0) = 20.0;
uniform float direct_scattering : hint_range(0.0, 70.0) = 10.0;

uniform float depth_absorbtion : hint_range(0.0, 10.0) = 1.0;

group_uniforms;
group_uniforms reflections;
uniform float fresnel_scale : hint_range(0., 1.) = .065;
uniform float fresnel_power = 1.;
uniform float specular : hint_range(0., 1.) = .5;
group_uniforms;

group_uniforms foam;
uniform bool foam_enabled = true;
uniform bool edge_foam_enabled = true;
uniform sampler2D foam_alpha;
uniform float foam_height : hint_range(-2.0, 5.0) = 2.5;
uniform float foam_scale : hint_range(0.1, 5.0) = 1.5;

ocean height function:

//////////////////////////////
// Ocean Noise
//////////////////////////////

float octave(vec2 uv) {
	float n = texture(water_noise, uv).r;
	return 1.0 - 2.0 * n;
}

vec2 rotateUV(vec2 uv, float rotation) {
	float mid = 0.5;
	return vec2(
		cos(rotation) * (uv.x - mid) + sin(rotation) * (uv.y - mid) + mid,
		cos(rotation) * (uv.y - mid) - sin(rotation) * (uv.x - mid) + mid
	);
}

float get_base_height(vec2 pos, float multiplier, int iterations) {
	float freq = multiplier * ocean_scale;
	float amp = 4.0;
	vec2 uv = pos;
	uv.x *= 0.6;
	float h = 0.0;
	float t = TIME * time_scale / freq * 0.004;

	for (int i = 0; i < iterations; i++) {
	    h += (octave((uv + t) * freq) + octave((uv - t) * freq * 0.89)) * amp;
	   // rotate uv to avoid repetition
	 	uv = rotateUV(uv, 0.2);
		
		// offset smaller waves by the movement of bigger ones
		uv -= h / freq * 0.003;
		
	    freq *= 2.0;
	    amp *= 0.15;
	}
    return h * 0.1 * height_scale;
}
float ocean_height(vec2 pos, int iterations) {
	float giant_wave = get_base_height(pos, 0.00007, iterations );
	float big_wave = get_base_height(pos, 0.0008, iterations);
	float detail_wave = get_base_height(pos, 0.02, iterations);
	return big_wave + detail_wave * 0.02 + giant_wave * 2.0;
}

//////////////////////////////

in the vertex shader, i changed the offset to be on all 3 axis instead of only Y for more interesting displacement:

    UV = (vertex.xz);
	
	// move on every axis for more complex displacement
    vertex += sea_level * vec3(0.0, 1.0, 0.0) + ocean_height(UV, ITERATIONS_VERTEX) * vec3(0.5, 1.0, 0.5);

    VERTEX = (VIEW_MATRIX * vec4(vertex, 1.0)).xyz;

and i changed the last part of the fragment shader to add foam:

vec3 final_color = albedo_color * _light_color;
	float foam_factor = 0.0;
	if (foam_enabled) {
		float detail_wave = get_base_height(UV, 0.005 * foam_scale, 2);
		float foam_texture_alpha = texture(foam_alpha, UV * 0.09 * foam_scale).r;
		foam_factor = clamp(h * 0.5 + (detail_wave * 2.0 +  edge_mask * 100.0 * float(edge_foam_enabled)) * foam_texture_alpha * 1.5 - foam_height,0.0, 1.0);
		
		final_color = mix(final_color, vec3(0.8), foam_factor);
	}
	if (_light_scattering_enabled) {
		// Approximate light scattering
		vec3 ray_dir = normalize(mat3(INV_VIEW_MATRIX) * VIEW);
		// light scattering based on height and sun angle
		vec3 light_factor = height_scattering * max(h, height_threshold) * max(pow(dot(normalize(_light_direction), -ray_dir), 4.0), height_angle_threshold) * _light_color * pow(0.5 - 0.5 * dot(normalize(_light_direction), world_normal), 3.0);
		// light scattering based on view angle
		light_factor += fersnel_scattering *pow(dot(ray_dir, world_normal), 2.0) * _light_color * albedo_color;
		// direct light scattering
		light_factor += direct_scattering * dot(normalize(_light_direction), world_normal) * albedo_color * _light_color;
		BACKLIGHT = light_factor * 4.0 * clamp(alpha, 0.4, 1.0) * max(1.0 - foam_factor, 0.5);
	}
	
	vec3 background = texture(screen_texture, refract_uv).rgb;
	ALBEDO = mix(background, final_color, alpha * alpha);
	ALPHA = clamp(1.0 - edge_mask, 0., 1.);

@JekSun97
Copy link

JekSun97 commented Jan 6, 2026

added edge and height foam

Great job! Looks great and very realistic!

@TokisanGames TokisanGames mentioned this pull request Feb 5, 2026
@ChampionX1001
Copy link
Contributor

ChampionX1001 commented Feb 7, 2026

Great progress, this is something I've wanted in Terrain3D and am glad it is taking shape.

#298 (comment)
As mentioned in issue linked above, it would be nice to have this clipmap be paintable. Instead of just having a background option as Ocean, drawing lakes and rivers would be nice too. A part of this would be to be able to draw the clipmap at different heights, so you can have lakes at different heights.

River flow would also be nice to be able to designate within terrain3D, as I see water inside your terrains as a part of the whole "terrain" ecosystem.

We should test some other popular ocean shaders and make sure they work just by dropping them in, though we may need to add the code snippet for geomorphing the lods that Xtarsia gave. That should be added here.

https://github.com/2Retr0/GodotOceanWaves
Here is a popular (but slept-on) shader for oceans. This shader looks a lot more morphic and realistic than most other I see, and it makes a lot of sense for surrounding islands. It uses the Fast-Fourier-Transform wave simulation developed by Jerry Tessendorf, which was also used in the movie Titanic, and implements it into GDshader. The FFT can simulate lunar tides and caustic dispersion at rapid speeds despite how high-fidelity it looks, but it still requires a lot of processing. However, the settings can be tweaked to make it very performant in Godot. I suggest we plug this in as well and see how it performs.

This shader lacks edge foam. However, it has not been updated for a year, so I could probably add edge foam to it. Edge foam makes a lot of sense considering this is Terrain3D, where there is going to be land lol

@mikest

This comment was marked as off-topic.

@TokisanGames
Copy link
Owner

TokisanGames commented Feb 8, 2026

@ChampionX1001 @mikest Both of these comments could be moved and discussed in #298 .
This PR won't be adding any of those other features. It's just waiting a final review and will be merged in. New additions can come in additional PRs. That ocean shader looks great. Any ocean shader should be able to be dropped in, with the addition of geomorphing vertex code which needs to be isolated and documented.

@ChampionX1001
Copy link
Contributor

This PR won't be adding any of those other features.

Thanks for informing us

Could you elaborate on whether this PR will support geomorphing vertex code out of the box (via the shader drop-in), because the FFT ocean shader I shared uses vertex displacement.

@TokisanGames
Copy link
Owner

@ChampionX1001 You can see what's in the PR right now. The sample shader geomorphing in vertex(). I expect every ocean shader uses vertex displacement, so they will all need the vertex code adapted to work with the clipmap.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request important High priority

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

8 participants