# Flash/Flex学习笔记(55)：背面剔除与 3D 灯光

Animation in ActionScript3.0 这本书总算快学完了，今天继续：上一回Flash/Flex学习笔记(50)：3D线条与填 里，我们知道任何一个3D多面体上的某一个面，都可以分解为多个三角形的组合。比立方体为例，每个面都由二个三角形组成，但在那一篇的示例中明显有一个问题：不管立方体的某一个面是不是应该被人眼看见(比如转到背面的部分，应该是看不见的)，这一面都被绘制出来了。

```//前
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);
//底
triangles[6] = new Triangle(points[3], points[2], points[6], 0xcc66cc);
triangles[7] = new Triangle(points[3], points[6], points[7], 0xcc66cc);
//右
triangles[8] = new Triangle(points[1], points[5], points[6], 0x66cccc);
triangles[9] = new Triangle(points[1], points[6], points[2], 0x66cccc);
//左
triangles[10] =new Triangle(points[4], points[0], points[3], 0xcccc66);
triangles[11] =new Triangle(points[4], points[3], points[7], 0xcccc66);```

```//前
triangles[0] = new Triangle(points[0], points[1], points[2], 0x6666cc);
triangles[1] = new Triangle(points[0], points[2], points[3], 0x6666cc);```

```triangles[0] = new Triangle(points[1],points[2],points[0],0x6666cc);
triangles[1] = new Triangle(points[0],points[2],points[3],0x6666cc);```

```triangles[0] = new Triangle(points[1],points[2],points[3],0x6666cc);
triangles[1] = new Triangle(points[1],points[3],points[0],0x6666cc);```

```//判断是否在背面
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;
}```

```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 (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();
}

//判断是否在背面
private function isBackFace():Boolean {
// 见 http://www.jurjans.lv/flash/shape.html
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轴深度(以三个顶点中离观察者最近的点为准)
public function get depth():Number {
var zpos:Number = Math.min(pointA.z,pointB.z);
zpos = Math.min(zpos,pointC.z);
return zpos;
}
}
}```

```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);//增加三角形数组的z轴排序

graphics.clear();
for (i = 0; i < triangles.length; i++) {
triangles[i].draw(graphics);
}
}```

3D光线：

```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) {
//light向量的空间坐标
this.x = x;
this.y = y;
this.z = z;
//亮度
this.brightness = brightness;
}

public function set brightness(b:Number):void {
//亮度值通常要求在0与1之间
_brightness = Math.max(b,0);
_brightness = Math.min(_brightness,1);
}

public function get brightness():Number {
return _brightness;
}
}
}```

ok，理论知识准备得差不多了，下面来改造Triangle三角形基类：

```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;//每个三角形增加一个光源Light
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 (isBackFace()) {
return;
}
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();
}

//★★★根据光线得到动态调整后的颜色
//取出红，绿，蓝三色分量
var red:Number = color >> 16;
var green:Number = color >> 8 & 0xff;
var blue:Number = color & 0xff;

var lightFactor:Number = getLightFactor();//★★★根据光线得到的颜色调整因子!!! 这是关键！

red *=  lightFactor;
green *=  lightFactor;
blue *=  lightFactor;
return red << 16 | green << 8 | blue;
}

//根据光线得到的颜色调整因子(最难理解的的一个函数)
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();

//计算法向向量norm的坐标值
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);

//向量norm与向量light的点积(数量积)
var dotProd:Number = norm.x * light.x + norm.y * light.y + norm.z * light.z;

//向量norm的模长
var normMag:Number = Math.sqrt(norm.x * norm.x + norm.y * norm.y + norm.z * norm.z);

//向量light的模长
var lightMag:Number = Math.sqrt(light.x * light.x + light.y * light.y + light.z * light.z);

//夹角angle
var angle:Number = Math.acos(dotProd / (normMag * lightMag);

return (angle / Math.PI) * light.brightness; //夹角除取值范围最大值PI，将得到一个0到1之间的小数，然后再乘light的亮度值，即得到最终的光线调整因子
}

//是否处于背面
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轴深度
public function get depth():Number {
var zpos:Number = Math.min(pointA.z,pointB.z);
zpos = Math.min(zpos,pointC.z);
return zpos;
}
}
}```

```function Init():void {
//前面四个点
points[0] = new Point3D(-50,-50,-50);
points[1] = new Point3D(50,-50,-50);
points[2] = new Point3D(50,50,-50);
points[3] = new Point3D(-50,50,-50);
//后面四个点
points[4] = new Point3D(-50,-50,50);
points[5] = new Point3D(50,-50,50);
points[6] = new Point3D(50,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);
triangles[3] = new Triangle(points[1],points[4],points[5],_t);

_t = 0x00FF00;;

//下面
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);

//★★★只要增加下面的代码，给每个三角形赋值同样的光源实例即可！
var light:Light = new Light(-275,-200,-150);
for (i = 0; i < triangles.length; i++) {
triangles[i].light = light;
}

}```

992 篇文章76 人订阅

0 条评论

## 相关文章

### Q119 Pascal's Triangle II

Given an index k, return the kth row of the Pascal's triangle. For example, give...

3738

1982

### 《Kotlin极简教程》第五章 Kotlin面向对象编程（OOP）一个OOP版本的HelloWorld构造函数传参Data Class定义接口&实现之写pojo bean定一个Rectangle对象封

We frequently create a class to do nothing but hold data. In such a class some s...

2314

3348

### 3027 线段覆盖 2

3027 线段覆盖 2  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题解  查看运行结果 题目描述 Descript...

2956

1193

### C# String.Format的格式限定符与Format方法将多个对象格式化一个字符串原理

Format方法将多个对象格式化成一个字符串Format方法解析格式字符串的原理:

1232

### 洛谷 P1598 垂直柱状图【字符串+模拟】

P1598 垂直柱状图 题目描述 写一个程序从输入文件中去读取四行大写字母（全都是大写的，每行不超过72个字符），然后用柱状图输出每个字符在输入文件中出现的次数...

3165

1622

2735