WaterMaterial sea-level property

There are many custom properties in WaterMaterial doc:

waterMaterial.windForce = 45; // Represents the wind force applied on the water surface
waterMaterial.waveHeight = 1.3; // Represents the height of the waves
waterMaterial.bumpHeight = 0.3; // According to the bump map, represents the pertubation of reflection and refraction
waterMaterial.windDirection = new BABYLON.Vector2(1.0, 1.0); // The wind direction on the water surface (on width and height)
waterMaterial.waterColor = new BABYLON.Color3(0.1, 0.1, 0.6); // Represents the water color mixed with the reflected and refracted world
waterMaterial.colorBlendFactor = 2.0; // Factor to determine how the water color is blended with the reflected and refracted world
waterMaterial.waveLength = 0.1; // The lenght of waves. With smaller values, more waves are generated

One more property would be perfect to hold water on the static sea-level.

When I tried to implement water material into the game, I didn’t immediately realize that the water had tides. To simplify the implementation, I decided to set a fixed Y-axis water level.
However, I later noticed that as the sea ebbs and flows, it rises to level 2.5 and drops to 0.9. This was an issue because I needed to determine the player’s environment (water vs. land) based on their height.
I eventually realized the sea level was tied to the formula itself, so I edited the shader to lock it at a fixed level of 0.0.

Here is the patch I’m currently applying on top of the babylonjs-material implementation.

WaterMaterialPatch.ts (find the row p.y+=0.0; That’s my fix how to make water, but then it lost many beauties, anyway sometimes need to hold ocean on the one level)

/*
 * Patched WaterMaterial vertex shader - fixed water level, no vertex displacement.
 * Original babylonjs-materials uses p.y+=abs(newY) which deforms the surface.
 * We use p.y+=0.0 so the mesh stays flat at the configured position.
 * Bump texture still provides visual wave effect via reflection/refraction distortion.
 * Must be imported before WaterMaterial from babylonjs-materials.
 */

import { Effect } from 'babylonjs'

const WATER_VERTEX_SHADER = `precision highp float;attribute vec3 position;
#ifdef NORMAL
attribute vec3 normal;
#endif
#ifdef UV1
attribute vec2 uv;
#endif
#ifdef UV2
attribute vec2 uv2;
#endif
#ifdef VERTEXCOLOR
attribute vec4 color;
#endif
#include<bonesDeclaration>
#include<bakedVertexAnimationDeclaration>
#include<instancesDeclaration>
uniform mat4 view;uniform mat4 viewProjection;
#ifdef BUMP
varying vec2 vNormalUV;
#ifdef BUMPSUPERIMPOSE
varying vec2 vNormalUV2;
#endif
uniform mat4 normalMatrix;uniform vec2 vNormalInfos;
#endif
#ifdef POINTSIZE
uniform float pointSize;
#endif
varying vec3 vPositionW;
#ifdef NORMAL
varying vec3 vNormalW;
#endif
#if defined(VERTEXCOLOR) || defined(INSTANCESCOLOR) && defined(INSTANCES)
varying vec4 vColor;
#endif
#include<clipPlaneVertexDeclaration>
#include<fogVertexDeclaration>
#include<__decl__lightFragment>[0..maxSimultaneousLights]
#include<logDepthDeclaration>
uniform mat4 reflectionViewProjection;uniform vec2 windDirection;uniform float waveLength;uniform float time;uniform float windForce;uniform float waveHeight;uniform float waveSpeed;uniform float waveCount;varying vec3 vRefractionMapTexCoord;varying vec3 vReflectionMapTexCoord;
#if defined(CLUSTLIGHT_BATCH) && CLUSTLIGHT_BATCH>0
varying float vViewDepth;
#endif
#define CUSTOM_VERTEX_DEFINITIONS
void main(void) {
#define CUSTOM_VERTEX_MAIN_BEGIN
#ifdef VERTEXCOLOR
vec4 colorUpdated=color;
#endif
#include<instancesVertex>
#include<bonesVertex>
#include<bakedVertexAnimation>
vec4 worldPos=finalWorld*vec4(position,1.0);vPositionW=vec3(worldPos);
#ifdef NORMAL
vNormalW=normalize(vec3(finalWorld*vec4(normal,0.0)));
#endif
#ifndef UV1
vec2 uv=vec2(0.,0.);
#endif
#ifndef UV2
vec2 uv2=vec2(0.,0.);
#endif
#ifdef BUMP
if (vNormalInfos.x==0.)
{vNormalUV=vec2(normalMatrix*vec4((uv*1.0)/waveLength+time*windForce*windDirection,1.0,0.0));
#ifdef BUMPSUPERIMPOSE
vNormalUV2=vec2(normalMatrix*vec4((uv*0.721)/waveLength+time*1.2*windForce*windDirection,1.0,0.0));
#endif
}
else
{vNormalUV=vec2(normalMatrix*vec4((uv2*1.0)/waveLength+time*windForce*windDirection ,1.0,0.0));
#ifdef BUMPSUPERIMPOSE
vNormalUV2=vec2(normalMatrix*vec4((uv2*0.721)/waveLength+time*1.2*windForce*windDirection ,1.0,0.0));
#endif
}
#endif
#include<clipPlaneVertex>
#include<fogVertex>
#include<shadowsVertex>[0..maxSimultaneousLights]
#include<vertexColorMixing>
#if defined(POINTSIZE) && !defined(WEBGPU)
gl_PointSize=pointSize;
#endif
#ifdef USE_WORLD_COORDINATES
vec3 p=worldPos.xyz;
#else
vec3 p=position;
#endif
p.y+=0.0;
#ifdef USE_WORLD_COORDINATES
gl_Position=viewProjection*vec4(p,1.0);
#else
gl_Position=viewProjection*finalWorld*vec4(p,1.0);
#endif
#ifdef REFLECTION
vRefractionMapTexCoord.x=0.5*(gl_Position.w+gl_Position.x);vRefractionMapTexCoord.y=0.5*(gl_Position.w+gl_Position.y);vRefractionMapTexCoord.z=gl_Position.w;worldPos=reflectionViewProjection*finalWorld*vec4(position,1.0);vReflectionMapTexCoord.x=0.5*(worldPos.w+worldPos.x);vReflectionMapTexCoord.y=0.5*(worldPos.w+worldPos.y);vReflectionMapTexCoord.z=worldPos.w;
#endif
#include<logDepthVertex>
#define CUSTOM_VERTEX_MAIN_END
}
`

/**
 * Apply patched water vertex shader before babylonjs-materials loads.
 * babylonjs-materials uses ShaderStore.ShadersStore[key]||(ShadersStore[key]=...)
 * so pre-setting our shader makes it use ours instead.
 */
export function applyWaterMaterialPatch(): void {
  Effect.ShadersStore['waterVertexShader'] = WATER_VERTEX_SHADER
}

// Apply on module load so it runs before WaterMaterial import in Ocean.ts
applyWaterMaterialPatch()
1 Like

Or alternatively we can create the function to getCurrentSeaLevel dynamically to have possibility update water level in the world. :ocean: