[3D基础]3D游戏中的各种旋转与基变换
“琉我”通过精心收集,向本站投稿了2篇[3D基础]3D游戏中的各种旋转与基变换,下面就是小编给大家带来的[3D基础]3D游戏中的各种旋转与基变换,希望大家喜欢,可以帮助到有需要的朋友!
篇1:[3D基础]3D游戏中的各种旋转与基变换
终于开始着手写[3D基础]系列的第二篇文章了,这篇文章所将的内容相信对于很多人而言都是相当重要的,因为涉及到相机坐标系变换,BILLBOARD实现原理,凹凸映射切空间变换等课题,按以前的习惯,重要内容用红色字体标出,对于最基本的线性代数理论不作证明,使用DX左手坐标系。
第一个问题:UVN相机坐标变换形式与原理。
我们知道3D世界空间到相机空间坐标的转换是通过UVN矩阵来实现,运算格式如下:
| Ux, Vx, Nx |
| x, y, z | * | Uy, Vy, Ny |
| Uz, Vz, Nz |U V N 分别为相机的右、上、前向量,且已规格化。现在我们来理解下这个向量矩阵乘法的几何意义。对运算进行分解如下:
X0 = | x, y, z | Dot | Ux, Uy, Uz |
Y0 = | x, y, z | Dot | Vx, Vy, Vz |
Z0 = | x, y, z | Dot | Nx, Ny, Nz |
是不是有点眉目了,几何上,一个向量V1与另外一个规格化向量V2的点积,结果是V1在V2向量上的带符号投影长度,
因此,这里X0、Y0、Z0便是向量| x,y,z |在U、V、N 向量上的投影长度,在相机坐标空间中,相机的右向量U将被用作X坐标轴,V将被用作Y坐标轴,N被用作Z坐标轴,所以变换后新的向量为| X0, Y0, Z0 |。
一个很重要的矩阵性质:正交矩阵的逆矩阵等于其转置矩阵。
第二个问题:相机平面对齐公告板的旋转。
其实公告板实现分两步,第一步先将公告板旋转到与相机平面平行,第二步将旋转后的定点平移到指定的空间位置。
我们只说第一步的原理,所谓与视平面对齐,其实就是一个矩形经旋转后位于XY平面上,已知旋转后的结果,求旋转前四边形的的坐标,用旋转后的结果乘UVN矩阵逆矩阵就OK了。如:
| Ux, Uy, Uz |
| 2, 0, 0 | * | Vx, Vy, Vz | 〔U V N矩阵为正交矩阵〕
| Nx, Ny, Nz |
第三个问题:凹凸映射切空间的变换。
原理与问题1和2一模一样,需要切空间,利用切空间向量组成的矩阵将光线向量转换到切空间中,然后计算光照,OVER!
篇2:Flash基础理论课 第十七章 背面剔除与3D灯光Ⅱ
返回“Flash基础理论课 - 目录”
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)。
图17-6 带有背面剔除,深度排序及3D灯光的三维立体模型
【[3D基础]3D游戏中的各种旋转与基变换】相关文章:
2.3d打印教学计划
5.3d打印调研报告
9.看3d电影作文
10.3d打印机投资方案范文






文档为doc格式