[DX9] Rendering "on top"

Started by
11 comments, last by dave 14 years, 1 month ago
Hello GameDevs ! I'm working on a game world editor where I'm using a 3D transformation gizmo to manipulate the objects, very much like in 3ds max or any other 3d modeling application. The problem I have is that I would like the gizmo to appear on top of everything else so that it is never occluded and hard to grab. The picking in itself is no problem but I was wondering how I might achieve the effect of rendering the gizmo on the very top in the frame images regardless of the actual depth order. Is it a depth stencil I should use? If so, how abouts would I use it? Many thanks in advance !
Advertisement
You want to turn off z testing using:

m_pDirect3DDevice->SetRenderState( D3DRS_ZENABLE, FALSE );

This isn't the full solution however, because z testing won't work for the manipulator model. If you want it to work there you will need to use another depth stencil buffer for the manipulator, unless ofcourse you can get away with clearing the primary depth stencil and reusing it.
Quote:Original post by Dave
You want to turn off z testing using:

m_pDirect3DDevice->SetRenderState( D3DRS_ZENABLE, FALSE );

This isn't the full solution however, because z testing won't work for the manipulator model. If you want it to work there you will need to use another depth stencil buffer for the manipulator, unless ofcourse you can get away with clearing the primary depth stencil and reusing it.


Awesome, I didn't quite catch how to do the last bit though. Being a bit unfamiliar with the use of depth stencils I tried the following:

LPDIRECT3DSURFACE9 pMainDepthStencil = NULL;LPDIRECT3DSURFACE9 pGizmoDepthStencil  = NULL;pDevice->SetRenderState( D3DRS_ZENABLE, FALSE );pDevice->GetDepthStencilSurface( &pMainDepthStencil  );pDevice->SetDepthStencilSurface( pGizmoDepthStencil   );// ... Render the gizmo ...pDevice->SetDepthStencilSurface( pMainDepthStencil  );pDevice->SetRenderState( D3DRS_ZENABLE, TRUE );


Do I need to somehow add the stencils together before presenting the frame?
Don't know what exactly you wanted to achieve with getting the surfaces of depth and stencil buffers (if it's preservation, then that can be achieved simpler, see below).

I think your solution is this:

- draw your scene with z-test and z-write enabled
- switch to no z-test (and no z-write if you want to preserve the z-buffer)
- draw your overlay controls

So the main catch is: What should be on top of everything else is drawn last.

The stencil buffer is a buffer which can be used for additionally masking, for instance for special clipping. It can be used for other things, too, but I think you don't need it for your problem.

Hope that helps.
Quote:Original post by unbird
Don't know what exactly you wanted to achieve with getting the surfaces of depth and stencil buffers (if it's preservation, then that can be achieved simpler, see below).

I think your solution is this:

- draw your scene with z-test and z-write enabled
- switch to no z-test (and no z-write if you want to preserve the z-buffer)
- draw your overlay controls

So the main catch is: What should be on top of everything else is drawn last.

The stencil buffer is a buffer which can be used for additionally masking, for instance for special clipping. It can be used for other things, too, but I think you don't need it for your problem.

Hope that helps.


The manipulator appears on top of everything nicely when I turn off and on the z-test using pDevice->SetRenderState( D3DRS_ZENABLE, FALSE ).

However this also causes the manipulator model to lose its visual depth (since it's a 3D model). Dave explained how to solve this using an additional depth stencil buffer but I don't know quite how to implement it :/
Ah, sorry, made a bad guess from your description. If your overlay is 3D, you do need the z-test.

Instead of turning off z-test for your overlay clear the z-buffer (and only the z-buffer) between the drawing calls.
d3dDevice->Clear(0, null, D3DCLEAR_ZBUFFER,  0, 1.0f, 0);


Quote:Original post by Vanderry
The manipulator appears on top of everything nicely when I turn off and on the z-test using pDevice->SetRenderState( D3DRS_ZENABLE, FALSE ).

However this also causes the manipulator model to lose its visual depth (since it's a 3D model). Dave explained how to solve this using an additional depth stencil buffer but I don't know quite how to implement it :/

Dave means that the simpliest way is to draw your scene, clear the z-buffer and then draw your manipulators (both z-test and z-write stay enabled). If this scenario is OK for you than no extra z-buffers are necessary.

Ok so basically when you create a direct3d device you have a render target and a depth stencil buffer. Both surfaces used for the render target and depth/stencil depend on a format set in the D3DPRESENT_PARAMS. When you draw your main scene, assuming you've not set any new render targets or depth stencils or changed the D3DRS_ZENABLE or D3DRS_ZWRITE render states, you render the visual output to the render target and depth information to the depth stencil. Now when it comes to overlaying your widget, supposing it is a 3d model such as XYZ axis arrows, you don't want to have the arrows drawn with depth testing against the main scenes depth buffer. Otherwise the arrows are obscured by objects and its not overlayed. So to fix this you have two options:

1) This is a crude solution. You simply turn of z testing and z writing so that all parts of the arrows model are drawn. The error here is that your model won't look correct because an arrow that would otherwise be behind another one of the three may actually be drawn on top due to the lack of z testing.

2) The full solution is to create yourself another depth stencil using IDirect3DDevice9::CreateDepthStencilSurface() and set this as the depth stencil buffer when you come to draw your manipulator model while leaving z testing and z writing on. Obviously once you're done drawing all your overlayed stuff you will need to set the depth stencil back to the one used for your primary scene.

The steps you want to take for this are:

1) When your device has been created you cache the current depth stencil target. It will remain set as the current one, but you'll have a pointer to it so that you can set it back later.
2) Create a new depth stencil surface using the method mentioned earlier.
3) Clear the target and depth then draw your primary scene.
4) Set your depth stencil buffer using IDirect3DDevice9::SetDepthStencilSurface().
5) Clear the depth stencil using IDirect3DDevice9::Clear() and pass in only the depth flag.
5) Draw your manipulator models.
6) Set the depth stencil that you cached earlier using IDirect3DDevice9::SetDepthStencilSurface() so that when you come to draw your primary scene next time you using the same buffer again.

Ofcourse, you don't strictly have to do what i said, the key thing to get right is using different depth stencils for the primary and manipulator scenes.

Hope that helps,
Ah, now I get it... I can't believe I didn't understand such a simple procedure, guess I expected it to be DirectX-complicated x)

Thanks a lot all of you guys !
I obviously just didn't explain it very well :D.

This topic is closed to new replies.

Advertisement