• 3D
  • [Godot 3] water shader

Hi there,

My game needs some water. Is there a shader available for Godot 3 ?

And what exactly are you looking for? River? Sea? Oceanic? Pond? A Puddle? more detail, please.

@Megalomaniak said: And what exactly are you looking for? River? Sea? Oceanic? Pond? A Puddle? more detail, please.

It's for rivers and lakes using a mesh or a plane with a normal map animation or any other animation trick that would look like water.

@Megalomaniak said: Mobile game, desktop? minimum HW requrements?

Godot 3 for medium HW GTX 950.

So based on: http://jayconrod.com/posts/34/water-simulation-in-glsl

shader_type spatial;
//render_mode world_vertex_coords;

uniform vec4 color : hint_color;

uniform int numWaves;
uniform float waterHeight;
uniform float wavelength = 8.0;
uniform float amplitude = 8.0;
uniform float speed = 8.0;
uniform vec2 direction = vec2(8.0, 8.0);

uniform float pi = 3.1415926535897932384626433832795;
varying vec3 position;
varying vec3 worldNormal;
varying vec3 eyeNormal;
uniform vec3 eyePos;

float wave(int i, float x, float y, float t) {
	float frequency = 2.0 * pi / wavelength;
	float phase = speed * frequency;
	float theta = dot(direction, vec2(x, y));
	return amplitude * sin(theta * frequency + t * phase);
}

float waveHeight(float x, float y, float t) {
	float height = 0.0;
	for (int i = 0; i < numWaves; i++) {
		height += wave(i, x, y, t);
	}
	return height;
}

float dWaveDx(int i, float x, float y, float t) {
	float frequency = 2.0 * pi / wavelength;
	float phase = speed * frequency;
	float theta = dot(direction, vec2(x, y));
	float A = amplitude * direction.x * frequency;
	return A * cos(theta * frequency + t * phase);
}

float dWaveDy(int i, float x, float y, float t) {
	float frequency = 2.0 * pi / wavelength;
	float phase = speed * frequency;
	float theta = dot(direction, vec2(x, y));
	float A = amplitude * direction.y * frequency;
	return A * cos(theta * frequency + t * phase);
}

vec3 waveNormal(float x, float y, float t) {
	float dx;
	float dy;
	for (int i; i < numWaves; i++) {
		dx += dWaveDx(i, x, y, t);
		dy += dWaveDy(i, x, y, t);
	}
	vec3 n = vec3(-dx, -dy, 1.0);
	return n;
}

void vertex() {
	vec4 pos = vec4(VERTEX, 1.0).xzyw;
	pos.z = (waterHeight + waveHeight(pos.x, pos.y, TIME));
	position = pos.xzy / pos.w;
	worldNormal = waveNormal(pos.x, pos.y, TIME);
	eyeNormal = (MODELVIEW_MATRIX * vec4(worldNormal, 0.0).xzyw).xyz;
	NORMAL = eyeNormal;
	TANGENT = eyeNormal;
	BINORMAL = eyeNormal;
	VERTEX = (position).xyz;
}

void fragment() {
	SPECULAR = float(1.0);
	ROUGHNESS = float(0.05);
//	CLEARCOAT = float(0.99);
//	CLEARCOAT_GLOSS = float(1.0);
	ALBEDO = color.rgb;
	ALPHA = color.a;
}

Its just a simple vertex waveform and needs a vertex dense grid mesh. But its a quick hack job so, like, eh. I do intend to make a proper water shader at some point but I need to familiarize myself with the new shader system more first. Have gotten too used to nodal graph shaders.

@Megalomaniak said: So based on: http://jayconrod.com/posts/34/water-simulation-in-glsl

shader_type spatial;
//render_mode world_vertex_coords;

uniform vec4 color : hint_color;

uniform int numWaves;
uniform float waterHeight;
uniform float wavelength = 8.0;
uniform float amplitude = 8.0;
uniform float speed = 8.0;
uniform vec2 direction = vec2(8.0, 8.0);

uniform float pi = 3.1415926535897932384626433832795;
varying vec3 position;
varying vec3 worldNormal;
varying vec3 eyeNormal;
uniform vec3 eyePos;

