您的位置:首页 > 其它

【3D游戏引擎系列】一、渲染流程和坐标转换

2013-08-03 11:35 225 查看

一、概述

在3D游戏中,真实游戏场景会经过一系列的变化,最终会以2D图片的形式在屏幕中向我们展示出来。这一整个过程就是通常所说的“绘制流水线”,大多数时候称之为“管

线”。所谓“固定管线”是指数据进入硬件(GPU)后,使用的是DirectX或者OpenGL内置指令,我们无法去干涉整个流程。而“可编程管线”是指这里面某些环节是我们可

控的,我们可以使用GPU指令编写更丰富的内容,达到更好的效果。深入了解各流程的意义,有助于更好地开发游戏引擎。

《计算机实时图形学》将图形绘制管线分为应用程序阶段、几何阶段以及光栅阶段。而《3d管线导论》则在几何阶段和光栅阶段细分了一个三角形设置阶段。虽然不同的书对

于细节的划分不一样,但是整个流程大致都是一致的。

坐标转换是渲染流程的一部分,不过因为程序中我们经常使用,再加上本次会介绍多一点,所以在标题中单提了出来。

二、应用程序阶段

有的观点认为应用程序阶段严格来说并不属于管线的一部分,但由于跟渲染结果息息相关,姑且还是把它看成整个过程的起点。这个阶段主要是跟CPU、内存交互,建立场景

图、管理场景、视锥裁剪、碰撞检测等等,都是此阶段进行的。本次,不对该阶段做过多介绍。

三、几何阶段

几何阶段,主要负责顶点坐标变换、光照、裁剪、投影以及屏幕映射。通常显卡信息中有个“T&L”的硬件部分,就是Transform & Lighting----变换3维顶点坐标和光照计算。

当我们在游戏中转换视角的时候,看到的画面最总会在屏幕中以2D形式呈现出来。3D世界中的点,转化成屏幕中像素点的过程,就是顶点坐标转换。



这里很抱歉用了康玉之大牛的图片,我懒得自己去画了。图中茶色的区域就是顶点坐标变换的流程。根据转换的先后顺序,主要有以下几个顺序:

1.物体坐标系

物体坐标系即局部坐标系,是以物体自身定义的坐标系为基准的。采用此坐标系的优势是方便建模。使用3dmax、maya等建模工具时,你不必考虑一个房子到底应该放在城市

的哪个地方,只需要考虑这个房子本身模型是什么就可以了。之后,再将物体放入世界空间中的特定点就可以了。

2.世界坐标系

有了模型后,我们需要将模型放入一个3D场景中,这个3D场景的就是世界空间。从物体空间到世界空间的转换由一个四阶矩阵控制,一般叫做worldmatrix。该变换可以控制物

体的平移、缩放以及旋转。这样可以控制物体在世界坐标系中的位置、大小和角度。光照等计算一般在这个阶段,因为光照会涉及到光源的位置等。

3.相机坐标系

作为人眼的代替,在游戏中使用了相机这一概念。

单纯的世界坐标系并不够,因为我们很多时候并不是在世界坐标系原点观察场景,而可能是任意一点。这时候需要将世界坐标系转换为相机坐标系。即将世界坐标系做一定变

换,让原点与相机重合,并且让相机的观察方向与Z轴一致。

很明显,玩游戏的时候我们会发现屏幕中的图像总是整个场景的一部分。这是因为我们看到的东西跟一个叫做“视锥体”的东西有关。



如同模拟人眼一样,相机只能观察到一定范围内的空间。范围就是视锥体所包围的空间。视锥体很像一个被切掉了顶部的金字塔。靠近相机的平面是近裁剪面,作为投影面,对

应的就是屏幕。

4.视口坐标系

将相机坐标系中的顶点经过投影矩阵,变化为视口坐标系中的点。主要的投影有平行投影和透视投影,一般游戏中用的是透视投影。这样更符合近大远小的习惯。视锥体中的点

经过透视投影后,会被转换到一个立方体中,x和y的范围是-1到1,z的范围是0到1。

5.屏幕坐标

视口坐标需要转换到屏幕上显示,这一过程中,会有背面消影,深度测试等,然后将符合的像素写入缓存显示出来。

三、光栅化阶段

光栅化阶段主要操作有消除者当面、纹理操作、blending、filtering。

四、矩阵变化

来源:http://mathworld.wolfram.com/Matrix.html以及/article/5770893.html

写这一节的主要原因是因为前段时间用OGRE的时候,做矩阵变换时结果始终不对。后来查了资料发现是因为矩阵乘法顺序的问题。

