Underwater - add caustic shader on sand texture

Hello everyone, i try to add a caustic sharder on shadermaterial with a texture.
I hope is the right way to do that.

I have some example:
caustic: http://glslsandbox.com/e#54421.0
Underwater scene: https://www.shadertoy.com/view/4ljXWh
Node loader caustic Threejs example: https://threejs.org/examples/?q=node#webgl_loader_nodes

Well you thinks is possible to mix a texture (sand) and a shader ?
I am not comfortable with vertex / fragment shaders implementation.

But with a simple example i succeeded to mix two textures with a png alpha layer.

Thanks for help ! :grinning:


Finally i succeed to add a shader on a box, the hard part is to add the time animation of this shader.

Add in the fragment shader:

<script id="fragment_sh" type="x-shader/x-fragment">

uniform float time; </script>

In uniforms:
var uniforms = { time: { type: "f", value: 1.0 }, };

In the body of threejs script add
var startTime = Date.now();

In the function animate or renderer:

var elapsedMilliseconds = Date.now() - startTime;
var elapsedSeconds = elapsedMilliseconds / 10000.;
uniforms.time.value = 10. * elapsedSeconds;


Now i try of inclure an alpha channel on this shader.
The black must be transparent in this shader.

I hope i can share to you the final result soon, if you have any suggestions, reply to this post !

It’s looking pretty good already, you’ve done a good job!

I have only one small suggestion. Instead of using Date.now(), I suggest using the built-in Three.Clock. It uses perfomance.now() when available which is more accurate than the regular Date.now().

When it’s not available, it defaults back to Date. So you don’t even need to worry about compatibility.

Thanks for the suggestion, i replace by

var startTime = THREE.Clock();

I have no error, but no shader rendering, see the white cube:


You can see the result on my page:


Create an instance const clock = new THREE.Clock() on global scope. Then, inside your update() use this instead:

uniforms.time.value = clock.getDelta();

I see, but this part is the speed animation of the shader, if i change it by
uniforms.time.value = clock.getDelta();
That’s run wihtout error, but no animation on the shader.

After many tests, that run good, i replace ‘Date.now() - startTimeby 'performance.now()
No ‘Date.now()’ is used, but an error appear:

Error: WebGL warning: <SetDimensions>: Exceeded 16 live WebGL contexts for this principal, losing the least recently used one.

var elapsedMilliseconds = performance.now();
var elapsedSeconds = elapsedMilliseconds / 10000.;
uniforms.time.value = 10. * elapsedSeconds;

ok this error, appear only when i refresh the page on local edition, not on server.

Error: WebGL warning: <SetDimensions>: Exceeded 16 live WebGL contexts for this principal, losing the least recently used one.

Now i work on the alpha channel to show the texture under this shader !!!

It’s a second hard part !


Try this:
uniforms.time.value = clock.getElapsedTime();

Take a look at the documentation on this method.


My bad, you are correct. For some reason I thought startTime was the previous time. Which would mean the delta. But in this case it’s the total elapsed time. :upside_down_face:

1 Like

The error message seems to indicate that you are creating new WebGL contexts over and over.

BTW, requestAnimationFrame by current standard passes a millisecond timestamp to the callback.

Shamelessly putting a link to some work I’ve been doing lately concerning water caustics. It’s really low level but you might be interested anyway:

Edit: Oups, sorry for digging up a post that is a year old, I though it was June of this year.


@martinRenou great job :+1:

1 Like