|
|
|
Back-face
Culling Culling This means if you can do something with maths to get the amount of triangles you need to draw closer to the amount actually visible, it will significantly improve your frame rates. One of the worst culprits is triangles which fall within the confines of the screen after they have been through your 3D pipeline, but are facing away from the viewer/camera. These triangles are almost as slow to draw as visible triangles. Fortunately there is a way to find out if a triangle is facing away from the viewer, and remove it from your draw list. This process is called Back-face Culling. Hidden surface removal is a pretty large subject and back-face culling is only a small part of it. Frustum culling will be covered in a later tutorial. Vectors float V[3]; to describe a vector. V[0] represents the x component, V[1] the y component and V[2] the z component. Normals With triangles there are two types of normal, depending on which kind of shading you are using. There are face normals and vertex normals. Face normals are used to determine visibility. A face normal tells us which direction a face (or surface) is facing. Both face normals and vertex normals are used for directional lighting, face normals with flat shading and vertex normals with gouraud shading. At the moment we are only interested in face normals. A face normal
doesn't really have a position, but you can imagine it sitting in the
centre of a triangle pointing out at a right angle to the surface of the
triangle, as shown in Figure 4.1 below.
In reality however, since we are only interested in the direction the face normal points, and not it's actual position in 3D space, our normals will all be specified as a direction from the origin (0,0,0). It's important when defining your 3D objects, that you specify your vertices in a consistent order, either always clockwise or always anti-clockwise. This is because the face-normal determines which way a face is pointing, and face normals are generated using your vertices. So if you specify your vertices in the wrong order, the triangle will face the wrong way. Calculating
Normals Another useful property of vectors is that we can subtract them by subtracting the individual x,y and z values. If we take the vector from the origin to one point of our triangle, and then subtract the vector from the origin to another point of our triangle, this gives us the vector between the two points. And, of course, the vector between two points of our triangle must be on the plane of the triangle. The vector from the origin to one point of our triangle is actually just identical to the x,y,z coordinates of that point, since the origin is at (0,0,0). So if we have the vector- float v1[3]; and let's say we are using this struct for storing a vertex of a triangle- struct
vertex and this struct for representing a triangle- struct
triangle then to calculate a vector from one point of our triangle to another point (in this circumstance from point 1 to point 0) we do- //
Calculate Vector from point 1 to 0 So we are just basically subtracting the x,y,z coordinates for point 1 from those of point 0. Now we have a vector v1[3] that lies on the plane of our triangle and points the direction from point 1 to point 0. We now need another vector on the plane of our triangle, this time we'll go from point 2 to point 1, so- float v2 [3];
// Calculate Vector from point 2 to 1 Normalizing
Vectors //
Compute magnitude of both Vectors
// Normalize both Vectors Pythagorus theorem in 3D takes the distance between two points in x, y and z and then calculates the 3D distance, but since the magnitude of a vector is taken from the origin there isn't a great deal of point in writing ((v1[0] - 0.0f) * (v1[0] - 0.0f) ) as I'm sure you will agree, so we can just use ( v1[0] * v1 [0] ). So, we've now normalized our two vectors. All that remains is to compute the cross product. The formula for the cross product, also known as vector product is- a x b = ( ay*bz - az*by ) + ( az*bx - ax*bz ) + ( ax*by - ay*bx ) where ay represents the y component of vector a. We can then split this into the three x,y and z components like this (in code)- result
[0] = ( a[1] * b[2] ) - ( a[2] * b[1] ); so the actual code with the variables we've been using is- //
Compute Cross Product to give surface normal So that's how to compute a face normal for a triangle, but before we get to how to determine visibility using it, I'm briefly going to discuss the general approach to using normals. Using Normals The best approach is to calculate all your face normals when your program first runs, then store them and rotate them every frame. The reason to keep the originals is that if you kept processing the resultant normals every frame, the errors would soon build to the point where the normals were useless. Avoid this by starting with the original data at the beginning of each frame. Computing Visibility a.b = ( ax * bx ) + ( ay * by ) + ( az * bz ) So a and b are vectors and the dot (.) denotes that we are calculating the dot product. ax denotes the x component of the vector a. You've probably figured out that one of the two vectors above is the surface normal. The other vector is the direction from the camera to the triangle in question. To calculate this we use the cameras position and any point on our triangle, the only difficulty is that the camera and the triangle have to be in the same space. That means we can either use both the triangle and camera position in global space, or the triangle and camera position in camera (view) space. It's likely that you will never have the triangles coordinates in global space, since you will probably find it easier to transform all your triangles from local to global to camera space using a single matrix, as I do. Hence you may just as well use camera space. The cameras position in camera space is, funnily enough, (0,0,0) since camera space is relative to the camera, so to compute a vector from the camera to our triangle we do this-
float v [3]; where cs[0].x is the x coordinate of point 0 of our triangle in camera space. Subtracting the camera coordinates isn't necessary because the camera lies at (0,0,0). So we now have our normal, rotated into camera space, and a vector from the camera to our triangle. The dot product can now be calculated to give us the cosine of the angle between the two vectors. We don't have to worry about converting from the cosine of the angle, to the actual angle, since we only need to know whether the triangle is facing away or not. Hence if the angle is either less than 90 degrees or more than 270 degrees, then the triangle is facing away (since the vector from the camera to the triangle is also facing away from the camera), and the cosine of the angle will be positive. So all we need to do is compute the dot product and cull triangles where the result is positive, like so- //
Calculate cosine of angle between two vectors
// Set triangle to be drawn by default
// Check if triangle is facing away from viewer where cs_nx is the x component of the face normal once in camera space, and bIsVisible is a bool for storing visibility of the triangle. Conclusion |
|
All
content copyright © Simon Brown 1999-2005. |