I run this script simulating water reflection, gathered from various samples on these forums:
from pandac.PandaModules import Vec4, TextureStage, ShaderAttrib, \
TransparencyAttrib, Texture, InternalName, \
loadPrcFileData, CardMaker, CullFaceAttrib, \
BitMask32, LMatrix4f, ConfigVariableManager
from direct.actor.Actor import Actor
from direct.showbase.ShowBase import ShowBase
from direct.task import Task
from direct.directutil import Mopath
from direct.interval.MopathInterval import MopathInterval
from direct.gui.DirectDialog import OkDialog
loadPrcFileData("", "model-path $MAIN_DIR/data")
loadPrcFileData("", "show-frame-rate-meter #t")
loadPrcFileData("", "win-size 1024 768")
loadPrcFileData("", "gl-debug #t")
#loadPrcFileData('', 'dump-generated-shaders #t')
if __name__ == '__main__':
app = ShowBase()
planeHeight = 2.0
transparencyAttrib = TransparencyAttrib.make(TransparencyAttrib.MAlpha)
###bufferToTexture
reflBuf = app.win.makeTextureBuffer("reflection", 512, 512)
reflTex = reflBuf.getTexture()
reflBuf.setClearColor(app.win.getClearColor() - Vec4(0.1, 0.1, 0.1, 0.0))
reflCam = app.makeCamera(reflBuf)
reflCam.node().getLens().setFov(app.camLens.getFov())
reflCam.node().getLens().setNear(app.camLens.getNear())
reflCam.node().getLens().setFar(app.camLens.getFar())
reflCamMask = BitMask32(2)
reflCam.node().setCameraMask(reflCamMask)
#reflCam render the (opposite wrt z) scene of render
reflCam.reparentTo(app.cam)
#what is rendered by reflCam should have these render attributes
reflCullAttr = CullFaceAttrib.makeReverse()
reflShaderAttr = ShaderAttrib.make()
reflShaderAttr = reflShaderAttr.setShader(app.loader.loadShader('water_reflect.cg'))
reflShaderAttr = reflShaderAttr.setShaderInput(InternalName.make('h'), planeHeight)
#
reflState = app.render.getState()
reflState = reflState.addAttrib(reflCullAttr)
reflState = reflState.addAttrib(reflShaderAttr)
#reflState = reflState.addAttrib(transparencyAttrib)
reflCam.node().setTagStateKey('reflCam')
reflCam.node().setTagState('True', reflState)
#direct camera state
dirCamMask = BitMask32(1)
app.cam.node().setCameraMask(dirCamMask)
app.cam.node().setTagStateKey('dirCam')
#direct
directShaderAttr = ShaderAttrib.make()
directShaderAttr = directShaderAttr.setShader(app.loader.loadShader('water_direct.cg'))
directState = app.render.getState()
directState = directState.addAttrib(directShaderAttr)
#directState = directState.addAttrib(transparencyAttrib)
app.cam.node().setTagState('direct', directState)
###environment
# skybox
skybox = app.loader.loadModel('skybox')
# make big enough to cover whole terrain, else there'll be problems with the water reflections
skybox.setZ(0)
skybox.setScale(100)
skybox.setBin('background', 1)
skybox.setDepthWrite(0)
skybox.setLightOff()
skybox.reparentTo(app.render)
#
#plane
cm = CardMaker("plane")
cm.setFrame(-1, 1, -1, 1)
plane = app.render.attachNewNode(cm.generate())
plane.setP(-90.0)
plane.setZ(planeHeight)
plane.setScale(100.0)
# reflection texture, created in realtime by the 'water camera'
reflTex.setWrapU(Texture.WMClamp)
reflTex.setWrapV(Texture.WMClamp)
ts0 = TextureStage('reflection')
plane.setTexture(ts0, reflTex)
# distortion texture
tex1 = app.loader.loadTexture('maps/water.png')
ts1 = TextureStage('distortion')
plane.setTexture(ts1, tex1)
#set render attributes directly
#set shader for plane
plane.setShader(app.loader.loadShader('water_projective.cg'))
# vx, vy, scale, skip
plane.setShaderInput('wateranim', Vec4(0.03, -0.03, 24.0, 0))
# offset, strength, refraction factor (0=perfect mirror, 1=total refraction), refractivity
plane.setShaderInput('waterdistort', Vec4(0.0, 4.0, 0.4, 0.45))
plane.setShaderInput('time', 0.0)
def updateShadersInputs(task):
plane.setShaderInput('time', task.time)
return Task.cont
app.taskMgr.add(updateShadersInputs, "updateShadersInputs")
#set transparency for plane
plane.setAttrib(transparencyAttrib)
#hide plane during reflection pass
plane.hide(reflCamMask)
#panda actor
pathScale = 15.0
pandaActor = Actor("panda", {"anim": "panda-walk"})
pandaActor.setScale(0.5 / pathScale)
pandaPlatform = app.render.attachNewNode("pandaPlatform")
pandaActor.reparentTo(pandaPlatform)
#
motionPath = Mopath.Mopath()
motionPath.loadFile("mopath")
motionPath.fFaceForward = True
pandaPlatform.setPos(0, 40, 0)
pandaPlatform.setScale(pathScale)
# panda animation
pandaActor.loop("anim", restart=0)
pandaInterval = MopathInterval(motionPath, pandaActor, name="pandaInterval")
pandaInterval.loop(playRate= -0.01)
#
app.render.setTag("reflCam", "True")
app.camera.setPos(0.0, -60.0, 10.0)
app.camera.lookAt(0.0, 20.0, 0.0)
mat = LMatrix4f(app.camera.getTransform().getMat())
mat.invertInPlace()
app.mouseInterfaceNode.setMat(mat)
#
okDialog = OkDialog(text="Water reflection test", command=lambda(arg): okDialog.cleanup())
#
app.run()
But it gives repeatedly these warnings:
...
:display:gsg:glgsg(warning): Program undefined behavior warning: Sampler object 3 is bound to non-depth texture reflection, yet it is used with a program that uses a shadow sampler. This is undefined behavior.
:display:gsg:glgsg(warning): Program undefined behavior warning: Sampler object 4 is bound to non-depth texture water, yet it is used with a program that uses a shadow sampler. This is undefined behavior.
...
I tested it on linux debian 8 (jessie) with “GeForce GTX 970” and " GeForce GTX 770" gpus with NVIDIA proprietary driver (ver. 349.16 - GLX ver. 1.4, OpenGL 4.5.0).
I tried to compile the custom shaders with cgc reporting neither errors nor warnings.
I attached the complete sample.
water_reflection.zip (1.24 MB)