|
||||||||||||||||||
Add Forum to Favorites | Send Topic To a Friend | View Forum FAQ | Track this topic |
Last Thread Next Thread ![]() |
| Shadow Caster Volumes For The Culling Of Potential Shadow Casters |
|
![]() SnprBoB86 Member since: 5/29/2001 |
||||
|
|
||||
| Rather than go through the complex and computationally costly process of computing a shadow volume, you might as well just use a shadow cone. Given the light position and a bounding sphere for each object, you can easily construct a shadow bounding cone and using the code at http://www.geometrictools.com/Foundation/Intersection/Wm3IntrSphere3Cone3.cpp (algo described at: http://www.geometrictools.com/Documentation/IntersectionSphereCone.pdf ) you can quickly collide the cone with the frustum bounding sphere. Sure, it's less percise, but it is probably faster to just have the GPU do the extra work than have the CPU spend too much time figuring out a minimal set for the GPU. |
||||
|
||||
![]() wolf XNA/DirectX MVP Member since: 1/8/2000 From: Carlsbad, CA, United States |
||||
|
|
||||
| I believe the previous poster is right. I guess it is something that you only want to do for stencil shadow volumes. With shadow maps you usually have a light frustum already and then you start to remove occluded objects with a sphere/sphere test, cone/sphere test and then probably a frustum/sphere test by making your frustum a little bit bigger. Some of the perspective shadow map approaches calculated the frustum that is used to render into the shadow maps per frame. This only made sense if you apply shadows to selected objects and not everything in the scene. Is there a valid reason to use stencil shadow volumes anymore? I can see that they are useful on older hardware, but as soon as your min spec is DX9 graphics cards or next-gen consoles, they might not be as good as shadow maps. |
||||
|
||||
![]() Neurovore Member since: 10/12/2006 From: Montreal, Canada |
||||
|
|
||||
| Congratulations on an article well written! But fwiw, it doesn't need to be as complex as this. The convex hull construction reduces to something quite simple for the topological equivalent to a box. Here's some code to illustrate this: // Generate the 8 extreme points of the view frustum cVector3 vertices[8]; frustum.GetPointExtremes(vertices); // Mark all frustum planes that are visible to the light bool plane_visible[cFrustum::NB_PLANES]; for (int j = 0; j < cFrustum::NB_PLANES; j++) plane_visible[j] = (frustum.planes[j].DistanceFrom(light_pos) < 1e-4f); // Convex hull planes list tVector<cPlane> planes; planes.reserve(20); // Add all non-visible planes to the convex hull for (int j = 0; j < cFrustum::NB_PLANES; j++) if (!plane_visible[j]) planes.push_back(frustum.planes[j]); struct Face { int v[4]; int f[4]; }; static Face faces[] = { { // NEAR { 4, 5, 7, 6 }, { cFrustum::TOP, cFrustum::RIGHT, cFrustum::BOTTOM, cFrustum::LEFT } }, { // FAR { 0, 2, 3, 1 }, { cFrustum::LEFT, cFrustum::BOTTOM, cFrustum::RIGHT, cFrustum::TOP } }, { // LEFT { 0, 4, 6, 2 }, { cFrustum::TOP, cFrustum::NEAR, cFrustum::BOTTOM, cFrustum::FAR } }, { // RIGHT { 5, 1, 3, 7 }, { cFrustum::TOP, cFrustum::FAR, cFrustum::BOTTOM, cFrustum::NEAR } }, { // TOP { 0, 1, 5, 4 }, { cFrustum::FAR, cFrustum::RIGHT, cFrustum::NEAR, cFrustum::LEFT } }, { // BOTTOM { 6, 7, 3, 2 }, { cFrustum::NEAR, cFrustum::RIGHT, cFrustum::FAR, cFrustum::LEFT } } }; // Search for faces that belong to visible planes for (int j = 0; j < cFrustum::NB_PLANES; j++) { if (plane_visible[j]) { // Search for an adjacent face that is non-visible, representing a terminator edge Face& face = faces[j]; for (int k = 0; k < 4; k++) { // Add a plane between the terminator edge and light position int fi = face.f[k]; if (!plane_visible[fi]) planes.push_back(cPlane(light_pos, vertices[face.v[(k + 1) & 3]], vertices[face.v[k]])); } } } I wrote this about 3 years ago when stencil shadows were all the rage - please don't blame me for that naughty vector allocation on the stack :) |
||||
|
||||
![]() Anonymous Poster |
||||
|
||||
| Even though I agree with previous posters I would also like to add another point. Why is it that it seems so hard to provide at least a very minimum of source code? Nothing beats hands on experience in my book, but that is just my point of view. Otherwise good job. |
||||
|
||||
![]() biki_ Member since: 2/25/2005 |
||||
|
|
||||
| here's a small idea that might simplify that algorithm further. if 2 planes P1 and P2 intersect along line L and we have given point Q we can make a plane that also intersects along L and goes through Q without calculating edge at all. here's what we do. 1. we calculate distances of Q to the planes P1 , P2 t1=P1.Distance(Q); t2=P2.Distance(Q); 2. we calculate new plane as: Pa=P1*t2-P2*t1; or Pb=P2*t1-P1*t2 depending on desired normal vector we choose Pa or Pb depending on which of the planes was back facing to light. ps. please correct me if i'm wrong. UPDATE! i just have tested this idea and it works perfectly, whole problem solved in just a few lines.. here's my silly little code.
int edges[12][2]={{0,2},{0,3},{0,4},{0,5},{1,2},{1,3},{1,4},{1,5},{2,4},{2,5},{3,4},{3,5}};
int MakeShadowCasterVolume (plane *out,plane *frustum,vector3f &light) {
int visible[6];
float distance[6];
int p1,p2,planes,i;
planes=0;
for (i=0;i<6;i++) {
distance[i]=frustum[i].Distance(light);
if (distance[i]>0) {
visible[i]=1; out[planes++]=frustum[i];
} else visible[i]=0;
}
for (i=0;i<12;i++) {
p1=edges[i][0]; p2=edges[i][1];
if (visible[p1]+visible[p2]==1) {
if (visible[p2]==1) {
out[planes++].Combine(frustum[p1],distance[p2],frustum[p2],-distance[p1]);
} else {
out[planes++].Combine(frustum[p2],distance[p1],frustum[p1],-distance[p2]);
}
}
}
return planes;
}
[Edited by - biki_ on November 29, 2006 6:13:04 AM] |
||||
|
||||
![]() Luke Hutchinson Member since: 4/16/2003 From: Sydney, Australia |
||||
|
|
||||
| Gday Guys, Sorry for the late reply. Only just noticed the article got posted. SnprBoB86, using shadow cones is a good suggestion. Guess it is probably application specific as to which approach is more appropriate. Note that the cone-sphere intersection test can be simplified for this case, as it is safe to disregard the check against the complimentary cone. Instead you can simply check that the frustum's bounding sphere intersects in front of the potential shadow caster. A simple plane that touches the shadow caster at the point closest to the lightsource, and with a normal in the direction of the light would do the trick. Never-the-less, setting up the shadow cones is a per object cost, rather than a per lighting pass cost. This per object cost is small if the objects bounding sphere is used, but that is introducing even more error. As the number of objects increases in the scene, i would think that the shadow cones would become more expensive. Partial results from both approaches can be cached across multiple frames, but i would guess more can be cached for the scvs. The frustum vertices and edges will be static, unless using portals. Also, as the size of the frustum increases, using its bounding sphere will introduce quite large errors (as in more work than necissary for rendering). To be fair tho, the scv approach also starts to lose its benefits as the frustum size increases (as the frustum increases, the scv approaches the frustum). As to wether doing more work on the gpu or on the cpu(s) is better, again, _very_ application specific. Wolf, your right, while i do mention shadow maps in the article, scvs are really more appropriate for stencil shadows. But as to wether or not stencil shadows are still relevant, i disagree :-) The memory required for large render targets can be a problem with shadow mapping, esp on consoles. Also, multicore/multiprocessor systems are becoming more common, and this can help balance alot of the workload back to the cpus. Think spus doing silhoutte determination :-) I think the jury is still out on this one. Neurovore, yep, when you know ahead of time which vertices belong to which edges and which planes, it does simply nicely. And often this information is known. Iterating over the edges of planes that are kept, rather than iterating of edges, should be roughly the same. Each edge would be iterated over twice, and in general youd expect that 50% of the planes would be kept. As for using a vector, hey, there's stl all through my listings. Yes dynamic memory allocation in this kind of code is a sin, but my excuse is that the code was for illustrative purposes :-) AP, what about the listings ? A running executable would have been nice though. Dont really want to release the source for the entire executable though. Not because theres anything top secret, or im a closed source nazi, just that the rest of the code isnt polished enough for release yet (actually been about 18 months since ive touched parts of it!). GameDev.net, is there a way to release an executable with the article ??? Biki_, isnt that the same thing? If you know the points P1 and P2, you know the edge ? As Neurovore also pointed out, alot of the setup can be skipped when the edges are known. But as i mentioned above, the edges and vertices of the frustum are static except in the case where extra portal clipping planes are added. Thanks guys for reading the article, and taking time to post your comments and suggestions. Much appreciated ! |
||||
|
||||
![]() biki_ Member since: 2/25/2005 |
||||
|
|
||||
| well almost the same. except mine does not require any knowledge about frustum vertices and it operates on extracted frustum planes only. |
||||
|
||||
![]() Luke Hutchinson Member since: 4/16/2003 From: Sydney, Australia |
||||
|
|
||||
Quote: Hey, i obviously didnt read ur post closely enough, sorry getting kinda late at night :-) Thats a massive simplification! If you only want to check planes that do intersect on the frustum's surface, then you get back to the problem of determining the edges. But really there is no need, a plane can be added for any pair of non-parallel planes where one is back facing and the other forward facing. This does mean that there will be a few extra planes used in the intersection test with the potential shadow casters, but the intersection test result will still be the same. |
||||
|
||||
![]() Luke Hutchinson Member since: 4/16/2003 From: Sydney, Australia |
||||
|
|
||||
| Though if supporting directional lightsources is required, you still need the frustum's vertices, as unlike the case of a point lightsource (or spot-light), you don't have a any other point that is known to lie on the plane. |
||||
|
||||
![]() biki_ Member since: 2/25/2005 |
||||
|
|
||||
| well.. you can always use homogenous coordinates for all calculations. and it will handle light sources at infinity. |
||||
|
||||
![]() biki_ Member since: 2/25/2005 |
||||
|
|
||||
| just did small test with homogenous vectors.. seems to be working perfectly. |
||||
|
||||
![]() Luke Hutchinson Member since: 4/16/2003 From: Sydney, Australia |
||||
|
|
||||
| Thats very cool. Your right Biki_, using homogenous coordinates, with the light direction as the x, y and z components, and 0 as the w component works for directional lights. So the calculation of the scv can be done simpler than in my article. Which is really good to know. For calculating a scv, would recommend Biki_'s improvements. Hopefully people will still find the info on LUP, and calculating the vertices/edges of convex polyhedra useful for something else :-) |
||||
|
||||
![]() Defrag Member since: 8/15/2006 From: Arbroath, United Kingdom |
||||
|
|
||||
| Just thought I'd revive this rather than start a new thread. I'm using biki_'s method and, up 'til this point I understand what's going on. Quote: Quote: I'm not quite sure what that's meant to mean. What does this function actually do and how does it do it? I understand two planes & distances from the respective planes are being used to generate a third plane, but I'm not sure how it's being done. Can anyone shed any light on this? Cheers. |
||||
|
||||
![]() biki_ Member since: 2/25/2005 |
||||
|
|
||||
| oh that's just linear combination of 2 planes result[0]=plane1[0]*value1+plane2[0]*value2; result[1]=plane1[1]*value1+plane2[1]*value2; result[2]=plane1[2]*value1+plane2[2]*value2; result[3]=plane1[3]*value1+plane2[3]*value2; |
||||
|
||||
All times are ET (US)![]() |
Last Thread Next Thread ![]() |
|