• Advertisement

Archived

This topic is now archived and is closed to further replies.

Stencil Buffer Shadow Volumes

This topic is 5524 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I just incorporated the DX8 stencil buffer shadow volume technique into my code, and all is working fine except for one snag. I''m using geometry that does not completely fill the entire viewing volume, so the far-plane clipped shadow volumes are visible as "floating" alpha-blended areas. I''ve tried playing with the stencil and zbuffer render state settings to see if I could get the renderer to drop out those stencil pixels set with a z value equivalent to the far plane, but I haven''t had any success. I haven''t found any way to handle this via a Google search either. I''m hoping someone with a better understanding of the stencil and zbuffer state settings will have some ideas for me to try, other than to throw in some large quad to "hide" these unwanted areas (like the sample code does with its terrain.) It just seems that there should be a way to do it through the state settings and I''m too dense to see it.

Share this post


Link to post
Share on other sites
Advertisement
I use nvidia''s "robust shadow volumes" zfail technique. This works for inward facing type rooms containing outward facing objects. The objects have silhouette edges computed and projected to form quads or two tris in dx8. I use indexed quads. Then the back face is also projected but front face isn''t. The volumes must yield a closed volume thus capping it with front/back shadow polys. For inward facing rooms I use only back facing walls w.r.t. light. I project the edges and back poly normally and I create front poly by reversing vertex order of back poly, you can also clone and reverse back face''s indices. Make absolutely sure that your front faces w.r.t. camera have clockwise or more like consistant winding order otherwise some walls will be partly shadowed and it won''t look right. So if your front face has clockwise winding order and vertex A and following vertex B form an edge then you want to make new shadow poly first vertex B and the other an A. So B is first followed by A. Use the same mechanism when projecting and building shadow volumes from back facing walls w.r.t. light. So that the shadow poly face closest to the viewer gets clockwise winding order when back face has clockwise winding order also. This is the same as when doing shadow volumes on objects with outward facing walls. Remember your rooms are inwardfacing ones. The B becoming A must not be used on rooms instead use back poly''s w.r.t. light winding order to construct the shadow polys. This might be confusing and if you use object''s shadow algo for rooms the shadow will not work fully, some parts of the room are darkened because stencil bits were set there. I didn''t realized this error and tried instead to clear the stencil buffer between rendering rooms which worked but the shadows didn''t propagate into other rooms correctly. Took some head scratching until I found out the problem and corrected it.

The infinate projection matrix won''t work with dx8 because of lack of D3DFVF_XYZW which dx9 will have along with two sided stencil. Currently you can only pass 3component vertices which dx will change to 4component vertices filling ''w'' with 1. Even if you gave 4component vertex to draw function it will fail since vertex stride won''t be right since you don''t have d3dfvf_xyzw flag instead you got d3dfvf_rhw which can''t be used since your vertices must go thru the pipeline for stencil and zbuffer checks to happen. I project my shadow volumes including the back poly cap by making vector from vertex to the light then normalizing it and scaling it by some value. The resultant vertex must be within camera''s frustum and can not be clipped by far plane. The infinate matrix just places this vertex on to the plane. I''ve fiddled with proj. matrix and got it working by looking at the MakeFOVLH() dx function. Take third column and third and fourth row elements I think the (3,3) matrix element is something like f/(f-n) and (3,4) is f*n/(n-f), (n-f) instead of (f-n) to have positive result I think, anyways, these two equations are mutliplied with "(f-n)/f" <-reciprocal, to yield correct values. Basically your vertex''s z=w. Then when divide by w happens it will place vertex into clip space at z=1 i.e. on the clipping plane. The vertex won''t get clipped if it did the counts won''t come out right. Your shadow volumes should be watertight otherwise light gaps follow. With zfail algo the near plane isn''t important because you''re counting from back towards the eye so on zpass the stencil keeps its original value but changes it only on zfail. Ask yourself, is the shadow poly in front of geometry if so it will pass zbuffer check and stencil bits get unchanged. If some parts of poly are behind existing geometry then those will mark the stencil bits. So it''s the shadow poly pixels that fail the zbuffer test that mark the stencil bits. When a shadow poly is behind some geometry the only stencil bits get marked are those that are behind the offending geometry, once they''re marked light won''t draw there. I use ambient for my rooms and draw ambient rooms first then I do shadow volumes then I draw light on zero stencil bits then I draw rooms again but this time adding in texture. I use colored attenuated per pixel lights. Point lights use one 2D attenuated map with z computed on cpu and used in texture factor to save one texture stage, the spotlights use the same attenuated map along with cubemap. This is lots of work on gf2 w/o doing any bump maps. I like how the lighted portion(the light coming thru a doorway from a hallway into ambient lit room) gets moved across the back wall of the room as the light in the hallway is moved from side to side. I''m also planning to increase the ambient level when light is turned on and decrease it with light going out. This should somewhat simulate real life direct lighting. My ambient are done per cell.

When you draw lights have Z_FUNCTION_LESSEQUAL flag insted LESS so that light is put into the scene, otherwise the newly rendered polys won''t pass zbuffer test and won''t be drawn. You can turn off stencil buffer after you shadow volumes and lighting passes right before you do the texturing pass. Lighting pass requires stencil compare and zbuffer testing to work. Use Z_FUNCTION_LESS for shadow volumes so that front shadow cap fails the zbuffer test so that the stencil bits get marked canceling or zeroing the stencil buffer where the front poly is drawn so front poly gets illuminated by the light(the front poly was shadowed by back shadow cap face). For room back polys wrt light this doesn''t matter as they''re not illuminated since they''re back facing so your lighting code should already be taking advantage of this self-shadowing trick.

Share this post


Link to post
Share on other sites
Hi JD. First, off thanks for taking the time out to reply in length. It took me a while but I did read through it all and really appreciate your input.

Second, I failed to mention that I have read through nVidia''s article, and compared it with what the DX sample code is doing. I have also read through the article on Gamasutra. As far as I can tell, the technique used in the DX sample is the same as that described in the articles.

The shadow volumes are rendered correctly. It''s just that I do not have a room or other geometry in which my geometry is placed. So the shadow volumes get clipped by the far plane. At render time those pixels representing the shadow volume capped at the far plane are considered visible. Which makes sense since they are within the shadow volume. But realistically they should not be rendered since they are not overshadowing any geometry. They''re essentially false shadows. Those are the pixels I''m trying to prevent from being rendered. The shadows which do fall on my geometry are working fine.

I do appreciate your input, especially with regards to the infinite plane. I hadn''t thought of the missing xyzw FVF. I guess I''ll just have to go with a room box.

To help with visualization, here''s a screen shot. Please ignore the jpg artifacts. The two "floating," blue pieces are what I''d like to get rid of.

Share this post


Link to post
Share on other sites
Hi,

Looks like your near clip plane is making havoc and invalidating the stencil bit count. Try pulling your camera back and see what happens. The shadow polys can be clipped by far plane no problem.

Come to think of it you might also be able to use shadow buffers instead of stencil shadows. They're slow for point lights because you need a cubemap or such but for spotlights they'll work nicely. Yes I think your scene is perfect for shadow buffers, it's an image space algo so no matter how many chess pieces you're adding the rendering speed stays constant. You can use projected spotlight and project some cool texture on the chessboard Like "you win" or "you lost" type message or anything.

[edited by - JD on December 5, 2002 1:33:31 AM]

Share this post


Link to post
Share on other sites
Darrell, I understand what''s going on. I was confused at first seeing that only few chess pieces were casting shadows assuming that some shadows got corrupted during rendering.

Perhaps using frame buffer with an alpha channel and drawing shadows only on the chess board might work. So you draw your board and chess pieces(since you want to cast shadow onto chess pieces that are partially out of the board) to frame buffer marking opaque areas. Then you render your shadow volumes only on opaque pixels(those of the board and chess pieces). Disable alpha testing and draw your light. This is only theoretical and it might not work but try it. Let me know how it goes.

Share this post


Link to post
Share on other sites
quote:
Original post by JD
This might be confusing and if you use object''s shadow algo for rooms the shadow will not work fully, some parts of the room are darkened because stencil bits were set there. I didn''t realized this error and tried instead to clear the stencil buffer between rendering rooms which worked but the shadows didn''t propagate into other rooms correctly. Took some head scratching until I found out the problem and corrected it.



