3d灯光讲解(共3篇)
篇1:3d灯光讲解
3ds Max灯光基础讲解
光线基础
这一部分我们来讨论所有类型的光线和如何更好的控制它们,
光的路线
首先要了解光是如何反射的,才能正确的模拟它们。
描述:光源发射出一束光,强度为 100% , falloff 的值决定了光线强度减弱的速度。当碰到障碍物,就会反射,反射强度依赖于物体的不同材质。抛光的金属会反射大约 50%-70% 的强度。黑色的橡胶则只能反射 10%-30% 。光线角(橙色箭头)依赖于材质的粗糙程度。粗糙程度越高则反射光线越趋向于分散,而不是只朝一个方向。
如果材质是透明会怎样?光线会在物体内部分散,当然强度和角度依赖于不同的材质。
最大的问题在于靠近边角的地方,这里光线反射的角度和距离都非常短。
这个图片是用了一个 spotlight ,用了 hard raytraced shodow ,和一个 surrounding light-emittering sphere ,
光线类型
常用的有3 种
1. INFINITE LIGHTS
2. POINT LIGHTS
3. SPOT LIGHTS
INFINITE LIGHTS
大多用于模拟太阳光,因为它是平行的。也就是说,只有角度,没有位置。多用来产生阴影。
POINT LIGHTS
多用来做反射,填充光。
point light 产生阴影的效果很差。唯一的方法是用 raytrace 。
如果选用了shadowmap ,渲染器必须渲染6个depthmap ,如果用了shaw softness ,那效果更糟。
SPOT LIGHTS
用于任何东西。
可用于阴影和反射。最重要的,可以限制阴影的区域。(尤其对于 shadowmap )。
反射的使用方法:
* 设置cone angle为0 , spread angle 根据需要设置。
* 关掉阴影。
* 选择正确的光线颜色代表 “emitter material” 。
* 放在光源物体里面,朝向正确的角度。
篇2:3d灯光讲解
当你准备照亮一个场景时,应注意下面几个问题:
-场景中的环境是什么类型的?
场景灯光通常分为三种类型:自然光、人工光以及二者的结合。
具有代表性的自然光是太阳光。当使用自然光时,有其它几个问题需要考虑:现在是一天中的什么时间;天是晴空万里还是阴云密布;还有,在环境中有多少光反射到四周?
人工光几乎可以是任何形式。电灯、炉火或者二者一起照亮的任何类型的环境都可以认为是人工的。人工光可能是三种类型的光源中最普通的。你还需要考虑光线来自哪里,光线的质量如何。如果有几个光源,要弄清除哪一个是主光源?确定是否使用彩色光线也是重要的。几乎所有的光源都有一个彩色的色彩,而不是纯白色。
最后一种灯光类型是自然光和人工光的组合。在明亮的室外拍摄电影时,摄影师和灯光师有时也使用反射镜或者辅助灯来缓和刺目的阴影。
-灯光的目的是什么?
换句话说,场景的基调和气氛是什么?在灯光中表达出一种基调,对于整个图像的外观是至关重要的。在一些情况下,唯一的目标是清晰地看到一个或几个物体,但通常并非如此,实际目标是相当复杂的。
灯光有助于表达一种情感,或引导观众的眼睛到特定的位置。可以为场景提供更大的深度,展现丰富的层次。因此,在为场景创建灯光时,你可以自问,要表达什么基调?你所设置的灯光是否增进了故事的情节?
-在场景中是否有特殊灯光效果,如果有,它们是应该用灯还是通过其他途径创建?
除了通常类型的灯光外,很多三维动画软件以白炽灯、立体光源和特殊材料属性的形式提供许多特殊效果。虽然严格说来,一些并不属于灯的类型,在场景中,它们通常在可见光效果的外观上再添加进来。一个简单的例子是可见光源的闪耀或发光。由于这些效果在3D中不能自动产生,你需要在渲染中专门把它们包括进来,并且考虑他们的外观和长处。
-是否有创作来源的参考资料?
在创作逼真的场景时,应当养成从实际照片和电影中取材的习惯。好的参考资料可以提供一些线索,让你知道特定物体和环境在一天内不同时间或者在特定条件下看起来是怎样的。
通过认真分析一张照片中高光和阴影的位置,通常可以重新构造对图像起作用的光线的基本位置和强度。通过使用现有的原始资料来重建灯光布置,也可以学到很多知识。
在考虑了上面的问题后,现在应当为一个场景创建灯光了。虽然光源的数量、类型和他们单独的属性将因场景不同而异,但是,有三种基本类型的光源:关键光、补充光和背景光,它们在一起协调运作。
-关键光
在一个场景中,其主要光源通常称为关键光。关键光不一定只是一个光源,但它一定是照明的主要光源。同样,关键光未必像点光源一样固定于一个地方。
虽然点光源通常放在四分之三的位置上(从物体的正面转45度,并从中心线向上转45度,这一位置很多时候被当作定势使用),但根据具体场景的需要,也可来自物体的下面或后面,或者其他任何位置,
关键光通常是首先放置的光源,并且使用它在场景中创建初步的灯光效果。
虽然最初的放置为照亮物体提供了一个好的方法,但是,得到的结果确实是单调而无趣的图像。阴影通常很粗糙且十分明显。同样,场景看起来总是太暗,因为没有自然的环境光来加亮阴影区域。这种情况在特定的场景中是很有用的,例如夜晚场景,但是,对大多数画面来说,就显得有些不合适了。
-补充光
补充光用来填充场景的黑暗和阴影区域。关键光在场景中是最引人注意的光源,但补充光的光线可以提供景深和逼真的感觉。
比较重要的补充光来自天然漫反射,这种类型的灯光通常称为环境光。这种类型的光线之所以重要,部分原因是它提高了整个场景的亮度。不幸的是,大多数渲染器的环境光统一地应用于整个场景。减低了场景的整体黑暗程度,它淘汰掉了一些可能的特性,不能对照亮的物体上的任何光亮和阴影进行造型,这是使场景看起来不逼真的主要原因。
模拟环境光的更好的方法是,在场景中把低强度的聚光灯或泛光灯放置在合理的位置上。这种类型的辅助光应当减少阴影区域,并向不能被关键光直接照射的下边和角落补充一些光线。
除了场景中的天然散射光或者环境光之外,补充光用来照亮太暗的区域或者强调场景的一些部位。它们可以放置在关键光相对的位置,用以柔化阴影。
-背景光
背景光通常作为“边缘光”,通过照亮对象的边缘将目标对象从背景中分开。它经常放置在四分之三关键光的正对面,它对物体的边缘起作用,引起很小的反射高光区。如果3D场景中的模型由很多小的圆角边缘组成,这种高光可能会增加场景的可信性。
-其他类型的光源
实际光源是那些在场景中实际出现的照明来源。台灯、汽车前灯、闪电和野外燃烧的火焰都是潜在的光源。
在为场景设置灯光以后,还有一些其他因素需要考虑。
-我的解决方法简单而必要吗?
场景中的灯光与真正的灯光不同,它需要在渲染时间上多花功夫,灯光设置越复杂,渲染所花费的时间越多,灯光管理也会变得越难。你应当自问,每一种灯光对正在制作的外观是否十分必要。
当增加光源时,自然会减少反射点。在一些点,增加光源不会对场景的外观有所改善,并且将变得很难区分所增加光源的价值。你可以尝试独立察看每一个光源,来衡量它对场景的相对价值。如果对它的作用有所怀疑,就删除它。
-有些物体是否需要从光源中排除?
从一些光源中排除一个物体,在渲染的时候,便可以节约时间。
这个原则对于制作阴影也是正确的。场景中的每一个光源都用来制作阴影,这种情况是很少见的。制作阴影可能是十分昂贵的(尤其是光线跟踪阴影的情况下),并且有时对最终图像是有害的。
-用贴图效果而不用实际光源能够模拟任何灯光吗?
建筑物光源、照亮的显示器和其他独立的小组合光源,有时可以用贴图创建,而不使用实际光源。
-是否可以使用一些技巧使场景更真实?
篇3:3d灯光讲解
3D灯光
刚刚这个例子近乎可以让我们的渲染达到完美的效果,但是它似乎还缺少点儿什么,有些单调。OK,OK,大家看到标题就已经知道了,下面就让我们加入3D的灯光效果吧。
同背面剔除一样,3D灯光的细节也是相当复杂并且需要数学运算的。我实在没有太多的空间讨论每个漂亮的细节,但是通过快速的网络搜索大家可以获得非常更多的相关资料,也许这些资料多得我们一生也看不完。在这里,我给大家的都是一些基础的需要用到的函数。
首先,需要一个光源。一个最简单的光源只有两个属性:位置和亮度(brightness)。在更加复杂的 3D 系统中,它也能够指向某个方向,并且还带有颜色,衰减率(falloff rate),圆锥区域等等。但是这些都超出了本例的范围。
让我们从制作一个 Light灯光类开始。它会持有我们刚说的那两个属性 —— 位置和亮度。
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 = -100,
y:Number = -100,
z:Number = -100,
brightness:Number = 1) {
this.x = x;
this.y = y;
this.z = z;
this.brightness = brightness;
}
public function set brightness(b:Number):void {
_brightness = Math.max(b, 0);
_brightness = Math.min(_brightness, 1);
}
public function get brightness():Number {
return _brightness;
}
}
}
现在可以在主类的 init 方法中创建一个新的默认灯光:
var light:Light = new Light();
或者可以创建一个指定位置和区域的灯光:
var light:Light = new Light(100, 200, 300, .5);
这里有两个重要的地方需要注意。一个是位置,仅用于计算灯光的角度。灯光的亮度不会因为距离而衰减。因此改变 x, y, z 到 –1,000,000 或 -1 对于照射在物体上的灯光的亮度是没有区别的。
只有 brightness 属性才会改变灯光的特性。我们当然可以加入一个函数用以判断灯光与物体间的距离来计算灯光的亮度值(brightness)。不会很难,现在已经介绍得差不多了,因此把这个函数留给大家去做。
brightness 必需是 0.0 到 1.0 之间的数。如果出了这个范围,会带来一些奇怪的结果。就是这个原因,我创建了一个私有属性 _brightness,并允许通过公共的 getter和setter 访问 brightness。这样做,允许我们传入的数值得到有效性的验证,确保这个数在有效范围内。
一个理想的类不应该出现公有的属性,即使这些属性不需要验证,也只有私有属性通过 getter和setter 函数才能访问。这里我抄了近路,为的是让代码简洁并突出动画编程的原则。但是在本例中,额外添加的这一步是有必要的。
下面,光源要做的就是根据灯光照射在多边形上的角度来改变三角形颜色的亮度值。因此如果一个多边形直接面对灯光,它就会显示出全部的颜色值。当离开灯光时,就会变得越来越暗。最终,当它完全离开光源时,它将完全变为阴影或黑色。
由于 Triangle 类的成员知道自己的颜色是什么,并知道如何绘制自己,似乎每个三角形只需访问这个 light 就可以实现自己 draw 函数。因此,让我们给所有三角形一个 light 属性。我还要超个近路设置它们为公有属性:
public var light:Light;
然后在主类中,创建这些三角形后,只需要循环它们把灯光的引用赋值给每个三角形:
var light:Light = new Light();
for(i = 0; i < triangles.length; i++) {
triangles[i].light = light;
}
或者,我们也可以让 light 作为 Triangle 构造函数中的一个附加的参数,让每个三角形都有一个光源。我将这个方法留给大家去选择。
现在,Triangle 需要一个关于其灯光颜色、角度、亮度的函数,并返回一个调整后的颜色值。以下是这个函数:
function getAdjustedColor():uint {
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;
}
这个函数首先将三角形的基本颜色分为了 red, green, blue 三个成分(见第四章)。然后调用另一个方法 getLightFactor,稍后会看到这个函数。现在,只需要知道它返回的是 0.0 到 1.0 之间的一个数,表示该颜色需要改变的大小,1.0 表示全部亮度,0.0 表示为全黑色。
然后将每个颜色成分乘以这个滤光系数(light factor),最后再将它们组合为一个 24 位的颜色值,并且将它作为调整后的颜色返回。它将成为灯光照射下三角形的颜色。
现在,如何得到这个 lightFactor 呢?让我们看一下:
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.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);
var dotProd:Number = norm.x * light.x +
norm.y * light.y +
norm.z * light.z;
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);
return Math.acos(dotProd / normMag * lightMag) / Math.PI * light.brightness;
}
哇,好大一个函数不是吗?要想完全理解它,就一定要对高等向量学有较深的掌握,但是我也会试将基础的地方解释一下。
首先,我们需要找到三角形的法线(normal)。它是一个向量,是三角形平面上的一条垂线,如图 17-5 所示。想象一下,我们拿着一块木制的三角板,然后从背后钉入一根钉子,它会从正面穿出。这根钉子就代表三角形平面的法线。如果您学过 3D 渲染和灯光的话,一定看过各种关于法线的资料。
图17-5 法线是到达三角形表面的一条垂线
我们可以通过该平面的两个向量计算出它们的外积(cross product)从而求出这条法线。两个向量的积是一条垂直于这两条向量的新向量。我们将使用的这两条向量是点 A和B,点 B和C 之间的连线。每个向量都用有带有 x, y, z的 Object 持有。
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;
然后计算法线,即另一个向量。求该对象的模(norm)。下面的代码用于计算向量ab和bc的外积:
var norm:Object = new Object();
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);
我没有太多的篇幅来介绍这种计算方法的细节,这是计算向量外积的标准公式。如果您对它的推导感兴趣,可以随便找一本线性代数的正规参考书查一查。
现在我们需要知道这条法线与灯光的角度。向量数学的另一个好东西叫做内积(dot product),它与外积不同。我们有了法线的向量和灯光的向量。下面计算点积:
var dotProd:Number = norm.x * light.x + norm.y * light.y + norm.z * light.z;
我们看到,内积要比外积简单一些!
OK,都差不多了!接下来,计算法线的量值,以及灯光的量值,大家应该还认识这个 3D 版的勾股定理吧:
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);
请注意,当一个三角形被渲染时,变量lightMag 每次都要进行计算,这样就允许灯光是移动的。如果知道光源是固定的,我们可以在代码的一开始就加入这个变量,只需在创建灯光或为三角形赋值时进行一次计算。或者可以为 Light 类添加 lightMag 属性,让它可以在每次 x, y, z 属性发生变化时被计算。看,我已经给大家留出了各种发挥的空间!
最后,将前面计算出的这些数放入一个具有魔力公式中:
return (Math.acos(dotProd / (normMag * lightMag)) / Math.PI) * light.brightness;
其中 dotProd 是一个分量,而 normMag * lightMag 是另一个分量。两者相除得出一个比率。回忆一下第三章,一个角度的余弦给了我们一个比率,而一个比率的反余弦给了我们一个角度。这就是灯光照射在多边形表面上的角度。它的范围在 0 到 Math.PI 个弧度之间(0 到 180 度),也就是说灯光完全照射在物体前面上或完全照射在物体背面。
用这个数除以Math.PI得出一个百分数,再用它乘以brightness的百分比就得出了最终用于改变底色的滤光系数。
OK,所有这些仅仅给出了多边形表面颜色!此刻,在现有的代码中实现它就非常简单了。我们在 draw 方法中使用它。应该像这样直接使用这个调整后的颜色:
g.beginFill(getAdjustedColor());
为了把上述内容综合起来,以下是全部最终的 Triangle.as和ExtrudedA.as 代码,列出了我们本章所有发生变化的部分:
首先是 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;
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(getAdjustedColor());
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 getAdjustedColor():uint {
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.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);
var dotProd:Number = norm.x * light.x +
norm.y * light.y +
norm.z * light.z;
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);
return Math.acos(dotProd / normMag * lightMag) / Math.PI * light.brightness;
}
private function isBackFace():Boolean {
// 见 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;
}
public function get depth():Number {
var zpos:Number = Math.min(pointA.z, pointB.z);
zpos = Math.min(zpos, pointC.z);
return zpos;
}
}
}
然后是 ExtrudedA:
package {
import flash.display.Sprite;
import flash.events.Event;
public class ExtrudedA extends Sprite {
private var points:Array;
private var triangles:Array;
private var fl:Number = 250;
private var vpX:Number = stage.stageWidth / 2;
private var vpY:Number = stage.stageHeight / 2;
public function ExtrudedA() {
init();
}
private function init():void {
points = new Array();
points[0] = new Point3D( -50, -250, -50);
points[1] = new Point3D( 50, -250, -50);
points[2] = new Point3D( 200, 250, -50);
points[3] = new Point3D( 100, 250, -50);
points[4] = new Point3D( 50, 100, -50);
points[5] = new Point3D( -50, 100, -50);
points[6] = new Point3D(-100, 250, -50);
points[7] = new Point3D(-200, 250, -50);
points[8] = new Point3D( 0, -150, -50);
points[9] = new Point3D( 50, 0, -50);
points[10] = new Point3D( -50, 0, -50);
points[11] = new Point3D( -50, -250, 50);
points[12] = new Point3D( 50, -250, 50);
points[13] = new Point3D( 200, 250, 50);
points[14] = new Point3D( 100, 250, 50);
points[15] = new Point3D( 50, 100, 50);
points[16] = new Point3D( -50, 100, 50);
points[17] = new Point3D(-100, 250, 50);
points[18] = new Point3D(-200, 250, 50);
points[19] = new Point3D( 0, -150, 50);
points[20] = new Point3D( 50, 0, 50);
points[21] = new Point3D( -50, 0, 50);
for (var i:uint = 0; i < points.length; i++) {
points[i].setVanishingPoint(vpX, vpY);
points[i].setCenter(0, 0, 200);
}
triangles = new Array();
triangles[0] =new Triangle(points[0], points[1],
points[8], 0xcccccc);
triangles[1] =new Triangle(points[1], points[9],
points[8], 0xcccccc);
triangles[2] =new Triangle(points[1], points[2],
points[9], 0xcccccc);
triangles[3] =new Triangle(points[2], points[4],
points[9], 0xcccccc);
triangles[4] =new Triangle(points[2], points[3],
points[4], 0xcccccc);
triangles[5] =new Triangle(points[4], points[5],
points[9], 0xcccccc);
triangles[6] =new Triangle(points[9], points[5],
points[10], 0xcccccc);
triangles[7] =new Triangle(points[5], points[6],
points[7], 0xcccccc);
triangles[8] =new Triangle(points[5], points[7],
points[10], 0xcccccc);
triangles[9] =new Triangle(points[0], points[10],
points[7], 0xcccccc);
triangles[10] =new Triangle(points[0], points[8],
points[10], 0xcccccc);
triangles[11] =new Triangle(points[11], points[19],
points[12], 0xcccccc);
triangles[12] =new Triangle(points[12], points[19],
points[20], 0xcccccc);
triangles[13] =new Triangle(points[12], points[20],
points[13], 0xcccccc);
triangles[14] =new Triangle(points[13], points[20],
points[15], 0xcccccc);
triangles[15] =new Triangle(points[13], points[15],
points[14], 0xcccccc);
triangles[16] =new Triangle(points[15], points[20],
points[16], 0xcccccc);
triangles[17] =new Triangle(points[20], points[21],
points[16], 0xcccccc);
triangles[18] =new Triangle(points[16], points[18],
points[17], 0xcccccc);
triangles[19] =new Triangle(points[16], points[21],
points[18], 0xcccccc);
triangles[20] =new Triangle(points[11], points[18],
points[21], 0xcccccc);
triangles[21] =new Triangle(points[11], points[21],
points[19], 0xcccccc);
triangles[22] =new Triangle(points[0], points[11],
points[1], 0xcccccc);
triangles[23] =new Triangle(points[11], points[12],
points[1], 0xcccccc);
triangles[24] =new Triangle(points[1], points[12],
points[2], 0xcccccc);
triangles[25] =new Triangle(points[12], points[13],
points[2], 0xcccccc);
triangles[26] =new Triangle(points[3], points[2],
points[14], 0xcccccc);
triangles[27] =new Triangle(points[2], points[13],
points[14], 0xcccccc);
triangles[28] =new Triangle(points[4], points[3],
points[15], 0xcccccc);
triangles[29] =new Triangle(points[3], points[14],
points[15], 0xcccccc);
triangles[30] =new Triangle(points[5], points[4],
points[16], 0xcccccc);
triangles[31] =new Triangle(points[4], points[15],
points[16], 0xcccccc);
triangles[32] =new Triangle(points[6], points[5],
points[17], 0xcccccc);
triangles[33] =new Triangle(points[5], points[16],
points[17], 0xcccccc);
triangles[34] =new Triangle(points[7], points[6],
points[18], 0xcccccc);
triangles[35] =new Triangle(points[6], points[17],
points[18], 0xcccccc);
triangles[36] =new Triangle(points[0], points[7],
points[11], 0xcccccc);
triangles[37] =new Triangle(points[7], points[18],
points[11], 0xcccccc);
triangles[38] =new Triangle(points[8], points[9],
points[19], 0xcccccc);
triangles[39] =new Triangle(points[9], points[20],
points[19], 0xcccccc);
triangles[40] =new Triangle(points[9], points[10],
points[20], 0xcccccc);
triangles[41] =new Triangle(points[10], points[21],
points[20], 0xcccccc);
triangles[42] =new Triangle(points[10], points[8],
points[21], 0xcccccc);
triangles[43] =new Triangle(points[8], points[19],
points[21], 0xcccccc);
var light:Light = new Light();
for (i = 0; i < triangles.length; i++) {
triangles[i].light = light;
}
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(event:Event):void {
var angleX:Number = (mouseY - vpY) * .001;
var angleY:Number = (mouseX - vpX) * .001;
for (var i:uint = 0; i < points.length; i++) {
var point:Point3D = points[i];
point.rotateX(angleX);
point.rotateY(angleY);
}
triangles.sortOn(“depth”, Array.DESCENDING | Array.NUMERIC);
graphics.clear();
for (i = 0; i < triangles.length; i++) {
triangles[i].draw(graphics);
}
}
}
}
我们看到,在文档类中只有两个次要的变化,
主要的工作都集中在 Triangle 中。同时,我还让所有的三角形使用相同的颜色,我认为这样做可以更好地观察灯光效果(见图 17-6)。