1.矩阵和线性变换:

矩阵是用来表示线性变换的一种工具,它和线性变换之间是一一对应的。

考虑线性变换:

a11*x1 + a12*x2 + ...+a1n*xn = x1'

a21*x1 + a22*x2 + ...+a2n*xn = x2'

...

am1*x1 + am2*x2 + ...+amn*xn = xm'

对应地,用矩阵来表示就是:

|a11 a12 ... a1n | |x1| |x1'|

|a21 a22 ... a2n | |x2| |x2'|

|... |* |...|= |... |

|am1 am2 ... amn | |xn| |xm'|

也可以如下来表示:

|a11 a21 ... am1|

|a12 a22 ... am2|

|x1 x2...xn|*|... |= |x1' x2'... xm'|

|a1n a2n ... amn|

其中涉及到6个矩阵。分别为A[m*n],X[n*1],X'[m*1]以及X[1*n],A[n*m],X'[1*m]。

可以理解成向量x(x1,x2,...,xn)经过一个变换矩阵A[m*n]或A[n*m]后变成另外一个向量x'(x1',x2',...,xm'))。



2.矩阵的表示法:行矩阵 vs. 列矩阵


行矩阵和列矩阵的叫法是衍生自行向量和列向量。

其实,矩阵A[m*n]可以看成是m个n维的row vector构成的row matrix,也可看成是n个m维的column vector构成的column matrix。

其中,X[n*1]/X'[m*1]就等价于1个n/m维的column vector。X[1*n]/X'[1*m]就等价于1个n/m维的row vector。

Row matrix和Column matrix只是两种不同的表示法,前者表示把一个向量映射到矩阵的一行,后者表示把一个向量映射到矩阵的一列。

本质上体现的是同一线性变换。矩阵运算规定了它们可以通过转置运算来改变这个映射关系。



3.矩阵的相乘顺序:前乘或左乘 vs. 后乘或右乘


需要注意的是两种不同的表示法对应不同的运算顺序:

如果对一个column vector做变换,则变换矩阵(row matrix/vectors)必须出现在乘号的左边,即pre-multiply,又叫前乘或左乘。

如果对一个row vector做变换,则变换矩阵(column matrix/vectors)必须出现在乘号的右边,即post-multiply,又叫后乘或右乘。

一般不会弄错,因为矩阵乘法性质决定了相同的内维数的矩阵才能相乘。至于为什么是这个规律,为什么要row vector乘以column vector或column vector乘以row vector???想想吧。。。

所以左乘还是右乘,跟被变换的vector的表示形式相关,而非存储顺序决定。

4.矩阵的存储顺序:按行优先存储 vs. 按列优先存储

涉及到在计算机中使用矩阵时,首先会碰到存储矩阵的问题。

因为计算机存储空间是先后有序的,如何存储A[m*n]的m*n个元素是个问题,一般有两种:按行优先存储和按列优先存储。

row-major:存成a11,a12,...,amn的顺序。

column-major:存成a11,a21,...,amn的顺序。

这样问题就来了,给你一个存储好的矩阵元素集合,你不知道如何读取元素组成一个矩阵,比如你不知道a12该放在几行几列上。

所以,每个系统都有自己的规定,比如以什么规则存储的就以什么规则读取。DX使用Row-major,OGL使用Column-major.即一个相同的矩阵A[m*n]在DX和OGL中的存储序列是不一样的,这带来了系统间转换的麻烦。

不过,一个巧合的事情是:DX中,点/向量是用Row Vector来表示的,所以对应的变换矩阵是Column Matrix/Vectors,而OGL中,点/向量是用Column Vector来表示的,所以对应的变换矩阵是Row Matrix/Vectors.所以,如果在DX中对一个向量x(x1,x2,x3,1)或点(x(x1,x2,x3,1))应用A[4*4]的矩阵变换,就是x' = x(x1,x2,x3,1) * A[4*4],由于采用Row-major,所以它的存储序列是a11,a12,...,a43,a44。在OGL中,做同样的向量或点的变换,因为其使用Row
Matrix/Vectors,其应用的变换矩阵应该是A'[4*4] = A[4*4]( ' 表示Transpose/转置),就是x' = A'[4*4] * x'(x1,x2,x3,1),但是由于采用Column-major,它的存储序列正好也是a11,a12,...,a43,a44!!!

所以实际上,对DX和OGL来讲,同一个变换,存储的矩阵元素序列是一样的.比如:都是第13,14,15个元素存储了平移变化量deltaZ,deltaY,deltaZ.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: