Backface Culling

Backface Culling

Backface culling was alluded to a couple of times in the last chapter, and now you are going to find out what it is all about and exactly how it works.

Remember that in the earlier models, you made all the fills semitransparent. The reason for this was that you were always drawing every polygon, and you had no control over what order they were drawn in. So a polygon on the back of the model might get drawn on top of one on the front of the model, creating some odd results. Giving them all an alpha value of 50% made them all relatively equal and let me put off this discussion while you concentrated on your modeling techniques. Now you are going to deal with it.

In principle, backface culling is pretty simple. You draw the polygons that are facing you, and you dont draw the ones that are facing away from you. The tricky part is determining which are which.

You should also remember that I was constantly reminding you to define the points of each polygon in a clockwise direction. Even though that is completely unnecessary for what youve been doing so far, youll now see why this was so important, and why it was good to get into that habit from the start.

Its a neat little observation that if the points of a polygon are arranged in a clockwise fashion when that triangle is facing you, they will be counterclockwise when that polygon is facing away from you. You can see this demonstrated in Figure 17-1, which has a triangle facing you. (Since all the polygons you will be using will be triangles , I will occasionally mix the two terms. For the most part, Ill use polygon as a general term , and triangle to discuss a specific triangular polygon under discussion.)

image from book
Figure 17-1: A triangle facing you has points in a clockwise direction.

And in Figure 17-2, Ive rotated the triangle so it is facing in the opposite direction.

image from book
Figure 17-2: A triangle facing away from you has points in a counterclockwise direction.

Now you see that points go around in a counterclockwise direction.

There are a few points of clarification to make here. First of all, what do I mean when I say a polygon is facing you? I mean that the exterior side of the polygon is facing you. Although its not obvious when I show a single triangle, remember that Im talking about 3D solids here. In that case, each polygon has one exterior side and one interior side.

Another point is that when determining clockwise or counterclockwise, Im talking about the screen positions of the points. Not the 3D x, y, and z positions , but the xPos , yPos position that is determined by applying perspective.

Finally, it should be noted that you could reverse the setup and make a system where counterclockwise polygons were the facing ones, and clockwise ones faced away. Either way works as long as you are consistent.

So again, we get back to the question, how do you determine if three points are arranged in a clockwise or counterclockwise direction? Think about it for a while. Its such an easy thing for your eyes to pick out, but when it comes down to putting it in code, it suddenly seems like a very abstract concept.

As I mentioned, the solution Im going to give you here is based on the one given in Macromedia Flash MX Studio . But just to make sure I was providing the best method possible, I decided to see if I could come up with my own function to distinguish clockwise from counterclockwise. While I did manage to put something together that worked perfectly , it was twice as long as the existing solution and far more complex, involving coordinate rotation and lots of trig. Since Im generally a nice guy, I decided to give you the simple one. And dont worry, its just as accurate as my overly complex one!

