Back Face Culling idea

Started by
15 comments, last by Stonemonkey 8 years, 10 months ago

dot(modelSpaceView, triangleDirection).

A triangle who's normal is facing away from the camera (i.e where that dot < 0) isn't necessarily back-facing; such triangles can well be visible under a perspective projection.

dot(eyePosInModelSpace-triangleVertex[0], triangleDirection);
or

dot(vec4(eyePosInModelSpace.xyz,1), trianglePlane.xyzw);

XB6GStf.png

Advertisement
I think the arrow in the 2nd picture should be in the opposite direction, it's eyepos-vertexpos, but otherwise a good visualization of the idea smile.png

I think the arrow in the 2nd picture should be in the opposite direction, it's eyepos-vertexpos, but otherwise a good visualization of the idea smile.png

Ah yeah, if the red arrow is flipped then it does work.

I couldn't see how this worked because I was picturing it backwards... laugh.png oops wacko.png

Whaa..? That's not how rasterizers detect triangle orientation. See 'Oriented edges section' from Fab. Giesen. It basically exploits a mathematical property and is nearly free.

I thought GPUs just rasterized the triangle as-is and stopped the row the two endpoints crossed. This means that you get a mechanism to determine the last scanline of a visible triangle and to do backface culling all in one (since culled triangles would end right in their first row, effectively rendering nothing), and without much math in the way.

How long before this turns into a discussion of GPU internals? =P

Don't pay much attention to "the hedgehog" in my nick, it's just because "Sik" was already taken =/ By the way, Sik is pronounced like seek, not like sick.

I thought GPUs just rasterized the triangle as-is and stopped the row the two endpoints crossed. This means that you get a mechanism to determine the last scanline of a visible triangle and to do backface culling all in one (since culled triangles would end right in their first row, effectively rendering nothing), and without much math in the way.

if you rotate a triangle 180degree, it would become invisible with your idea, because right would be left and the left side of it would be right. but your explanation is quite short, sorry if I misunderstand it :/

I didn't make the link between that article which talks about points in the positive triangle area, and direction of front face and back face.

Ooops. Sorry. Right blog, wrong link. See Fill rules.

Edit: The baricentric conspiracy article is still important to understand what's going on. Through the math exposed there, the author arrives to the code:


if (w0 >= 0 && w1 >= 0 && w2 >= 0)
                renderPixel(p, w0, w1, w2); 

To determine whether a point is inside a triangle.

w0, w1 & w2 are calculated using:


int w0 = orient2d(v1, v2, p); //F12
int w1 = orient2d(v2, v0, p); //F20
int w2 = orient2d(v0, v1, p); //F01

He obtains the formula from his baricentric conspiracy math derivation, where he defines F12, F20 & F01 using a counter-clockwise scheme.

If a point is inside a triangle but the vertices are given in clockwise order, then w0, w1 & w2 will all be negative. This is a damn good mathematical property.

Counterclockwise triangles can be checked doing "if (w0 >= 0 && w1 >= 0 && w2 >= 0)"; while clockwise triangles can be checked doing "if (w0 <= 0 && w1 <= 0 && w2 <= 0)"

Another way to switch this, is to alter the definition of w0, w1, & w2 so that you use F21, F02 and F10 instead; then check for all positive for clockwise triangles, and all negative for counterclockwise ones.

In other words, checking whether a triangle is counterclock- or clock-wise is a matter of checking whether all three coefficients are positive or negative (or toggling the math formula to flip this identity). No cross product involved, no dot product involved. No need for a triangle normal at all. In fact, this can be solved using integer arithmetic and no need for floating point!

Since during rendering we only care that triangles are in one order, we don't need to do anything. We just leave "if (w0 >= 0 && w1 >= 0 && w2 >= 0)" and the vertices in the wrong winding order will automatically be left out.

I do something similar to the diagram hodgman posted but with a slight difference. It requires an extra element (triangle->cull) pre calculated and stored in the triangle data which is the distance from the triangle plane to the model origin.
Then when rendering, transform the camera to model space and all you need to do for each triangle is:
If (dot(camera->position,triangle->normal)>triangle->cull) draw_triangle(triangle);

I use this in software rendering and do a few other things to go along with it, triangles on the same plane can be grouped together so only 1 test is required for the group (that can be used other ways too to omit triangles forming openings and depressions into faces) and I have a flag on the vertices so I'm not recalculating the same vertex more than once for the same entity.

This topic is closed to new replies.

Advertisement