Reflecting Multiple Objects

Started by
5 comments, last by khulad 13 years, 2 months ago
Hi again guys,
I've been looking at Frank D Luna's method of reflecting an object in a (planar) mirror.
I understand the code, and it works fine.
However, I tried to reflect another object (now reflecting two objects), and the second object when behnd the first object is been reflected in the mirror as if it's actually in-front of it. I know this is because the technique disables depth writes when drawing the reflected object (so the mirror doesn't obscure it).
So my question is, how can I adapt this technique to successfully reflect multiple objects?

The code that does the reflection of one object is below:

HR(gd3dDevice->SetRenderState(D3DRS_STENCILENABLE, true));
HR(gd3dDevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS));
HR(gd3dDevice->SetRenderState(D3DRS_STENCILREF, 0x1));
HR(gd3dDevice->SetRenderState(D3DRS_STENCILMASK, 0xffffffff));
HR(gd3dDevice->SetRenderState(D3DRS_STENCILWRITEMASK, 0xffffffff));
HR(gd3dDevice->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP));
HR(gd3dDevice->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP));
HR(gd3dDevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE));

// disable writes to the depth and back buffers
HR(gd3dDevice->SetRenderState(D3DRS_ZWRITEENABLE, false));
HR(gd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true));
HR(gd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO));
HR(gd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE));

// Draw mirror to stencil only.
drawMirror();

// Re-enable depth writes
HR(gd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE, true ));

// Only draw reflected teapot to the pixels where the mirror
// was drawn to.
HR(gd3dDevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL));
HR(gd3dDevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP));

// Build Reflection transformation.
D3DXMATRIX R;
D3DXPLANE plane(0.0f, 0.0f, 1.0f, 0.0f); // xy plane
D3DXMatrixReflect(&R, &plane);

// Save the original world matrices.
D3DXMATRIX oldTeapotWorld = mTeapotWorld;

// Add reflection transform.
mTeapotWorld = mTeapotWorld * R;

// Reflect light vector also.
D3DXVECTOR3 oldLightVecW = mLightVecW;
D3DXVec3TransformNormal(&mLightVecW, &mLightVecW, &R);
HR(mFX->SetValue(mhLightVecW, &mLightVecW, sizeof(D3DXVECTOR3)));

// Disable depth buffer and render the reflected teapot.
HR(gd3dDevice->SetRenderState(D3DRS_ZENABLE, false));
HR(gd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false));

// Finally, draw the reflected teapot
HR(gd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW));
drawTeapot();
mTeapotWorld = oldTeapotWorld;
mLightVecW = oldLightVecW;

// Restore render states.
HR(gd3dDevice->SetRenderState(D3DRS_ZENABLE, true));
HR(gd3dDevice->SetRenderState( D3DRS_STENCILENABLE, false));
HR(gd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW));


Thankyou for your time and patience.
Advertisement

I know this is because the technique disables depth writes when drawing the reflected object (so the mirror doesn't obscure it).
[/quote]

You shouldn't have to disable depth writing to draw a reflection.

Maybe you can:

1) Render all non-reflected objects
2) Clear the depth buffer
3) Render the reflected objects

That way you can still use the depth buffer, but the rest of the scene depth will be cleared so it's not obstructing the reflected objects.
[size=2]My Projects:
[size=2]Portfolio Map for Android - Free Visual Portfolio Tracker
[size=2]Electron Flux for Android - Free Puzzle/Logic Game
Doesn't seem to work when I do that. :(
I agree with karvosts: For rendering solid objects disabling depth does not sound right. And actually Luna's example does not even work with one object (if it's non-convex, which the teapot is). Take a look here:

c94d24118546199.jpg

The only thing I changed was the teapots world matrix, i.e. rotate and take it closer to the mirror. Notice the depth artifact in the mirror at the teapot's handle.

Cannot imagine a way with this setup, i.e. first the scene, then the reflection, because I think, yes, you should clear depth in-between, but only where you see your mirror pane. I wonder if this is even possible in DX 9 (e.g. masked clearing with the stencil).

But how about this (meant for discussion, not sure it works):

  • Clear all
  • Draw the mirror pane to the stencil, so setting up the mask (no color, no depth)
  • Draw your reflected scene enabling depth, but only draw where your stencil mask allows. (*)
  • Clear depth. From now on we don't need the stencil anymore.
  • Draw the mirror pane again, this time only depth, effectively "solidifying" the mirror pane.
    (You might consider using color and blending here, e.g. to achieve a tint or a dust effect.)
  • Draw your scene normally, i.e. with depth again.

(*) At this step I think one has to be careful because a mirroring transformation does not exactly work the same way a real life mirror does. The latter reflects objects from "your side" to the "mirror side", but it does not reflect objects behind the mirroring plane to "your side". A mirroring transformation yet does, it just reflects everything at the mirror plane. One general solution I see is to use the mirror plane for clipping when rendering the reflected scene. Not sure how to setup this in DX, but it does allow arbitrary clipping planes AFAIK. This way you could even let objects penetrate the mirror, I think.

Another, non-general approach: Make sure your objects don't intersect the mirror plane and - in the reflection pass - only render objects which are in front of the mirror plane.
If you want to clear the depth buffer only where the stencil is set, then just do this:

1. Turn depth tests off.
2. Turn stencil tests on.
3. Turn depth writes on.
4. Turn colour writes off.
5. Render a full screen quad, at the appropriate Z value to clear to.

B y the way D3D does support hardware clip planes if you need them. Use D3DRS_CLIPPLANEENABLE and IDirect3DDevice9::SetClipPlane() to turn them on.
Nice one Adam_42. Facepalm on my side ;)
Thanks guys I'll try and get it working.

This topic is closed to new replies.

Advertisement