Water plane reflection GLSL

Hi,

(running some of this code requires the dev version of Panda 1.9) This post looks like a lot of code, but it is basically the standard water plane code. If you’ve ever worked with reflective water you’ve probably come accros it somewhere.
Im trying to convert some code from cg to glsl, but im running into some problems with the reflection of my water plane. The problem lies in the glsl code (last two code blocks) but im posting a runnable example.

The panda code for the waterplane reduced to a minimum looks like:

from panda3d.core import *
import direct.directbase.DirectStart


class Water(object):
    def __init__(self):
        #Create the plane geometry and add it to the scene
        maker = CardMaker("grid")
        maker.setFrame( 0, 1, 0, 1)
        self.waterNP = NodePath('waterSurface')
        node = self.waterNP.attachNewNode(maker.generate())
        node.setHpr(0,-90,0)
        node.setScale(20)
        self.waterNP.reparentTo(render)
        self.waterNP.setLightOff(1)
        
        #Add a buffer and camera that will render the reflection texture
        wBuffer = base.win.makeTextureBuffer("water", 512, 512) 
        wBuffer.setClearColorActive(True) 
        wBuffer.setClearColor(base.win.getClearColor()) 
        self.wCamera = base.makeCamera(wBuffer) 
        self.wCamera.reparentTo(render) 
        self.wCamera.node().setLens(base.camLens) 
        self.wCamera.node().setCameraMask(BitMask32.bit(1)) 
        self.waterNP.hide(BitMask32.bit(1))
        
        #Create this texture and apply settings
        wTexture = wBuffer.getTexture() 
        wTexture.setWrapU(Texture.WMClamp) 
        wTexture.setWrapV(Texture.WMClamp) 
        wTexture.setMinfilter(Texture.FTLinearMipmapLinear)
        
        #Create plane for clipping and for reflection matrix
        self.wPlane = Plane(Vec3(0, 0, 1), Point3(0, 0, self.waterNP.getZ())) 
        wPlaneNP = render.attachNewNode(PlaneNode("water", self.wPlane)) 
        tmpNP = NodePath("StateInitializer") 
        tmpNP.setClipPlane(wPlaneNP) 
        tmpNP.setAttrib(CullFaceAttrib.makeReverse()) 
        self.wCamera.node().setInitialState(tmpNP.getState())
        self.waterNP.projectTexture(TextureStage("reflection"), wTexture, self.wCamera)

        #self.waterNP.setShader(loader.loadShader("waterShader.cg"))
        #self.waterNP.setShader(Shader.load(Shader.SLGLSL, "waterV.glsl", "waterF.glsl"))

        taskMgr.add(self.updateWater,"updateWater")
        
        
    def updateWater(self, task):
        #Set the camera to the correct position
        self.wCamera.setMat(base.cam.getMat(render)*self.wPlane.getReflectionMat())
        return task.cont
    

# To test the reflection, add a model
class RandomModel(object):
    def __init__(self):
        self.model = loader.loadModel('???')
        self.model.setLightOff()
        self.model.reparentTo(render)


M = RandomModel()
W = Water()
base.run()

Just running that code should work. The cg shader can now be implemented by uncommenting the corresponding line in the above code and adding this file to the directory:

//Cg
void vshader(
      in float2 vtx_texcoord0 : TEXCOORD0,
      in float4 vtx_position : POSITION,

      uniform float4x4 mat_modelproj,
      uniform float4x4 trans_model_to_world,

      out float4 l_worldPos : TEXCOORD1,
      out float4 l_position : POSITION,
      out float2 l_texcoord0 : TEXCOORD0
) {
   l_worldPos = mul(trans_model_to_world, vtx_position);
   l_position = mul(mat_modelproj, vtx_position);
   l_texcoord0 = vtx_texcoord0;
}

void fshader(
		in float4 l_worldPos : TEXCOORD1,
		in float2 l_texcoord0 : TEXCOORD0,
		
		uniform sampler2D tex_0 : TEXUNIT0,
		uniform float4x4 texmat_0,
		
		out float4 o_color : COLOR0
) {
	float4 texCoordReflec = mul(texmat_0, l_worldPos);
	texCoordReflec.xyz /= texCoordReflec.w;
	o_color = tex2D(tex_0, texCoordReflec.xy);
}

This should still work… If i now, however, want to apply the equivalent glsl shader it gives some strangely warped reflection (this part requires the dev version due to the call to “mat4 trans_model_to_world”):

Vertex:

//GLSL
#version 120

attribute vec4 p3d_MultiTexCoord0;
attribute vec4 p3d_Vertex;

uniform mat4 trans_model_to_world; 

out vec4 worldPos;

void main(){
	worldPos = mul(trans_model_to_world, p3d_Vertex);
	gl_Position = mul(gl_ModelViewProjectionMatrix, p3d_Vertex);
	gl_TexCoord[0] = p3d_MultiTexCoord0; 
}

Fragment:

//GLSL
#version 120

in vec4 worldPos;

uniform sampler2D p3d_Texture0;

void main(){
	vec4 texCoordReflec = mul(gl_TextureMatrix[0], worldPos);
	texCoordReflec.xyz /= texCoordReflec.w;
	gl_FragColor = texture2D(p3d_Texture0, texCoordReflec.xy);
}

Does anyone know what is going wrong? What is the difference between the Cg and glsl code? It seems to be the same thing…

Much appreciated!
Stein

Here is a cut down version of my water shader.i still use gl_ prefixes and i think they are being phased out.
Also this shader uses the cubemap method i think you might need to change some of your syntax.

Vertex

//GLSL

attribute vec2 p3d_MultiTexCoord0;
uniform mat4 trans_model_to_world;
uniform vec3 eyePositionW;
varying vec3 view;
varying vec3 Normal;

void main() {
	Normal = normalize(gl_NormalMatrix*gl_Normal);
	vec3 pos = newVertex*trans_model_to_world;
	view = pos - eyePositionW;

    	gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

Fragment

//GLSL

uniform samplerCube texcube;
varying vec3 view;
varying vec3 Normal;

void main() {
    vec3 Normal = vec3(0,0,1); //i just use upwards facing vector because all my water is flat.
    vec3 reflected = reflect(view, Normal);
    vec4 r = textureCube(texcube,reflected);
    gl_FragColor = r;
}

Thanks for your code!
You’re using cubemaps, does that mean you require 6 cameras? Or do you blank out all 5 sides that arent facing upwards? And dont you have distortion problems?

I would still very much appreciate if someone could look at my code. Im very familiar with it so i prefer using that and i believe it should only be a minor error. Perhaps a bug in the declaration of gl_TextureMatrix[0] that will be fixed in a new development version?