Reflectionmap question

hello folks,

I am trying to get some code translated for a Panda3D tutorial in c++ to share with the community but I got some problems.

the original code is doing this

/*------------------------------------------------------------------------------------------
 * Render to the Reflection Map
 */
//clip objects below the water line, and render the scene upside down
GraphicsDevice.RenderState.CullMode = CullMode.CullClockwiseFace;

GraphicsDevice.SetRenderTarget(0, mReflectionMap);
GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, mOptions.WaterColor, 1.0f, 0);

//reflection plane in local space
//the w value can be used to raise or lower the plane to hide gaps between objects and their
//reflection on the water.
Vector4 waterPlaneL = new Vector4(0.0f, -1.0f, 0.0f, 0.0f);

Matrix wInvTrans = Matrix.Invert(mWorld);
wInvTrans = Matrix.Transpose(wInvTrans);

 //reflection plane in world space
 Vector4 waterPlaneW = Vector4.Transform(waterPlaneL, wInvTrans);

 Matrix wvpInvTrans = Matrix.Invert(mWorld * mViewProj);
 wvpInvTrans = Matrix.Transpose(wvpInvTrans);

 //reflection plane in homogeneous space
 Vector4 waterPlaneH = Vector4.Transform(waterPlaneL, wvpInvTrans);

 GraphicsDevice.ClipPlanes[0].IsEnabled = true;
 GraphicsDevice.ClipPlanes[0].Plane = new Plane(waterPlaneH);

 Matrix reflectionMatrix = Matrix.CreateReflection(new Plane(waterPlaneW));

 if(mDrawFunc != null)
     mDrawFunc(reflectionMatrix);

 GraphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;
 GraphicsDevice.ClipPlanes[0].IsEnabled = false;

 GraphicsDevice.SetRenderTarget(0, null);

and here what I have got so far

NodePath root = gTheWindow->get_render();
PT(PlaneNode) planeNode = new PlaneNode("WaterPlane");
NodePath clipPlaneNP = root.attach_new_node(planeNode);

//pass the position of the camera to the shader
PT(Camera) pCamera = new Camera("ReflectionCam");
PT(PerspectiveLens) pLens = new PerspectiveLens();
pLens->set_film_size(800, 600);
pCamera->set_lens(pLens);
NodePath cameraNP = clipPlaneNP.attach_new_node(pCamera);

const LMatrix4f& worldMat = root.get_mat();
const LMatrix4f& worldViewProj = cameraNP.get_mat();
/*------------------------------------------------------------------------------------------
* Render to the Reflection Map
*/

LVector4f waterPlaneL(0.0f, -1.0, 0.0f, 0.0f);

LMatrix4 wInvWorldTrans;
wInvWorldTrans.invert_from(worldMat);
wInvWorldTrans.transpose_from(wInvWorldTrans);

LVector4f waterPlaneW = waterPlaneL * wInvWorldTrans;

LMatrix4 wvpInvWorldTrans;
wvpInvWorldTrans.invert_from(worldViewProj);

LVector4f waterPlaneH = waterPlaneL * wvpInvWorldTrans;

LPlanef clipPlane(waterPlaneH);
planeNode->set_plane(clipPlane);
	
LPlanef tempPlane(waterPlaneW);
LMatrix4f reflectionMat = tempPlane.get_reflection_mat();

PT(GraphicsOutput) myBuffer = gTheWindow->get_graphics_output()->make_texture_buffer("My Buffer", 512, 512);
	
myBuffer->set_clear_color(mOptions.WaterColor);
PT(DisplayRegion) displayRegion = myBuffer->make_display_region();
displayRegion->set_camera(cameraNP);

mReflectionMap = myBuffer->get_texture();

cameraNP.set_clip_plane(clipPlaneNP);
		
CPT(RenderState) pRenderState = RenderState::make_empty();
pRenderState->set_attrib(CullFaceAttrib::make(CullFaceAttrib::M_cull_clockwise));
pRenderState->set_attrib(ClipPlaneAttrib::make(ClipPlaneAttrib::O_add, planeNode));