float wave(int i, float x, float y, float t) {
	float frequency = 2.0 * pi / wavelength;
	float phase = speed * frequency;
	float theta = dot(direction, vec2(x, y));
	return amplitude * sin(theta * frequency + t * phase);
}

float waveHeight(float x, float y, float t) {
	float height = 0.0;
	for (int i = 0; i < numWaves; i++) {
		height += wave(i, x, y, t);
	}
	return height;
}

float dWaveDx(int i, float x, float y, float t) {
	float frequency = 2.0 * pi / wavelength;
	float phase = speed * frequency;
	float theta = dot(direction, vec2(x, y));
	float A = amplitude * direction.x * frequency;
	return A * cos(theta * frequency + t * phase);
}

float dWaveDy(int i, float x, float y, float t) {
	float frequency = 2.0 * pi / wavelength;
	float phase = speed * frequency;
	float theta = dot(direction, vec2(x, y));
	float A = amplitude * direction.y * frequency;
	return A * cos(theta * frequency + t * phase);
}

vec3 waveNormal(float x, float y, float t) {
	float dx;
	float dy;
	for (int i; i < numWaves; i++) {
		dx += dWaveDx(i, x, y, t);
		dy += dWaveDy(i, x, y, t);
	}
	vec3 n = vec3(-dx, -dy, 1.0);
	return n;
}

void vertex() {
	vec4 pos = vec4(VERTEX, 1.0).xzyw;
	pos.z = (waterHeight + waveHeight(pos.x, pos.y, TIME));
	position = pos.xzy / pos.w;
	worldNormal = waveNormal(pos.x, pos.y, TIME);
	eyeNormal = (MODELVIEW_MATRIX * vec4(worldNormal, 0.0).xzyw).xyz;
	NORMAL = eyeNormal;
	TANGENT = eyeNormal;
	BINORMAL = eyeNormal;
	VERTEX = (position).xyz;
}

void fragment() {
	SPECULAR = float(1.0);
	ROUGHNESS = float(0.05);
//	CLEARCOAT = float(0.99);
//	CLEARCOAT_GLOSS = float(1.0);
	ALBEDO = color.rgb;
	ALPHA = color.a;
}

Its just a simple vertex waveform and needs a vertex dense grid mesh. But its a quick hack job so, like, eh. I do intend to make a proper water shader at some point but I need to familiarize myself with the new shader system more first. Have gotten too used to nodal graph shaders.

This is a start, changing the camera angle the effect stops working.

That looks like you need to tweak the uniforms(from the material interface). If you are willing to share the scene/project I can try to tweak them myself and/or debug it.

@Megalomaniak said: That looks like you need to tweak the uniforms(from the material interface). If you are willing to share the scene/project I can try to tweak them myself and/or debug it.

Download demo https://upload.cat/781515baa90f24cf]( https://upload.cat/781515baa90f24cf " https://upload.cat/781515baa90f24cf")

I would be interested in a shader working with planes instead of dense vertex grids. The water effects could be done blending two normal maps through time with linear values or using some atlas normal map animation.

Looking at your 'water plane' scene it has more going on than needs, seems to be to-do with the dae importer I guess.

Anyways. I created a meshinstance with a plane mesh node as a comparison/control measure and a new material for both(not each) planes(yours and mine) and saved the material to a file, then gave the material a new shader(again saved to file), pasted the above shader code, reformatted it a bit since the forums code tag messes up the indentation(although that shouldn't affect the functionality) and then I actually tweaked the shader parameters:

Here is how the water looks in the level with these parameters:

Now since I've set the flow direction to be along X axis a lot of the polygons could be removed because in this instance only mesh density along X affects the visual result.

Next step would be to add a Boolean check to enable/disable the vertex waveform. And then add a UV scrolling function which could be used to animate/scroll a tiling bump/normalmap. As I mentioned earlier I do intend to create a more complex water shader. This was just a quick hack for a starting point so you could have a placeholder. ;)

PS. holy he... ...llo. Those blue floating spheres are very wasteful in terms of polys. Seriously, just one is 27024 vertices.