查看: 2214|回复: 0
打印 上一主题 下一主题

Flash3D透视变换

[复制链接]
.    

3797

主题

11

听众

5万

积分

首席设计师

Rank: 8Rank: 8

纳金币
32328
精华
41

活跃会员 优秀版主 荣誉管理 论坛元老

跳转到指定楼层
楼主
发表于 2012-12-21 14:40:41 |只看该作者 |倒序浏览
这算是我写的最认真的一次帖子,Flash3D的路的确很曲折难走,但是我相信有各位兄弟的相互扶持,共同进取。一定能够让我们少走些弯路。在这先感谢菜头帮的各位弟兄在平时给我的帮助。
***投影是3D固定流水线的重要组成部分,是将相机空间中的点从视锥体(***stum)变换到规则观察体(Canonical View Volume,简称CVV)中,待裁剪完毕后进行***除法的行为。在算法中它是通过***矩阵乘法和***除法两步完成的。
用Molehill写3D程序时,你必须要向显卡提交一个通过模型矩阵乘视图矩阵乘***变换矩阵后得到的总矩阵:
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,0,mvp,***e);(其中mvp就是最终要提交的总矩阵)
显卡拿到这个总矩阵后,就开始对各个顶点进行***变换的第一步,把顶点投影到CVV空间里面进行裁剪,这里有个关键的地方是CVV是一个边长为1的正方体。当投影到CVV空间的点不在这个正方体里面时,这个顶点将会被剔除出流水线。也就是你最终无法再屏幕上看不到这个顶点。
在理解***变换之前先必须给大家理清几个概念。
首先,最重要的一点就是齐次坐标。不理解齐次坐标,***投影就无从谈起。
一般一个空间点用三个坐标(x,y,z)就可以完全表示清楚。但是这种形式与空间变换矩阵相乘时,只能描述旋转变化,无法描述平移变换。因此,加入第四个维度w进行描述就能解决这个问题,Vector3D中就有一个w维度(默认为0)。同时有第四个维度后,就可以区分点和三维向量。
普通坐标 和齐次坐标之间的转换:
从普通坐标转换成齐次坐标时,如果(x,y,z)是个点,则变为(xw,yw,zw,w);(w可以为任何非零数,一般取1)如果(x,y,z)是个向量,则变为 (x,y,z,0)从齐次坐标转换成普通坐标时,如果是(x,y,z,w),(w不为零)则知道它是个点,变成(x/w,y/w,z/w);如果是(x,y,z,0),则知道它是个向量,仍然变成(x,y,z)。
齐次坐标表示的向量和点与矩阵相乘,结果与实际相符:点实现了平移,而向量只有大小和方向,平移不变。 其次,是一个简单却又非常重要的结论,就是矩阵乘法具有结合律
M1*M2*M3= M1*(M2*M3);
下面进入主题部分.
一般坐标系有两种,左手和右手坐标系,数学上一般是右手坐标系为主。下面我以右手坐标系来讲解,喜欢左手坐标系的朋友可以依此类比。
一般相机有个视锥体裁切的概念,但在这个不规则体中进行裁剪比较麻烦。通过***投影后,把它投影到CVV正方体中,进行裁切就很简单了, 但具体的怎么裁切不是这里的重点。
下面来说明下投影的具体原理,***是由一个4X4矩阵描述的,由上面的结论:M1*M2*M3= M1*(M2*M3)我们可以得出结论:只要把眼睛在原点,对着z轴负方向看的***矩阵写出来就可以了。这句话请好好琢磨下。
如下图,是右手坐标系中,视锥体由eye:眼睛位置,np:近裁剪平面,fp:远裁剪平面组成。N是眼睛到近裁剪平面的距离,F是眼睛到远裁剪平面的距离。在molehill中投影面选近裁剪平面为投影平面。下面为Adobe提供的一个***矩阵的生成方法。
public function perspectiveRH(width:Number,
           height:Number,
           zNear:Number,
           zFar:Number):void {
   this.copyRawDataFrom(Vector.<Number>([
    2.0*zNear/width, 0.0, 0.0, 0.0,
    0.0, 2.0*zNear/height, 0.0, 0.0,
    0.0, 0.0, zFar/(zNear-zFar), -1.0,
    0.0, 0.0, zNear*zFar/(zNear-zFar), 0.0
   ]));
  }