pCamera->set_initial_state(pRenderState);

now then, first of all I get a Panda warning

:pgraph(warning): Using deprecated ClipPlaneAttrib interface.

if anyone has got some input I would welcome any suggestion!

any input is welcome! :slight_smile:

Use something like this instead:

CPT(ClipPlaneAttrib) attr;
attr = ClipPlaneAttrib::make();
attr = attr->add_on_plane(clipPlaneNP);
pRenderState->set_attrib(attr);

I don’t believe set_clip_plane on the cameraNP itself has any useful effect.

Hi rdb,

I will try your sugggestion!

About it not having any useful effect…

What this code is supposed to accomplish is create a reflection texture for a water shader.
I think the idea is to place a new camera flip the view and render it to a texture.

Have you got any idea if there is another better approach to this?

rdb’s point is that applying the ClipPlaneAttrib to the camera doesn’t do anything. You want to apply it to the scene instead.

There have been a few examples of performing water reflections, using Panda code written in Python. If you want to make water reflection in C++, it’s probably an easier translation to start from one of these Python examples first.

David

trying rdb’s code I get

error C2679: binary '=' : no operator found which takes a right-hand operand of type 'ConstPointerTo<T>' (or there is no acceptable conversion)
1>        with
1>        [
1>            T=RenderAttrib
1>        ]
1>        i:\projects\awproject\panda3d\include\pointerto.h(109): could be 'PointerTo<T> &PointerTo<T>::operator =(ClipPlaneAttrib *)'
1>        with
1>        [
1>            T=ClipPlaneAttrib
1>        ]
1>        i:\projects\awproject\panda3d\include\pointerto.h(110): or       'PointerTo<T> &PointerTo<T>::operator =(const PointerTo<T> &)'
1>        with
1>        [
1>            T=ClipPlaneAttrib
1>        ]
1>        while trying to match the argument list '(PointerTo<T>, ConstPointerTo<T>)'
1>        with
1>        [
1>            T=ClipPlaneAttrib
1>        ]
1>        and
1>        [
1>            T=RenderAttrib
1>        ]

I have had a look at some python code that delas with water reflection rendering.

I have tried to tranlsate it to c++ but I cant figure out why nothing seem to rendered into my texture.

		NodePath root = gTheWindow->get_render();

	PT(GraphicsOutput) myBuffer = gTheWindow->get_graphics_output()->make_texture_buffer("My Buffer", 512, 512);

	//setup the reflection camera
	PT(Camera) pReflectionCam = new Camera("ReflectionCam");
	PT(PerspectiveLens) pLens = new PerspectiveLens();
	pLens->set_film_size(800, 600);
	pLens->set_near_far(1.0f , 5000.0f);
	pReflectionCam->set_lens(pLens);
	NodePath cameraNP = root.attach_new_node(pReflectionCam);

	PT(DisplayRegion) displayRegion = myBuffer->make_display_region();
	displayRegion->set_camera(cameraNP);

	myBuffer->set_clear_color(mOptions.WaterColor);

	CPT(RenderState) pRenderState = RenderState::make_empty();
	pRenderState->set_attrib(CullFaceAttrib::make(CullFaceAttrib::M_cull_clockwise)); //I tried counter clockwise as well
//	CPT(ClipPlaneAttrib) attr = ClipPlaneAttrib::make(); 
//	attr = attr->add_on_plane(clipPlaneNP); 
//	pRenderState->set_attrib(attr);
	pReflectionCam->set_initial_state(pRenderState);

	PT(PlaneNode) planeNode = new PlaneNode("WaterPlane");
	LPlanef waterPlane(LVector3f(0,0,1), LVector3f(0,0,0));
	planeNode->set_plane(waterPlane);
	NodePath waterPlaneNP = root.attach_new_node(planeNode);
	waterPlaneNP.show();

	LMatrix4f reflectionMat = waterPlane.get_reflection_mat();

	LMatrix4f camMat = cameraNP.get_mat();

	cameraNP.set_mat(reflectionMat * camMat );

	cameraNP.show_bounds(); //doesn't seem to work 

	mReflectionMap = myBuffer->get_texture();
	mReflectionMap->set_wrap_u( Texture::WM_clamp);
	mReflectionMap->set_wrap_v( Texture::WM_clamp);

	PT(TextureStage) pStage0 = new TextureStage("watersurface");
	gOceanNode.set_texture(pStage0, mReflectionMap);

