2
votes

I am trying to make a water surface in WebGL using Three.js. I think I will start with just a mirror as I think I know how to add displacement to make basic ripple effects.

This is what I know: Reflection is usually made by rendering a vertically (y-axis) flipped scene on a FBO using the water plane as a culling plane. Then this FBO is used as a texture for the water plane. Using a displacement map (or a noise texture) the image can be displaced and a water effect achieved.

The problems: First off, I can't find a way to flip the scene in ThreeJS. In OpenGL you can just use glScale and put -1 for Y, but I don't think this is possible in WebGL (or GLES on which it is based). At least I found no such thing in ThreeJS. There is a scale parameter for geometry, but there is none for scene. One solution could be changing the .matrixWorldInverse in Camera, but I am not sure how I could do that. Any ideas?

The second hurdle is clipping/culling plane. Again, the old way was using glClipPlane, but its not supported even in the newest OpenGL standard as far as I know, so its also not in WebGL. I read somewhere that you can do that in vertex shader, but in ThreeJS I only know how to add shaders as materials and I need this during the render to FBO.

And third, rendering the FBO to water plane with correct texture coordinates, so I think basically projecting from the camera position.

I can't find any more information on this on the internet. There are very few WebGL reflection examples and the only thing close was here, and it used some "Oblique View Frustum" method for culling. Is this really the best way to do it nowadays? Instead of one function we now must code this ourselves in software (to be ran on CPU not GPU)? Also cube reflections provided in ThreeJS of course is not applicable for a plane, so yes, I tried those. If someone can make as easy as possible example on how to do this I would greatly appreciate it.

3
This very topic is currently under discussion at github.com/mrdoob/three.js/issues/3856, maybe worth a look :)Lee Stemkoski

3 Answers

4
votes

Check this three.js example out.

Out of the box and ready to use, straight from the source:

water = new THREE.Water( renderer, camera, scene, {
    textureWidth: 512, 
    textureHeight: 512,
    waterNormals: waterNormals,
    alpha:  1.0,
    sunDirection: light.position.clone().normalize(),
    sunColor: 0xffffff,
    waterColor: 0x001e0f,
    distortionScale: 50.0,
} );


mirrorMesh = new THREE.Mesh(
    new THREE.PlaneBufferGeometry( parameters.width * 500, parameters.height * 500 ),
    water.material
);

mirrorMesh.add( water );
mirrorMesh.rotation.x = - Math.PI * 0.5;
scene.add( mirrorMesh );

Seems to look like an ocean to me :)

2
votes

You can see this presentation http://29a.ch/slides/2012/webglwater/

and this fiddle may be useful for you jsfiddle.net/ahmedadel/44tjE

0
votes

This only addresses the scaling part of your question. The matrix that is attached to the Object3D has a makeScale method.