这个矩阵的具体推导过程大家没必要深究,下面我会向大家解释下这个矩阵的意思。当这个矩阵与空间中的一点(X,Y,Z,1)相乘后结果是(2XN/W,2YN/H,(Z+N)F/(N-F),-Z);
(其中N是zNear,F是zFar,W是width,H是height)
我们分析下结果:
(1) 第四个数是-Z,与变换前的Z只相差一个符号,因此这个数可以把它用在Z-buffer里面。
(2) 当Z等于-N时(即点在近裁切面上),结果变为(2XN/W,2YN/H,0,N)变为普通坐标(2X/W,2Y/H,0),此时,z=0;当Z等于-F时(即点在远裁切面上),结果变为(2XN/W,2YN/H,F,F)变为普通坐标(2XN/FW,2YN/FH,1)此时,z=1(因此,molehill中,CVV在z方向的范围是0到1)。
(3) 当Z=-N,X=-W/2时,普通坐标为x=-1;当Z=-N,X=W/2;普通坐标x=1;当Z=-N,Y=-H/2时,普通坐标为y=-1;当Z=-N,Y=H/2时,普通坐标为y=1;(因此,molehill中,CVV在z方向的范围是0到1)
现在你应该理解***投影是怎么回事了吧。
下面我结合两个例子来说明下:
先以一个简单的为例,下面是一个三角形的顶点数据,
vertexBuffer3D.uploadFromVector(Vector.<Number>([
    0,0,-5,1,0,0,
    10,10,-5,0,1,0,
   -10,10,-5,0,0,1]),0,3);
向z轴负方向看,你就可以看到这个三角形。
在初始化时:
matrix=new Matrix3D();(这个matrix作为视图矩阵)
perspectiveMatrix3D=new PerspectiveMatrix3D();
perspectiveMatrix3D.perspectiveRH(20,20,5,20000);然后在enterFrameHander函数里面:
var mvp:Matrix3D=perspectiveMatrix3D.clone(); //复制出***矩阵
mvp.prepend(matrix);              //然后此步骤得到最终的总矩阵
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,0,mvp,***e);
这样你就能够看到下面的图:
分析下这张图,由于设置近裁切距离是5,所以,这个三角形正好是帖在近裁切面上的。且红色的点坐标为(0,0,-5),因此***后的点是(0,0,0)其中z=0是作为裁切用的在0到1范围内,而前两个坐标是显示在屏幕上用的。这里是(0,0)因此正好显示在屏幕正中间。绿色点***后坐标是(1,1,0)因此显示在右上方,蓝色点为(-1,1,0)因此显示在左上方。 (1)如果你要把显示的三角形缩小一半,你可以用下面的方式:
var mvp:Matrix3D=perspectiveMatrix3D.clone();
mvp.prependScale(0.5,0.5,1);(注意,此时z方向不能缩小,否则***投影后就跑到近裁切面前面去了,但可以放大,三角形会远离眼睛)
mvp.prepend(matrix);
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,0,mvp,***e);
(2)如果你想旋转三角形也行,用下面的方式进行:
var mvp:Matrix3D=perspectiveMatrix3D.clone();
mvp.prependRotation(45,Vector3D.Z_AXIS);
mvp.prepend(matrix);
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,0,mvp,***e);
结果如下图:
下面来个复杂点的:任意点向任意方向看,这个对于屏幕老是黑的新手很有用。
Adobe给了一个函数:
lookAtRH(eye:Vector3D, at:Vector3D, up:Vector3D):void
我用它时老是得不到预想的结果,后来看它的源代码发现这个函数是错的。就比如开始的四句:
_z.copyFrom(at);   
_z.subtract(eye);
_z.normalize();
_z.w = 0.0;
其中第二句就没有任何意义。因为subtract函数不修改原向量,而是返回一个新向量。
下面我给出了自己写的代码:
public function lookAtRHB(eye:Vector3D, at:Vector3D, up:Vector3D):void {
   _z.copyFrom(eye);
   _z=_z.subtract(at);
   _z.normalize();
   _z.w = 0;
   
   _x.copyFrom(up);
   _crossProductTo(_x,_z);
   _x.normalize();
   _x.w = 0;
   
   _y.copyFrom(_z);
   _crossProductTo(_y,_x);
   _y.w = 0;
   
   _w=eye;
   _w.negate();
   _w.w=1.0;
   
   var mat:Matrix3D=new Matrix3D();
   mat.copyRowFrom(0,_x);
   mat.copyRowFrom(1,_y);
   mat.copyRowFrom(2,_z);
   mat.invert();
   mat.copyRowFrom(3,_w);
   this.prepend(mat);
  }
经过测试时正确的,大家如果把这个看懂了,我想***就应该是没什么问题了。
比如现在我想眼睛在(10,10,0)的位置眼睛看着(10,10,-5)那个点。则
perspectiveMatrix3D.lookAtRHB(new Vector3D(10,10,0),new Vector3D(10,10,-5),Vector3D.Y_AXIS);
var mvp:Matrix3D=perspectiveMatrix3D.clone();
mvp.prepend(matrix);
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,0,mvp,***e);
这样就有下面效果:中心点的确变成了绿色的那个顶点
现在大家应该对***有更好的理解了吧,要是不理解就自己动手多练练,加深印象。
【来源:互联网】
更多精彩教程,尽在web3D纳金网http://www.narkii.com/college/
分享到: QQ好友和群QQ好友和群 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
转播转播0 分享淘帖0 收藏收藏0 支持支持0 反对反对0
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

手机版|纳金网 ( 闽ICP备2021016425号-2/3

GMT+8, 2024-9-21 10:40 , Processed in 0.629912 second(s), 29 queries .

Powered by Discuz!-创意设计 X2.5

© 2008-2019 Narkii Inc.

回顶部