can anyone see something obviously wrong/missing here ?

this is what I see

Does it work when you run the original Python code?

David

I never run python tbh - I don’t even build with my panda version (–no-python).

I got a high end graphics card (NVIDIA GeForce GTX580 series) in case you were thinking that maybe my graphics card doesn’t support it.

I am looking at Flavios example from thus thread:
[url]Water Reflections]

At this point I think the main question is why I am not seeing a reflection rendered onto my texture but the texture is getting filled with my clear color. This makes me think that
A) my camera setup is wrong
Or
B) camera point in wrong direction

Those sound like good guesses to me. You can research this further by trying standard experiments: put an object around (or right in front of) your camera and see if that shows up.

Also check the near and far planes on your camera to make sure they are reasonable.

David

ok I made the test you suggested and here a visual result

so there seems to be multiple problems.

  1. the camera seems to be pointing towards the “green smiley”
  2. the result is magnified (although this might be just because I apply the texture across the waterNode)
  3. the reflected smiley is flickering like crazy

[/url]

You have to either use texture projection to project the texture onto the plane from the camera’s point of view or you have to use a shader to do this.

I intend to use a shader which uses the reflection map. But the reflectionmap seems to be reflecting the wrong plane in xz rather than xy . Any idea why that is ?
(also the code for the attribute you posted above does not compile)

EDIT :

fixed it by setting the camera like this

gCameraNP.set_hpr(0, 90.0f, 0);

and fixed rdb’s code from above by doing this

CPT(RenderAttrib) attr = ClipPlaneAttrib::make();
const ClipPlaneAttrib* pClipPlaneAttr = DCAST(ClipPlaneAttrib, attr);
attr = pClipPlaneAttr->add_on_plane(waterPlaneNP); 
pRenderState->set_attrib(attr);
pReflectionCam->set_initial_state(pRenderState);

this is what I get now - note there is no shaders applied yet.

I understand why the balls are rendered as big as they are but I got a problem left to solve before I can move on to look at my shader.

The reflection texture is flickering like hell…
I setup this whole scene in an initialize frame lets call it “frame0” and after that I setup a task where I update the matrix of the reflection camera.

this is how I setup the task

static AsyncTask::DoneStatus RenderTask(GenericAsyncTask* pTask, void* data)
{  
	LMatrix4f reflectionMat = gWaterPlane.get_reflection_mat();

	LMatrix4f camMat = gCameraNP.get_mat();

	gCameraNP.set_mat(camMat * reflectionMat );    
    return AsyncTask::DS_cont;    
}

void InitRenderTask()
{
	PT(AsyncTaskManager) taskMgr = AsyncTaskManager::get_global_ptr();
	PT(GenericAsyncTask) task = new GenericAsyncTask("RenderTask", &RenderTask, NULL);
	task->set_priority(1);
	task->set_delay(0.0f);
	taskMgr->add(task);
}

I believe what is happening is that the task isn’t executed at the same rate the rendering is done so the camera matrix isnt being setup. Although I would have thought if that was the case the camera matrix would just be the same as the one from the previous frame. shrug

You are setting up a task that runs once per frame. It does not run faster than the rendering.

However, your task takes the current camera matrix, multiplies it by the reflection matrix (which flips it around the mirror), and then stores it back onto the same camera matrix.

Think about what this means: each frame, the camera matrix flips around the mirror. You’ve got it popping back and forth on alternate frames. Of course you have terrible flickering.

I think you meant to read a different camera matrix (the main world camera, for instance), flip it around the mirror, and store that onto the mirror world camera. Not to read the same camera and store it back on itself.

David

makes perfect sense what you said…

