Matrix3D 类使用一个 4x4 正方形矩阵,即一个由四行和四列数字构成的表,其中容纳了用于转换的数据。矩阵的前三行容纳每个 3D 轴 (x,y,z) 的数据。平移信息位于最后一列中。方向和缩放数据位于前三个列中。缩放因子是位于前三个列中的对角数字。以下是 Matrix3D 元素的表示形式:
空间中的某个点(x,y,z)的任何移动,旋转,缩放的变换,都是用这个点乘以某个一个矩阵而得到。那么模型是由一个个(x,y,z)的点组成,模型整个要变换,就是让每个点乘以某个矩阵。这个我们在《三维成像基本原理里》提到过。
这是三维引擎开发,和引擎代码阅读的必备的基础常识。
Flash3d的引擎里给我们提供了很丰富的运算类。(详细我们在《pv3d, sandy, away3d的三维数学运算类》介绍)
由于(x,y,z)乘以3*3矩阵可以达到旋转和缩放的目的,但是无法表示移动,所以为了统一,我们通常是采用乘以一个4行4列的矩阵。我们flash3d的引擎也都是为我们准备的4*4/4*3矩阵有关的算法。也就是说所有变换方法,都是用如下公式:
变换后点的(x’,y’,z’)=原来的(x,y,z) 乘以 4*4矩阵M
如果使用pv3d的数学类,写法如下:
var 点:number3D=new Number3D(x的值,y的值,z的值); //原来的点
var 数组:Array=[n11,n12,n13, 0,
n21,n22,n23, 0,
n31,n32,n33, 0,
nx , ny, nz, 1]; //构造矩阵内每个值,需要一个数组,我们通常都是以这种格式书写
var 矩阵: Matrix3D=new Matrix3D(数组); //构造矩阵
Matrix3D.multiplyVector4x4(矩阵,点); //点的x,y,z数据直接就更新为相乘后的数据了。
那么具体是用哪个矩阵M呢?下面我们就列出来:
(本教程的公式图,来自Skyman君的博客http://www.cnblogs.com/skyman/)
旋转
旋转在三维里应该是最麻烦的东西,因为人类对旋转的描述是很有限。光靠矩阵控制旋转很多情况是非常复杂和不形象的,所以我们还发明了优拉角和四元数这些对旋转的控制更优良的办法,这些我们以后会讲到。
角度θ表示的是延某轴旋转的角度。
下面三个矩阵分别表示了点绕x轴,y轴,z轴的旋转矩阵。
移动
从公式我们很容易能看出,第4行1,2,3列的数,分别控制着点在x,y,z方向上的移动。
缩放
从公式我们很容易能看出,对角线上的n11,n22,n33,分别控制着点在x,y,z方向上的缩放。
这些就是最简单的变换矩阵,熟悉了这些,我们就又向前迈进了一步。
图形学应该是一个很有趣的东西,并非像很多学校和学生教授的的那种痛苦不堪的东西。其实道理非常简单,如果自己再具备一点编程知识,很容易就能做出自己的成像程序。
附上一个3D实例 ,需要一张图片 bylee
package {
import flash.display.Bitmap;
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Vector3D;
[SWF(width=600,height=600,backgroundColor=0xffffff,frameRate=24)]
public class main extends Sprite {
// [Embed(source="example.jpg")]
// private var Img:Class;
[Embed(source="bin-debug/11.png")]
private var Img:Class;
private var _sprite:Sprite;
private var imgs:Array;
public function main():void {
init();
}
private function init():void
{
imgs = [];
_sprite = new Sprite();
_sprite.y = stage.stageWidth / 2;
_sprite.x = stage.stageHeight / 2;
var img1:Sprite = createImg();
var img2:Sprite = createImg();
var img3:Sprite = createImg();
var img4:Sprite = createImg();
var img5:Sprite = createImg();
var img6:Sprite = createImg();
img2.z = - 200;
img1.z = 200;
img3.rotationX = 90;
img3.y = 200;
img4.rotationX = -90;
img4.y = -200;
img5.rotationY = 90;
img5.x = -200;
img6.rotationY = -90;
img6.x = 200;
addChild(_sprite);
addEventListener(Event.ENTER_FRAME, onEnter);
}
private function onEnter(e:Event):void
{
_sprite.rotationY += 2;
_sprite.rotationX += 3;
sortImgs();
}
//把子显示对象按全局z坐标排序后再在父容器中添加它
private function sortImgs():void {
imgs.sort(depthSort);
for (var i:int = 0; i < imgs.length; i++) {
_sprite.addChild(imgs[i]);
}
}
//把图片放入一个空Sprite的正中,方便旋转
private function createImg():Sprite {
var tempSp:Sprite = new Sprite();
var img:Bitmap = new Img();
img.x = -img.width / 2;
img.y = -img.height / 2;
tempSp.addChild(img);
imgs.push(tempSp);
return tempSp;
}
//当显示对象是一个容器时,容器在旋转时,子对象也要随之旋转,这样子对象的局部3D坐标不
//同于全局的3D坐标,故要将之转换,下面的函数作用就是比较两个显示对象的全局z坐标
private function depthSort(objA:DisplayObject, objB:DisplayObject):int {
var posA:Vector3D = objA.transform.matrix3D.position;
var posB:Vector3D = objB.transform.matrix3D.position;
posA = _sprite.transform.matrix3D.deltaTransformVector(posA);
posB = _sprite.transform.matrix3D.deltaTransformVector(posB);
return posB.z - posA.z;
}
}
}