Animation in actionscript3.0 is almost finished. continue today: Last flash/flex study note (50): 3D line and fill, we know that a certain surface on any 3D polygon can be divided into multiple triangles. RatioCubeAs an example, each side is composed of two triangles, but there is obviously a problem in that example: whether or not a plane of a cube should be visible to the human eye (for example, to the back of the cube, it should be invisible), this side is drawn.
In this article, I will take you to learn how to delete the back (that is, invisible ".
Make some preparations: each side of the cube has an "outside" and "inside ". The outside refers to the outward side of the observer, which refers to the inside of the cube. In 3D programming, we usually refer to "outside"
For example, this is the front of the cube and is divided into 0-1-2 and 0-2-3 triangles (note that the order of the three vertices is "Clockwise ), when the Cube's "Front" rotates to "back", the order of triangle vertices changes from "Clockwise" to "Clockwise ".
Implication: If we can determine that the vertex order of a triangle is "counter-clockwise", the Triangle must be on the back and should be hidden or not drawn.
Therefore, if we follow the "Clockwise law of triangle vertices" above when building the triangle on each side of the cube, the above solution should be able to meet the requirements, review the construction of cube Triangle ArrayCode:
// Triangles [0] = New Triangle (points [0], points [1], points [2], 0x6666cc ); triangles [1] = New Triangle (points [0], points [2], points [3], 0x6666cc ); // triangles [2] = New Triangle (points [0], points [5], points [1], 0x66cc66 ); triangles [3] = New Triangle (points [0], points [4], points [5], 0x66cc66 ); // triangles [4] = New Triangle (points [4], points [6], points [5], 0xcc6666 ); triangles [5] = New Triangle (points [4], points [7], points [6], 0xcc6666 ); // bottom triangles [6] = New Triangle (points [3], points [2], points [6], 0xcc66cc ); triangles [7] = New Triangle (points [3], points [6], points [7], 0xcc66cc ); // right triangles [8] = New Triangle (points [1], points [5], points [6], 0x66cccc ); triangles [9] = New Triangle (points [1], points [6], points [2], 0x66cccc ); // left triangles [10] = New Triangle (points [4], points [0], points [3], 0xcccccc66 ); triangles [11] = New Triangle (points [4], points [3], points [7], 0xcccc66 );
We suggest you buy a three-dimensional cube toy. Each vertex is marked with a pen based on the vertex number in the previous article. Comparing the above Code, we found that such code exactly complies with this rule, of course, the Code does not have to be exactly the same as this, for example:
// Triangles [0] = New Triangle (points [0], points [1], points [2], 0x6666cc ); triangles [1] = New Triangle (points [0], points [2], points [3], 0x6666cc );
You can also write it as follows:
Triangles [0] = New Triangle (points [1], points [2], points [0], 0x6666cc); triangles [1] = New Triangle (points [0], points [2], points [3], 0x6666cc );
Or
Triangles [0] = New Triangle (points [1], points [2], points [3], 0x6666cc); triangles [1] = New Triangle (points [1], points [3], points [0], 0x6666cc );
As long as the clockwise rule is satisfied, you can. OK, half of the success has been made. How can we determine if the triangle is on the back?
// Determine whether the private function isbackface (): Boolean {var CAX: Number = pointc. screenx-pointa. screenx; var Cay: Number = pointc. screeny-pointa. screeny; var BCX: Number = pointb. screenx-pointc. screenx; var bcy: Number = pointb. screeny-pointc. screeny; return CAX * bcy> Cay * BCX ;}
Add this private method to triangle. CS (I don't know how to do it. Anyway, this function does work, and it should be memorized by the formula .)
The last small problem: during the rotation, the three vertices of the triangle, "Z axis depth" (zpos value), are changing, it is possible that the vertex of a triangle blocks the vertex of another triangle. So we have to solve the issue of triangle z-axis sorting. Here there is a rule that we can consider the top zpos value closest to the observer among the three vertices as the z-axis depth of the triangle, so triangle. the Z axis attribute "depth" must be added to CS to triangle. the CS content is as follows:
Package {import flash. display. graphics; public class triangle {private var pointa: point3d; private var pointb: point3d; private var pointc: point3d; private var color: uint; Public Function triangle (A: point3d, B: point3d, C: point3d, color: uint) {pointa = A; pointb = B; pointc = C; this. color = color;} public function draw (G: Graphics): void {// if it is the back, if (isbackface () {return;} G. beginfill (color); G. moveTo (pointa. screenx, pointa. screeny); G. lineto (pointb. screenx, pointb. screeny); G. lineto (pointc. screenx, pointc. screeny); G. lineto (pointa. screenx, pointa. screeny); G. endfill ();} // determine whether the back is Private function isbackface (): Boolean {// see http://www.jurjans.lv/flash/shape.htmlvar CAX: Number = pointc. screenx-pointa. screenx; var Cay: Number = pointc. screeny-pointa. screeny; var BCX: Number = pointb. screenx-pointc. screenx; var bcy: Number = pointb. screeny-pointc. screeny; return CAX * bcy> Cay * BCX;} // obtain the zaxis depth of the triangle (whichever is closest to the observer among the three vertices) Public Function get depth (): number {var zpos: Number = math. min (pointa. z, pointb. z); zpos = math. min (zpos, pointc. z); Return zpos ;}}}
After a bunch of tricks, the exciting moment finally came. In the original cube sample code, you only need to add a line of code:
Function enterframehandler (E: Event): void {var DX: Number = mousex-vpx; var DY: Number = mousey-vpy; var anglex: Number = Dy * 0.001; var angley: number = DX * 0.001; var anglez: Number = math. SQRT (dx * dx + dy * Dy) * 0.0005; If (dx> 0) {anglez * =-1 ;}for (var I: uint = 0; I <pointnum; I ++) {var point: point3d = points [I]; point. rotatex (anglex); point. rotatey (angley); point. rotatez (anglez);} triangles. sorton ("depth", array. descending | array. numeric); // Add the z-axis order Graphics of the triangle array. clear (); for (I = 0; I <triangles. length; I ++) {triangles [I]. draw (graphics );}}
Compile and run the program to get a k swf animation and a 3D cube with mouse interaction. Cool!
After modifying other examples, the effect is as follows:
3D light:
This part is hard to understand (requires a certain linear algebra basis), first and finally (The position of the light source is at the left vertex, Z axis "-100"-that is, the top left vertex of the Flash animation is 100 vertical from the screen, which requires a bit of imagination)
Sorry, it's a bit painful to recall what the math taught us !)
For example, a triangle must first determine its "normal" Vector norm, which is the cross product of vector AB and vector BC. Then the light source itself is also a vector, and the vector light and the Vector norm form an angle θ, and the value range of θ is 0 ~ Between Pi (that is, 180 degrees), when θ is 180 degrees, it is direct positive, when θ is 0 degrees, it is the back of the radiation (actually less than or equal to 90 degrees, if the surface color of the triangle is not displayed, the plane color of the triangle is displayed normally (the brightest). When the surface or the surface is not displayed, the color should be dimmed and close to black.
For this conclusion, let's take a look at the following Demonstration: (The position of the light source is set to the animation center, PX from the screen, that is, it is directed to the center of the screen)
First, define the light Vector class:
Package {public class light {public var X: Number; Public var y: Number; Public var Z: Number; private VaR _ brightness: Number; public function light (X: number =-200, Y: Number =-200, Z: Number =-200, brightness: Number = 1) {// space coordinate of the Light vector this. X = x; this. y = y; this. z = z; // brightness this. brightness = brightness;} public function set brightness (B: Number): void {// The brightness value is usually between 0 and 1 _ brightness = math. max (B, 0); _ brightness = math. min (_ brightness, 1);} public function get brightness (): number {return _ brightness ;}}}
So what if we calculate the vector's yarn and angle? First, give a mathematical formula:
Cross Product formula:
Angle Formula
Point product (also known as the number product or Inner Product) Formula
OK. The theoretical knowledge is almost ready. Next we will transform the triangle base class:
Package {import flash. display. graphics; public class triangle {private var pointa: point3d; private var pointb: point3d; private var pointc: point3d; private var color: uint; Public var light: light; // Add a light source lightpublic function triangle (A: point3d, B: point3d, C: point3d, color: uint) to each triangle {pointa = A; pointb = B; pointc = C; this. color = color;} public function draw (G: Graphics): void {If (isbackface () {return;} G. beginfill (getadjustedcolor ());//★★★Here, the color G is dynamically filled according to the light angle. moveTo (pointa. screenx, pointa. screeny); G. lineto (pointb. screenx, pointb. screeny); G. lineto (pointc. screenx, pointc. screeny); G. lineto (pointa. screenx, pointa. screeny); G. endfill ();}//★★★Private function getadjustedcolor (): uint {// retrieve the red, green, and blue components var RED: Number = color> 16; var Green: number = color> 8 & 0xff; var Blue: Number = color & 0xff; var lightfactor: Number = getlightfactor ();//★★★Adjust the factor according to the color of the light !!! This is the key! Red * = lightfactor; green * = lightfactor; blue * = lightfactor; return red <16 | green <8 | blue ;} // adjust the factor based on the color obtained by the light (the most difficult function to understand) Private function getlightfactor (): number {var AB: Object = new object (); AB. X = pointa. x-pointb. x; AB. y = pointa. y-pointb. y; AB. z = pointa. z-pointb. z; var BC: Object = new object (); BC. X = pointb. x-pointc. x; BC. y = pointb. y-pointc. y; BC. z = pointb. z-pointc. z; var norm: Object = new object (); // count algorithm Coordinate value of Vector norm. X = (AB. y * BC. z)-(AB. z * BC. y); norm. y =-(AB. x * BC. z)-(AB. z * BC. x); norm. z = (AB. x * BC. y)-(AB. y * BC. x); // The dot product (quantity product) of the Vector norm and the vector light var dotprod: Number = norm. x * Light. X + norm. y * Light. Y + norm. z * Light. z; // model length var normmag: Number = math. SQRT (norm. x * norm. X + norm. y * norm. Y + norm. z * norm. z); // var lightmag: Number = math. SQRT (light. x * Light. X + light. y * Light. Y + light. z * Light. z); // anglevar angle: Number = math. ACOs (dotprod/(normmag * lightmag); Return (angle/math. pi) * Light. brightness; // in addition to the maximum value Pi, the angle will get a decimal point between 0 and 1, and then multiply the brightness value of light, obtain the final light adjustment factor} // whether it is on the back of Private function isbackface (): Boolean {var CAX: Number = pointc. screenx-pointa. screenx; var Cay: Number = pointc. screeny-pointa. screeny; var BCX: Number = pointb. screenx-pointc. screenx; var bcy: Number = pointb. screeny-pointc. screeny; return CAX * bcy> Cay * BCX;} // Z axis depth public function get depth (): number {var zpos: Number = math. min (pointa. z, pointb. z); zpos = math. min (zpos, pointc. z); Return zpos ;}}}
we can see that almost all the processing work has been completed in triangle. CS. After all this is done, can 3D light be automatically displayed in active paintings? No. We haven't added a light source to the cube yet! However, this is easy. Just change it to the following:
Function Init (): void {// the first four points Points [0] = new point3d (-50,-50,-50); points [1] = new point3d (50, -50,-50); points [2] = new point3d (50,-50); points [3] = new point3d (-50, 50,-50 ); // The last four points: points [4] = new point3d (-50,-50, 50); points [5] = new point3d (50,-50, 50 ); points [6] = new point3d (50, 50); points [7] = new point3d (-50, 50, 50); For (var I: uint = 0; I <pointnum; I ++) {points [I]. setvanishingpoint (vpx, vpy); points [I]. setcenter (0, 0, 50);} // triangles = new array (); VaR _ T: Number = 0xff0000; // triangles [0] = New Triangle (points [1], points [2], points [0], _ t ); triangles [1] = New Triangle (points [0], points [2], points [3], _ T); _ t = 0xff0000 ;; // triangles [4] = New Triangle (points [5], points [4], points [6], _ t ); triangles [5] = New Triangle (points [4], points [7], points [6], _ T); _ t = 0x00ff00 ;; // triangles [2] = New Triangle (points [1], points [0], points [4], _ t) above ); triangles [3] = New Triangle (points [1], points [4], points [5], _ T); _ t = 0x00ff00 ;; // The following triangles [6] = New Triangle (points [3], points [2], points [6], _ t ); triangles [7] = New Triangle (points [3], points [6], points [7], _ T); _ t = 0x0000ff; // triangles [8] = New Triangle (points [2], points [1], points [5], _ t ); triangles [9] = New Triangle (points [2], points [5], points [6], _ T); _ t = 0x0000ff; // triangles [10] = New Triangle (points [4], points [0], points [3], _ t ); triangles [11] = New Triangle (points [4], points [3], points [7], _ T );//★★★Just add the following code and assign the same light source instance to each triangle! VaR light: Light = new light (-275,-200,-150); for (I = 0; I <triangles. length; I ++) {triangles [I]. light = light;} addeventlistener (event. enter_frame, enterframehandler); stage. addeventlistener (keyboardevent. key_down, keydownhandler );}
Note that you only need to assign the same light source instance to each triangle in the triangle array.
After writing this chapter, I am tired. This chapter is indeed difficult. I think of Chairman Mao's classic quote: "learning mathematics and physics is not afraid to go all over the world !"