从零实现3D图像引擎:(10)Hello3DWorld

1. 数学分析

前面我们已经把预先想到的可能会用到的数学工具都封装好了,从这篇开始,应该很少出现一大篇都是数学推导的了,终于看见光明了,这次我们将开始第一个3D程序的编写,所以题目就叫Hello3DWorld吧。

0) 3D程序的本质

很多书都会先介绍“3D流水线”的词,但其实明白3D程序的人一下子就知道这是什么,而不了解的人看了这个词也没有意义。其实我也觉得没什么特殊意义,因为所有计算机程序都是一个一个流程执行下来的,不全都是某某流水线么。简单来说,3D流水线就是,从在计算机中表示三维世界的数据,到绘制到计算机2D屏幕上的整个过程。

3D流水线的子过程有很多,都列出来只能让人更迷惑,还是举个例子说个简单的情况吧:

(1) 首先,你的3D游戏什么也没有,只有一个空空的世界,它除了有X,Y,Z三个坐标以外,什么也没有。

(2) 你的手里有一个金字塔,如果在计算机中表示,则金字塔有自己的一套X,Y,Z坐标系,还有金字塔的四个尖端(顶点)在金字塔坐标系中的坐标。

(3) 你需要把金字塔放在游戏世界中,于是需要建立一个关系——金字塔坐标系中的原点,在游戏世界中的坐标是什么。

(4) 一旦这个关系找到了,就可以把金字塔的所有顶点都移动到游戏世界中去。

(5) 东西都放好了,怎么显示这个3D世界呢?不妨以我们自己来做例子,我们生活在三维世界中,我们看到的东西,就是要显示的东西。所以在游戏世界中,我们也需要一个类似眼睛的东西,我们管它叫另外一个名字——相机。

(6) 相机在游戏世界中,就好象我们人在现实世界中。人的位置不同、看的方向不同,那么眼睛里的景色也不同。所以,在游戏世界里,我们要给这个相机设置坐标,还有它的朝向,这样才能确定看到什么景色。

(7) 现在相机所看的是一个锥形的3D世界,锥形的尖的位置就是相机的坐标,锥形的底面积随着相机朝向的方向越来越大。

(8) 最后,我们把这个锥体内的景色都投影到一个2D平面上,并放在计算机屏幕上显示。

下面来对上面说的步骤分开详细说明。

1) 物体局部坐标->世界坐标

一张图足以说明:

 

我们只要把物体的顶点的坐标的X,Y,Z都加上物体系原点在世界系下的坐标(0,0,5),就可以得到物体的各顶点在世界系下的坐标了。

2) 世界坐标->相机坐标

下图展示了上面所说的视景椎体和相机在游戏世界中的作用,注意该图是俯视2D图,方便理解。还有这个物体和上图的金字塔不是一回事,这是一个普遍的物体和相机,坐标都是很随意的,没有上面的那么特殊。

 

如上图,其实对于画在计算机屏幕上的目的而言,只有视景椎体内(屏幕后面的其实也没有用)的物体我们才关心,而且我们也实际上不关心物体在世界中的坐标,而只关心物体透视在屏幕上是什么坐标。这就引出了相机坐标系的概念,为了方便透视的运算,我们将建立相机坐标系,其中原点就是相机的位置,相机的朝向固定为正Z轴的方向,如下图:

 

这样,我们在做透视投影时如果知道了物体在相机坐标系下的坐标,透视运算将会非常的容易,这个下面会说到,现在我们先把物体挪过来吧。如下图所示,需要做两步操作:

 

经过上述两部,物体的坐标就从世界坐标系转化到了相机坐标系中。这里要注意,我是将物体和相机一起移动到原点,这是因为要保持相机视景椎体内的景色不变,在相机移动到原点之后,相机在相机系的坐标就是(0,0,0),朝向就是正Z轴,所以对相机不需要运算。需要运算的只有物体的顶点坐标,要算两次,一次是平移,一次是旋转。

(1) 物体平移到相机系

很显然,相机移动了多少,物体就移动了多少。所以我们要求一个(dx, dy, dz),使得相机的坐标和这个(dx, dy, dz)相加为(0, 0, 0),这样,我们只需要把物体的坐标与这个(dx, dy, dz)相加,也就能求得物体在相机系下的坐标了。

我们设相机在世界系下的坐标为(cx, cy, cz),那么(dx, dy, dz)是多少呢?还用我说么。。。当然是(-cx, -cy, -cz)了。

所以,我们只要把物体在世界系下的坐标wx, wy, wz,分别减去相机在世界系下的坐标cx, cy, cz就好了,OK完成。

(2) 物体随相机旋转

说到这里有点郁闷,因为之前没有介绍向量与矩阵的转化和矩阵的几何意义,但在这个小节来做这个完整的推导只能让人更迷惑,所以对旋转矩阵的推导就作为下一篇的内容吧,这里先给出结果:

物体绕X、Y、Z轴分别旋转的变换矩阵:

Rx(theta) =

1 0 0

0 cos(theta) sin(theta)

0 -sin(theta) cos(theta)

Ry(theta) =

cos(theta) 0 -sin(theta)

0 1 0

sin(theta) 0 cos(theta)

Rz(theta) =

cos(theta) sin(theta) 0

-sin(theta) cos(theta) 0

0 0 1

只要用一个点向量乘以这个变换矩阵,得到的结果向量就是绕对应轴旋转theta度后的点向量了。

要注意的是,上面的矩阵都是物体旋转的矩阵,可是我们现在已知的是相机的朝向,所以在把物体转到相机系的过程中,物体顶点绕各个轴旋转的角度是和现在已知的相机朝向相反的,所以物体世界坐标->相机坐标的旋转过程要使用-theta代入到上面的矩阵中去才是正确的。

3) 透视投影

经过上面的操作,现在只差一步了,把视景椎体中的物体顶点透视投影到屏幕2D平面上来。其实特别简单,如下图:

 

这里又是个俯视2D图,可以清楚的看到这个三角形的三个顶点是如何透视投影到屏幕上的。

下面的图则表明了透视后的点的X坐标怎么算:

 

点P就是物体的某一个顶点,因为这个图是沿Y轴负方向俯视下来的,所以Y坐标在这里都无法显示,但因为Y与X的推导原理相同,所以只看这里的X,我们就可以得到结论。

投影后的点为P',所以所求的x'就是线段AP,根据相似三角形的原理,可以非常容易得出:

AP' : BP = OA : OB

所以AP' = OA * BP / OB

好了,我们把坐标代进来吧:

x' = 视距 * x / z

如果我们将视距设置为1,那么x' = x / z,简单吧,而且整个视野内的物体的X坐标都将在[-1 , 1]这个范围内。

同理,y' = y / z。

4) 屏幕变换

就差最后一步了,我们现在x的范围是[-1, 1],y的范围也是[-1, 1],但是计算机屏幕的分辨率是800 * 600 (随便举个例子),怎么转换呢?

先把负数去掉吧,我们给X和Y坐标都先加个1,于是范围就变成了[0, 2]了。

离终点很近了,我们再把X坐标乘以400,范围不就是[0, 800]了么。OK,X坐标解决了。

<
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值