the result seems somehow right as in the size of the balls seems correct but …

it looks like I got 2 planes being mirrored on top of each other…

here my updated code

	NodePath root = gTheWindow->get_render();

	gMyBuffer = gTheWindow->get_graphics_output()->make_texture_buffer("My Buffer", 512, 512);

	//setup the reflection camera
	PT(PerspectiveLens) pLens = new PerspectiveLens();
	pLens->set_fov(39.0f);
	DisplayRegion* pRender3D = gTheWindow->get_display_region_3d();
	float w = pRender3D->get_pixel_width();
	float h = pRender3D->get_pixel_height();
	float aspect_ratio = w / h;
	pLens->set_aspect_ratio(aspect_ratio);
	pLens->set_near_far(1.0f, 5000.0f);
	PT(Camera) pReflectionCam = new Camera("BaseCamera", pLens);
	gReflCamNP = root.attach_new_node(pReflectionCam);

	PT(DisplayRegion) displayRegion = gMyBuffer->make_display_region();
	displayRegion->set_camera(gReflCamNP);

	gMyBuffer->set_clear_color(mOptions.WaterColor);

	PT(PlaneNode) planeNode = new PlaneNode("WaterPlane");
	gWaterPlane = LPlanef(LVector3f(0,0,1), LVector3f(0,0,0));

	planeNode->set_plane(gWaterPlane);
	NodePath waterPlaneNP = root.attach_new_node(planeNode);
	waterPlaneNP.show();

	CPT(RenderState) pRenderState = RenderState::make_empty();
	pRenderState->set_attrib(CullFaceAttrib::make(CullFaceAttrib::M_cull_clockwise));
	CPT(RenderAttrib) attr = ClipPlaneAttrib::make();
	const ClipPlaneAttrib* pClipPlaneAttr = DCAST(ClipPlaneAttrib, attr);
	attr = pClipPlaneAttr->add_on_plane(waterPlaneNP); 
	pRenderState->set_attrib(attr);
	pReflectionCam->set_initial_state(pRenderState);

	mReflectionMap = gMyBuffer->get_texture();
	mReflectionMap->set_wrap_u( Texture::WM_clamp);
	mReflectionMap->set_wrap_v( Texture::WM_clamp);
	mReflectionMap->set_minfilter(Texture::FT_linear_mipmap_linear);

 	gpStage0 = new TextureStage("watersurface");
 	gOceanNode.set_texture(gpStage0, mReflectionMap);

and the render loop

void RenderWater()
{
        LMatrix4f reflectionMat = gWaterPlane.get_reflection_mat();

	NodePath cameras = gTheWindow->get_camera_group();
	NodePath defaultCam = cameras.get_child(0);

	LMatrix4f camMat = defaultCam.get_mat();

	gReflCamNP.set_mat(camMat * reflectionMat );

}

here another point of view - note the 2 “mirror” planes that seem to cross.
The flat yellow grid (on the blue surface) is the actual waterPlaneNP.show()result… so the seems to be correct.

You can’t expect to see a meaningful result without either using texture projection or using a shader to handle the projection for you. Right now you’re just applying the texture using the default UV coordinates which isn’t meaningful at all for what you’re trying to do.

I understand that the result wouldnt be meaningful but as I see it my texture is wrong because it contains the double amount of balls (there is 5 balls in the scene and 10 are rendered onto the texture).

here the result with my shader
this picture is taken looking straight down the centre of the texture

I’ll look at my shader a bit more and see if my projection is not working.

With regard to the appearance of the grid and additional balls on the textures, could it perhaps be that the reflection camera can “see” the surface to which you’re applying the reflection, causing it to include that in the reflection, producing a reflection of a reflection?

yeah I was thinking the same but then again how do I prevent that ? I thought the clip plane was meant to take care of that . But to be honest I think the clip plane itself doesn’t seem to do much, I have placed a ball below the water and it still is rendered onto the reflection texture. :cry:

I think you would really do well to study the existing implementations in Python, to get a good idea of how it all is supposed to work. This is probably worth installing Python to do it.

David