I believe the depth-fail technique would work no matter the facing of the room.

quote:

The infinate projection matrix won''t work with dx8 because of lack of D3DFVF_XYZW which dx9 will have along with two sided stencil. Currently you can only pass 3component vertices which dx will change to 4component vertices filling ''w'' with 1.



Not true for DX8.1, u can pass D3DFVF_XYZRHW to by-pass D3D''s transformation and lighting pipeline. But whether D3D renders vectors in clip space properly is another matter.

quote:
I project my shadow volumes including the back poly cap by making vector from vertex to the light then normalizing it and scaling it by some value. The resultant vertex must be within camera''s frustum and can not be clipped by far plane.



It might be better to project ur shadow to infinity and then use a perspective projection matrix to ensure ur shadow volume do not get clipped at the far place.

quote:

The infinate matrix just places this vertex on to the plane. I''ve fiddled with proj. matrix and got it working by looking at the MakeFOVLH() dx function. Take third column and third and fourth row elements I think the (3,3) matrix element is something like f/(f-n) and (3,4) is f*n/(n-f), (n-f) instead of (f-n) to have positive result I think, anyways, these two equations are mutliplied with "(f-n)/f" <-reciprocal, to yield correct values. Basically your vertex''s z=w. Then when divide by w happens it will place vertex into clip space at z=1 i.e. on the clipping plane....


The infinite matrix was derived wrongly. Taking infinity in z or w would crash ur program. Take vertex at z=1 forces all the shadow volume vertices to be drawn at the far plane, which is suceptable to hardware rounding errors.

quote:

When you draw lights have Z_FUNCTION_LESSEQUAL flag insted LESS so that light is put into the scene, otherwise the newly rendered polys won''t pass zbuffer test and won''t be drawn. You can turn off stencil buffer after you shadow volumes and lighting passes right before you do the texturing pass. Lighting pass requires stencil compare and zbuffer testing to work. Use Z_FUNCTION_LESS for shadow volumes so that front shadow cap fails the zbuffer test so that the stencil bits get marked canceling or zeroing the stencil buffer where the front poly is drawn so front poly gets illuminated by the light(the front poly was shadowed by back shadow cap face). For room back polys wrt light this doesn''t matter as they''re not illuminated since they''re back facing so your lighting code should already be taking advantage of this self-shadowing trick.


You could use z-bias to force the shadow volume''s front cap to render behind the occluders. This way, u would not need to meddle with the light drawing which could crash other algorithms that depends on the depth buffer.





- Yen Kwoon, Hun
Some graphics company...

Share this post


Link to post
Share on other sites
Darrell, I meant alpha blending not testing, sorry. Though I would just turn off stenciling but do zdepth testing and draw inward facing box into the scene instead. The shadowed pixels outside the board will then be covered up by the box's pixels.

Flipstar, thanks for your comments. I have not detected zdepth problems so far but maybe in real scenes it will show up. I'll keep the zbias in mind. Currently, for rooms and models I create front cap from back cap but with vertex winding reversed. I only take into account back facing faces w.r.t. light. I project the back face and edges as is normally done not to infinity though. I draw the front cap into stencil buffer with zfunc LESS so it fails and marks the stencil. I can see this might be hw precision troubling. I'm not sure I understand you w.r.t. infinate proj. matrix. Say point's z=3, after infinate projection the point's z=w. The hw does homogenous 'w' divide I think so z would become 1 in clip space. Though maybe float precision will cause some pixels to get clipped I don't know since I can't get d3d8.1 to work with xyzw coordinates. Will go back to infinate proj. matrix when I get dx9 api and will test it.

[edited by - JD on December 5, 2002 3:32:23 PM]

Share this post


Link to post
Share on other sites
quote:
Original post by JD
Currently, for rooms and models I create front cap from back cap but with vertex winding reversed. I only take into account back facing faces w.r.t. light.

For high poly count model, u would want to use front facing geometries to make up the front cap. Using back facing geometries would give u erroneous self-shadowing unless u can gaurantee model symmetry.

