Back Face Culling


It seems that there is quite a bit of confusion about back face culling (bfc), so let's see if we can make things a bit clearer for everyone.

Back face culling is the term used to mean the task of removing polygons in a 3D scene that are facing opposite to the camera. Thus these polygons are not visible and should be removed. There are a few algorithms to achieve this, but there is only one that is actually used in any serious engine.

It's based on two elements, the camera position and the normal of the polygon that we want to try to cull.

There are two approaches to this, but they are nearly the same, based on the distance from the camera position and the plane defined by that normal.

n = normal of the face
P = camera position

D = nx*px+ny*py+nz*pz

That is actually the plane equation, but it's the dot product operation too.

Performing the bfc consists of just calculating the distance from the camera point and checking if this distance is positive or negative. If it is negative the face is looking opposite of the camera and should be culled.

So, the two methods:

You can precalc the distance of the planes respect the origin by taking a point of the plane (or a vertex of the polygon we want to cull) and storing that distance in the plane structure. Then, in the bfc function, we just perform a dot product between the plane normal and the camera position and you compare this new distance with the one precalculated. If the new distance is bigger than the one precalculated the face is culled.

Problem with this algorithm is that you have to precalculate those values, which is not very elegant and it wastes a bit of memory. But anyway, it is fast and works nicely.

The other method is mostly the same, but you don't need to precalculate any distances. You just get a new point,

np = (cx - vx , cy - vy, cz - vz) (c = camera, v = some vertex of the face)

for every face, and you compute the Dot product between this new point and the normal of the face. The D you get is less than 0 if the face has to be culled.

Back Face Culling in object space

So this is the bfc algorithm, but what is good with it is that it can be used before the objects are transformed, which saves a lot of calculation.

To compute the bfc over faces that have not been transformed yet you need to know where the camera is relative to those faces.

You probably have a transformation matrix with the concatenation of all the transformations to get the object from its local coordinate system to the camera space, just before projection. So, what you do is, invert that matrix and transform it with the camera position. The camera position will now be in the local space of the object, you can now perform the bfc with the method described above. With this technique you will save about 50% of the mults and adds that you normally need to transform an object to camera space, so don't be lazy to implement this beauty, just get some good algebra book and implement a matrix inversion algo. There are a lot of free sources with matrix inversion algorithms too.


Some credits and tnx fly to:

                                 \           /
                                  \         /
                                   \   |   /
                               -->> |Kalms| <<--
                                   /       \
                                  /         \
                                 /           \



Here are two examples of the two algorithms described above:

 // It's supposed that face_vis array is initialised with 1
 // obj->dist is the structure with the precalculated planes distances to
 // origin

 void Back_face_culling_1 (camera_3d *cam,single_obj  *obj)
   int     i;

   for (i=0;i<obj->nfaces;i++)
     if (Dot_product(&cam->position,&obj->normals[i]) < obj->dist[i])
       obj->face_vis[i] = 0;

 void Back_face_culling_2 (camera_3d *cam,single_obj  *obj)
   vector  aux;
   int     i,ind;

   for (i=0;i<obj->nfaces;i++)
     ind = obj->faces[i].A;
     vector_sub (&cam->position,&obj->vertex[ind],&aux);

     if (Dot_product(&aux,&obj->normals[i]) < 0)
       obj->face_vis[i] = 0;

- Sarwaz/MRY