I’ve made a Water class using stuff I found on this forum. Below I’ll copy the files I’m using. Note that this doesn’t manage refractions, only reflections
. In order to use that, you only have to make an instance of the WaterNode class. This is more than what you asked, but I hope you can find the code useful in order to answer to your question.
Water.py
from pandac.PandaModules import CardMaker, Texture, TextureStage, Plane, \
PlaneNode, TransparencyAttrib, CullFaceAttrib, RenderState, ShaderAttrib
class WaterNode():
def __init__( self, x1, y1, x2, y2, z, anim, distort ):
# anim: vx, vy, scale, skip
# distort: offset, strength, refraction factor (0 = perfect mirror,
# 1 = total refraction), refractivity
if not hasattr( self, 'buffer' ):
WaterNode.buffer = base.win.makeTextureBuffer( 'waterBuffer', 512, 512 )
if not hasattr( self, 'watercamNP' ):
WaterNode.watercamNP = base.makeCamera( WaterNode.buffer )
maker = CardMaker( 'water' ) # Water surface
maker.setFrame( x1, x2, y1, y2 )
self.waterNP = render.attachNewNode( maker.generate() )
self.waterNP.setPosHpr( ( 0, 0, z ), ( 0, -90, 0 ) )
self.waterNP.setTransparency( TransparencyAttrib.MAlpha )
self.waterNP.setShader( loader.loadShader( 'water.sha' ) )
self.waterNP.setShaderInput( 'wateranim', anim )
self.waterNP.setShaderInput('waterdistort', distort )
self.waterNP.setShaderInput( 'time', 0 )
self.waterPlane = Plane( ( 0, 0, z + 1 ), ( 0, 0, z ) ) # Reflection plane
PlaneNode( 'waterPlane' ).setPlane( self.waterPlane )
WaterNode.buffer.setClearColor( ( 0, 0, 0, 1 ) ) # buffer
WaterNode.watercamNP.reparentTo( render ) # reflection camera
cam = WaterNode.watercamNP.node()
cam.getLens().setFov( base.camLens.getFov() )
cam.getLens().setNearFar( 1, 5000 )
cam.setInitialState( RenderState.make( CullFaceAttrib.makeReverse() ) )
cam.setTagStateKey( 'Clipped' )
cam.setTagState('True', RenderState.make(
ShaderAttrib.make().setShader(
loader.loadShader( 'splut3Clipped.sha' ) ) ) )
tex0 = WaterNode.buffer.getTexture() # reflection texture, created in
# realtime by the 'water camera'
tex0.setWrapU( Texture.WMClamp ); tex0.setWrapV( Texture.WMClamp )
self.waterNP.setTexture( TextureStage( 'reflection' ), tex0 )
self.waterNP.setTexture( TextureStage( 'distortion' ),
loader.loadTexture( 'water.png' ) ) # distortion texture
self.task = taskMgr.add( self.update, 'waterUpdate', sort = 50 )
def remove( self ):
self.waterNP.removeNode()
taskMgr.remove( self.task )
def destroy( self ):
base.graphicsEngine.removeWindow( WaterNode.buffer )
base.win.removeDisplayRegion(
WaterNode.watercamNP.node().getDisplayRegion( 0 ) )
WaterNode.watercamNP.removeNode()
def update( self, task ):
self.waterNP.setShaderInput( 'time', task.time ) # time 4 H2O distortions
WaterNode.watercamNP.setMat( # update matrix of the reflection camera
base.camera.getMat() * self.waterPlane.getReflectionMat() )
return task.cont
water.sha
//Cg
//Cg profile arbvp1 arbfp1
//
// OUT
// texcoord0: projective mapping for the reflection texture
// texcoord1: texture coords into the distortion map, scaled and animated
//
void vshader( in float4 vtx_position : POSITION,
in float3 vtx_normal : NORMAL,
in float2 vtx_texcoord0 : TEXCOORD0,
in uniform float4 k_time,
in uniform float4 k_wateranim,
in uniform float4x4 mat_modelproj,
in uniform float4x4 mat_modelview,
in uniform float4x4 mat_projection,
out float4 l_position : POSITION,
out float4 l_texcoord0 : TEXCOORD0,
out float4 l_texcoord1 : TEXCOORD1)
{
// transform vertex position by combined view projection matrix
l_position = mul(mat_modelproj, vtx_position);
// projective matrix (MR)
float4x4 scaleMatrix = { 0.5f, 0.0f, 0.0f, 0.5f,
0.0f, 0.5f, 0.0f, 0.5f,
0.0f, 0.0f, 0.5f, 0.5f,
0.0f, 0.0f, 0.0f, 1.0f };
float4x4 matMR = mul(scaleMatrix, mat_modelproj);
// transform the vertex position by the projective
// texture matrix and copy the result into homogeneous
// texture coordinate set 0
l_texcoord0 = mul(matMR, vtx_position);
// water distortion map
// animate and scale distortions
l_texcoord1.xy = vtx_texcoord0.xy * k_wateranim.z + k_wateranim.xy * k_time.x;
}
//
// IN
// tex_0: reflection texture as produced by reflection camera
// tex_1: distortion texture
//
void fshader( in float4 l_texcoord0 : TEXCOORD0,
in float4 l_texcoord1 : TEXCOORD1,
in uniform sampler2D tex_0 : TEXUNIT0,
in uniform sampler2D tex_1 : TEXUNIT1,
in uniform float4 k_waterdistort,
out float4 o_color : COLOR)
{
// calculate distortion from distortion map
float4 distortion = normalize(tex2Dproj(tex_1, l_texcoord1) - k_waterdistort.x) * k_waterdistort.y;
// projectively sample the 2D reflection texture
// o_color.rgb = tex2Dproj(tex_0, l_texcoord0).rgb;
float4 reflection = tex2Dproj(tex_0, l_texcoord0+distortion);
// refraction factor: smaller numbers make the water appear more reflective ("shinier")
float factor = k_waterdistort.z;
// refraction (1.0 = perfect mirror, 0.0 total refraction)
float ref = k_waterdistort.w;
float4 refraction = float4(ref, ref, ref, 0.0f);
// calculate fragment color
o_color = lerp( reflection, refraction, factor );
// optionally set alpha component to transparency,
// a constant value in this simple example
// o_color.a = 0.6;
}
splut3Clipped.sha
//Cg
//Cg profile arbvp1 arbfp1
// shader loosely based on pro-rsoft's from the panda3d forums
// shader assumes 6 texture stages:
// 0-2 textures
// 3-5 alpha maps
// vertex shader accepts unit 0 and 3 texture coords as input
// and outputs unit 0 scaled and unit 3 unchanged
// fragment shader uses unit 0 coordinates to access textures
// and unit 3 coordinates (un-scaled) to access alpha maps
// gsk, june-2008
void vshader( in float4 vtx_position : POSITION,
in float3 vtx_normal : NORMAL,
in float2 vtx_texcoord0 : TEXCOORD0,
in float2 vtx_texcoord3 : TEXCOORD3,
in uniform float4x4 mat_modelproj,
in uniform float4x4 trans_model_to_world,
in uniform float4 k_lightvec,
in uniform float4 k_lightcolor,
in uniform float4 k_ambientlight,
in uniform float4 k_tscale,
out float l_brightness,
out float4 l_mpos,
out float2 l_texcoord0 : TEXCOORD0,
out float2 l_texcoord3 : TEXCOORD3,
out float4 l_position : POSITION)
{
// worldspace position, for clipping in the fragment shader
l_mpos = mul(trans_model_to_world, vtx_position);
l_position=mul(mat_modelproj,vtx_position);
l_texcoord0=vtx_texcoord0*k_tscale;
l_texcoord3=vtx_texcoord3;
// lighting
float3 N = normalize( vtx_normal );
float3 L = normalize( k_lightvec.xyz );
l_brightness = (max( dot( -N, L ), 0.0f )*k_lightcolor)+k_ambientlight;
}
void fshader( in float4 l_position : POSITION,
in float2 l_texcoord0 : TEXCOORD0,
in float2 l_texcoord3 : TEXCOORD3,
in float l_brightness,
in float4 l_mpos,
in uniform float4 k_waterlevel,
in uniform sampler2D tex_0 : TEXUNIT0,
in uniform sampler2D tex_1 : TEXUNIT1,
in uniform sampler2D tex_2 : TEXUNIT2,
in uniform sampler2D tex_3 : TEXUNIT3,
in uniform sampler2D tex_4 : TEXUNIT4,
in uniform sampler2D tex_5 : TEXUNIT5,
out float4 o_color : COLOR )
{
// clipping
if ( l_mpos.z < k_waterlevel.z) discard;
// alpha splatting and lighting
float4 tex1=tex2D(tex_0,l_texcoord0);
float4 tex2=tex2D(tex_1,l_texcoord0);
float4 tex3=tex2D(tex_2,l_texcoord0);
float alpha1=tex2D(tex_3,l_texcoord3).z;
float alpha2=tex2D(tex_4,l_texcoord3).z;
float alpha3=tex2D(tex_5,l_texcoord3).z;
o_color =tex1*alpha1;
o_color+=tex2*alpha2;
o_color+=tex3*alpha3;
o_color=o_color*(l_brightness);
o_color.a=1.0;
}
water.png