quote:
I project the back face and edges as is normally done not to infinity though. I draw the front cap into stencil buffer with zfunc LESS so it fails and marks the stencil. I can see this might be hw precision troubling. I'm not sure I understand you w.r.t. infinate proj. matrix. Say point's z=3, after infinate projection the point's z=w. The hw does homogenous 'w' divide I think so z would become 1 in clip space. Though maybe float precision will cause some pixels to get clipped I don't know since I can't get d3d8.1 to work with xyzw coordinates.

Not true, z would range from 0.0 to 1.0. The idea of semi-infinite vertices projection is to draw vertices with w=0.0 (not z=0.0) in clip space. If you are using D3DFVF_XYZRHW, remember to transform and lit the vertices yourself, else it would never work.




- Yen Kwoon, Hun
Some graphics company...

[edited by - flipstar on December 6, 2002 9:56:30 AM]

Share this post


Link to post
Share on other sites
Hi guys. Sorry for my late reply. We''re "socked in" here in NC w/o electricity at home, so I haven''t had much time to play around with all of the suggestions yet.

I did solve the "floating" objects problem though, by going through the article posted here by Yen Kwoon, Hun (aka flipstar), and making changes as appropriate. I hope to be able to play around next with the other suggestions offered here and elsewhere.

I''m not decided yet if I want high poly chess pieces or not. I''m getting a decent fps since I''m currently only re-rendering during piece movements. That''s because I turn off shadow rendering if the player(s) decided to transform the chessboard/camera/lights, waiting until the requested movement inputs stop to re-render the whole scene. But, maybe LODing to low poly chess pieces during these actions will allow me to have shadows then as well. That would be great because the shadows really do make a vivid difference in the look of the scene.

But rest assured, all input by both of you is very much appreciated. This is my first "non-beginner" effect I''ve tried, so being helped out by the pros is "a godsend."

Share this post


Link to post
Share on other sites
And my apologies for not explaining the scene I posted when I posted it. The only pieces I had rendering shadows in that version were the kings and queens. This is because, at that point, having all pieces render shadows created a really big mess due to the problem I was having.

Share this post


Link to post
Share on other sites
I want to clarify couple of issues. I don''t use infinate projection matrix so all shadow poly vertices have w=1 before xform. I''ve been working on infinate projections using d3dfvf_rhw. You must construct projection matrix then a viewport matrix like it''s described in d3d8 docs. I concatenate view/proj/viewport then do w-divide on verts then they should get clipped. I still have to write clipping code. You could have software do the clipping but that''s inefficient as the api has to back xform thru the viewport matrix and rhw. The gf2 I''m on won''t back project in hw mode only with using software vertex processing. At this junction, the code is getting more complex than non-infinate projected code and not sure it''s worth it over the other method. You don''t have to use lit vertices for shadow volumes since color buffer writes are disabled when drawing them to stencil buffer. You can use stencil/zbuffer test on post-xformed rhw coordinates. You can use FOVLH() d3dx function to make projection matrix then GetViewport() to get vp params from which you can make viewport matrix as described in the docs. Try to avoid negating DWORD before multiplication is done as this will get you incorrect value as I think DWORDs are unsigned. Negate result after you done multiplication. D3D api sets viewport to screen size by default, this is just what we want unless you''re not drawing to entire view screen. Your verts after infinate or not infinate shadow volumes should have their w=1 as d3d docs describe. The w=0 verts will turn into w=1 after infinate projection matrix xform except when you have vertex such as (0,0,0,0). Hopefully, the api detects w=0 in post-xform and won''t divide the components by zero. Reason why w might equal zero is because your z=0. The infinate proj. matrix''s third row third column will be 1 and fourth row and third column will be near plane''s value. Read nvidia''s robust shadow volumes paper for detailed information. Don''t use Opengl''s matrix as described in that paper not even if you transpose it since I don''t think it will work. Look up D3DXMatrixPerspectiveLH() function and multiply (3,3) and (4,3) components by (f-n)/f to yield 1 and near value as I described above. Keep the rest of projection matrix as is. If you''re not going to infinate project your shadow volume verts then use unmodified D3DXMatrixPerspectiveLH() projection matrix. All points w''s are 1 in this case(set by d3d api internally) as you''re using d3dfvf_xyz vertex stride, no room for ''w''. You''ll get hw vertex xform with this algo.

Share this post


Link to post
Share on other sites

  • Advertisement