What youre going to do is make a function called isBackFace . This will take three point objects as parameters, and return true if they are counterclockwise and false if they are clockwise. Here is that function:

 function isBackFace(pointA:Object, pointB:Object, pointC:Object):Boolean {       // see http://www.jurjans.lv/flash/shape.html       var cax:Number = pointC.xPos - pointA.xPos;       var cay:Number = pointC.yPos - pointA.yPos;       var bcx:Number = pointB.xPos - pointC.xPos;       var bcy:Number = pointB.yPos - pointC.yPos;       return cax * bcy > cay * bcx;       {             return true;       }       return false; } 

You see the link to the website, http://www.jurjans.lv/flash/shape.html. Todd Yard credited this site in Macromedia Flash MX Studio , so Ill pass it on. In addition to giving credit where it is due, the site has some excellent reference material and tutorials on many similar subjects.

For a quick explanation, the function calculates the lengths of two sides of the triangle, and with some sleight-of-hand multiplication and comparison, is able to tell which direction they are going. If you are interested in the specifics of why this works, see the site just mentioned, or do a search for backface culling. Im sure youll find plenty of reading material. For now, though, Im going to just say it is simple, quick, efficient, and 100% workable , and leave it at that!

So, how do you use this function? Youll call it in the renderTriangle function to see whether or not you actually do want to render said triangle or not.

 function renderTriangle(tri:Object):Void {       var pointA:Object = points[tri.a];       var pointB:Object = points[tri.b];       var pointC:Object = points[tri.c];       if(isBackFace(pointA, pointB, pointC))       {             return;       }       beginFill(tri.col, 100);       moveTo(pointA.xPos, pointA.yPos);       lineTo(pointB.xPos, pointB.yPos);       lineTo(pointC.xPos, pointC.yPos);       lineTo(pointA.xPos, pointA.yPos);       endFill(); } 

Here you grab references to each of the three points and store them in pointA , pointB , and pointC . This helps to keep things clear. You then pass these three points to the isBackFace function, which returns true or false . If it returns true , you know the triangle is facing away from you and you dont need to render it. So at that point you call return , which exits the function. If isBackFace returns false , you need to render the triangle; go right ahead and do so. Notice also that the beginFill call is now using 100% alpha to make fully opaque fills rather than transparent ones.

As a reinforcement, here is the code for ch17_01.fla in its entirety, with the new parts in bold:

 var points:Array = new Array(); var triangles:Array = new Array(); var fl:Number = 250; var vpX:Number = Stage.width / 2; var vpY:Number = Stage.height / 2; var zOffset:Number = 400; init(); function init() {       points[0] =  {x: -50, y:-250, z:-50};       points[1] =  {x:  50, y:-250, z:-50};       points[2] =  {x: 200, y: 250, z:-50};       points[3] =  {x: 100, y: 250, z:-50};       points[4] =  {x:  50, y: 100, z:-50};       points[5] =  {x: -50, y: 100, z:-50};       points[6] =  {x:-100, y: 250, z:-50};       points[7] =  {x:-200, y: 250, z:-50};       points[8] =  {x:   0, y:-150, z:-50};       points[9] =  {x:  50, y:   0, z:-50};       points[10] = {x: -50, y:   0, z:-50};       points[11] = {x: -50, y:-250, z: 50};       points[12] = {x:  50, y:-250, z: 50};       points[13] = {x: 200, y: 250, z: 50};       points[14] = {x: 100, y: 250, z: 50};       points[15] = {x:  50, y: 100, z: 50};       points[16] = {x: -50, y: 100, z: 50};       points[17] = {x:-100, y: 250, z: 50};       points[18] = {x:-200, y: 250, z: 50};       points[19] = {x:   0, y:-150, z: 50};       points[20] = {x:  50, y:   0, z: 50};       points[21] = {x: -50, y:   0, z: 50};       triangles[0] =  {a:0, b:1,  c:8,  col:0x6666cc};       triangles[1] =  {a:1, b:9,  c:8,  col:0x6666cc};       triangles[2] =  {a:1, b:2,  c:9,  col:0x6666cc};       triangles[3] =  {a:2, b:4,  c:9,  col:0x6666cc};       triangles[4] =  {a:2, b:3,  c:4,  col:0x6666cc};       triangles[5] =  {a:4, b:5,  c:9,  col:0x6666cc};       triangles[6] =  {a:9, b:5,  c:10, col:0x6666cc};       triangles[7] =  {a:5, b:6,  c:7,  col:0x6666cc};       triangles[8] =  {a:5, b:7,  c:10, col:0x6666cc};       triangles[9] =  {a:0, b:10, c:7,  col:0x6666cc};       triangles[10] = {a:0, b:8,  c:10, col:0x6666cc};       triangles[11] = {a:11, b:19,  c:12,  col:0xcc6666};       triangles[12] = {a:12, b:19,  c:20,  col:0xcc6666};       triangles[13] = {a:12, b:20,  c:13,  col:0xcc6666};       triangles[14] = {a:13, b:20,  c:15,  col:0xcc6666};       triangles[15] = {a:13, b:15,  c:14,  col:0xcc6666};       triangles[16] = {a:15, b:20,  c:16,  col:0xcc6666};       triangles[17] = {a:20, b:21,  c:16,  col:0xcc6666};       triangles[18] = {a:16, b:18,  c:17,  col:0xcc6666};       triangles[19] = {a:16, b:21,  c:18,  col:0xcc6666};       triangles[20] = {a:11, b:18,  c:21,  col:0xcc6666};       triangles[21] = {a:11, b:21,  c:19,  col:0xcc6666};       triangles[22] = {a:0,  b:11, c:1,  col:0xcccc66};       triangles[23] = {a:11, b:12, c:1,  col:0xcccc66};       triangles[24] = {a:1,  b:12, c:2,  col:0xcccc66};       triangles[25] = {a:12, b:13, c:2,  col:0xcccc66};       triangles[26] = {a:3,  b:2,  c:14, col:0xcccc66};       triangles[27] = {a:2,  b:13, c:14, col:0xcccc66};       triangles[28] = {a:4,  b:3,  c:15, col:0xcccc66};       triangles[29] = {a:3,  b:14, c:15, col:0xcccc66};       triangles[30] = {a:5,  b:4,  c:16, col:0xcccc66};       triangles[31] = {a:4,  b:15, c:16, col:0xcccc66};       triangles[32] = {a:6,  b:5,  c:17, col:0xcccc66};       triangles[33] = {a:5,  b:16, c:17, col:0xcccc66};       triangles[34] = {a:7,  b:6,  c:18, col:0xcccc66};       triangles[35] = {a:6,  b:17, c:18, col:0xcccc66};       triangles[36] = {a:0,  b:7,  c:11, col:0xcccc66};       triangles[37] = {a:7,  b:18, c:11, col:0xcccc66};       triangles[38] = {a:8,  b:9,  c:19, col:0xcccc66};       triangles[39] = {a:9,  b:20, c:19, col:0xcccc66};       triangles[40] = {a:9,  b:10, c:20, col:0xcccc66};       triangles[41] = {a:10, b:21, c:20, col:0xcccc66};       triangles[42] = {a:10, b:8,  c:21, col:0xcccc66};       triangles[43] 2= {a:8,  b:19, c:21, col:0xcccc66}; } function onEnterFrame():Void {       var numPoints:Number = points.length;       for (var i:Number=0;i<numPoints;i++) {             var point:MovieClip = points[i];             var angleY:Number = (_xmouse - vpX) * .001;             var cosY:Number = Math.cos(angleY);             var sinY:Number = Math.sin(angleY);             var angleX:Number = (_ymouse - vpY) * .001;             var cosX:Number = Math.cos(angleX);             var sinX:Number = Math.sin(angleX);             var x1:Number = point.x * cosY - point.z * sinY;             var z1:Number = point.z * cosY + point.x * sinY;             var y1:Number = point.y * cosX - z1 * sinX;             var z2:Number = z1 * cosX + point.y * sinX;             point.x = x1;             point.y = y1;             point.z = z2;             var scale:Number = fl / (fl + point.z + zOffset);             point.xPos = vpX + point.x * scale;             point.yPos = vpY + point.y * scale;       }       clear();       var numTriangles:Number = triangles.length;       for(var i:Number =0;i<numTriangles;i++)       {             renderTriangle(triangles[i]);       } }  function renderTriangle(tri:Object):Void   {   var pointA:Object = points[tri.a];   var pointB:Object = points[tri.b];   var pointC:Object = points[tri.c];   if(isBackFace(pointA, pointB, pointC))   {   return;   }   beginFill(tri.col, 100);   moveTo(pointA.xPos, pointA.yPos);   lineTo(pointB.xPos, pointB.yPos);   lineTo(pointC.xPos, pointC.yPos);   lineTo(pointA.xPos, pointA.yPos);   endFill();   }   function isBackFace(pointA:Object, pointB:Object, pointC:Object):Boolean   {   // see http://www.jurjans.lv/flash/shape.html   var cax:Number = pointC.xPos - pointA.xPos;   var cay:Number = pointC.yPos - pointA.yPos;   var bcx:Number = pointB.xPos - pointC.xPos;   var bcy:Number = pointB.yPos - pointC.yPos;   return cax * bcy > cay * bcx;   }  

When you run that file, youll get something that looks like what appears in Figure 17-3.

image from book
Figure 17-3: Backface culling in action

As you can see, you are a far cry better off than without the backface culling, and if you take a close look, youll see that only the surfaces that are facing you are being rendered. But it still needs work. The problem now is that some of the faces that should be in back of others are showing up in front of them. If the term depth sorting comes to mind, you are truly on the ball. That is our next subject.



Foundation ActionScript. Animation. Making Things Move
Foundation Actionscript 3.0 Animation: Making Things Move!
ISBN: 1590597915
EAN: 2147483647
Year: 2005
Pages: 137
Authors: Keith Peters

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net