首页 > 代码库 > 3D图形学理论入门指南(转)

3D图形学理论入门指南(转)

原文地址:http://blog.sina.com.cn/s/blog_e7779a160102wpt1.html
 
3D图形学理论入门指南
介绍
        当我还小的时候,我曾以为计算机图形学是最酷的玩意儿。但是随即我认识到,学习图形学——创建那些超级闪亮的计算机程序——比我想象的要难上许多。我四处出击,阅读OpenGL渲染管线详解之类的文章,浏览关于图形工作原理的博客、网站等,对照着教程学习,试图搞懂一切。结果呢,一无所获。当我按照NeHe的教程设置好一切,却因为错误的调用了某个glXXX()函数,导致各种错误。我不具备正确调试程序的基础理论知识,所以我放弃了——就像我那个年纪的少年在遇到挫折时通常会做的那样。
        然而,在若干年之后,我有机会能够在大学里参加一些计算机图形学的课程。这次我终于知道它们是如何正确工作了。如果我早知道这些,我那时应该能获得更多成功。所以,为了帮助和我有类似困境的人们,我打算分享下我学到的东西。
图形学背后的理念
概览
        先想想真实世界的样子。在3D真实世界里,光线从许多个不同的光源发出,在多个物体间跳转,然后一部分光子通过眼球刺激到你的视网膜。在真实的场景里,3D的世界投影到2D的表面。虽然你的大脑从环境中获取各种视觉元素然后组成一个立体的影像来反映整个3D空间,但这些都源于2D信息。当场景中的物体移动,或者你相对于你的场景发生移动,或光照改变时,视网膜上的2D图像也立刻发生改变。我们的视觉系统快速处理图像,然后大脑据此构造出3D模型。

技术分享
如果我们能够获取一些图片,然后以类似或更高的速率来交替显示它们,就能生成一个看起来像真实空间的场景。电影大致基于同一原理工作。在电影里,3D场景的图片高速闪过,看起来就像连续的一样。请参照上面马的例子。如果我们能够在计算机上持续的绘制一个运动的场景,那么它看起来就像一个3D的世界一样。图形学就是这样工作的:将虚拟的3D世界快速转换成2D表现形式,让大脑感觉像是一个3D的场景一样。
约束条件
        人类视觉将一系列图片看作连续的阈值大约时16Hz。对计算机来说,我们有最多62.5毫秒来完成下列事情:
        判断虚拟场景中眼睛看向哪里。
        计算场景在这个角度下如何呈现。
        计算需要被绘制在屏幕上的像素的颜色。
        用这些颜色填充帧缓存。
        将缓存发送至显示设备。
        显示图片。
        这是一个复杂的问题。时间上的限制意味着我们不能直接硬来——比如往3D场景里扔一堆光子,计算它们的轨迹和强度,算出哪些能够照进眼睛并将之映射到2D的图片上,最后绘制。(这并不完全正确,因为这有点像光线追踪时做的事。但光线追踪技术相当复杂,而且完全是另一回事,因此也可以这么说。)幸运的是,我们可以利用一些很酷的技巧来大大缩减计算量。
基础图形理论
        整个世界是一个舞台
技术分享
如果我们能够获取一些图片,然后以类似或更高的速率来交替显示它们,就能生成一个看起来像真实空间的场景。电影大致基于同一原理工作。在电影里,3D场景的图片高速闪过,看起来就像连续的一样。请参照上面马的例子。如果我们能够在计算机上持续的绘制一个运动的场景,那么它看起来就像一个3D的世界一样。图形学就是这样工作的:将虚拟的3D世界快速转换成2D表现形式,让大脑感觉像是一个3D的场景一样。
约束条件
        人类视觉将一系列图片看作连续的阈值大约时16Hz。对计算机来说,我们有最多62.5毫秒来完成下列事情:
        判断虚拟场景中眼睛看向哪里。
        计算场景在这个角度下如何呈现。
        计算需要被绘制在屏幕上的像素的颜色。
        用这些颜色填充帧缓存。
        将缓存发送至显示设备。
        显示图片。
        这是一个复杂的问题。时间上的限制意味着我们不能直接硬来——比如往3D场景里扔一堆光子,计算它们的轨迹和强度,算出哪些能够照进眼睛并将之映射到2D的图片上,最后绘制。(这并不完全正确,因为这有点像光线追踪时做的事。但光线追踪技术相当复杂,而且完全是另一回事,因此也可以这么说。)幸运的是,我们可以利用一些很酷的技巧来大大缩减计算量。
基础图形理论
        整个世界是一个舞台
技术分享
假设我们要分解一个球形。我们可以将球体的中心位置定为本地的原点。这样我们就可以用一个公式来获得球面上的一些点然后将这些点连接成多边形以供绘制。一个常用的公式是S(u,v)=[r sin u cos v,r  sin u sin v,r cos v],u和v的取值范围分别是u∈[0,π],v∈[0,2π],r是球体的半径。就像你在图中看到的那样,球体表面的点被绘制成矩形。我们能够很方便的把它们连成三角形。
        球面上的点位于所谓的模型坐标系。坐标相对于本地的原点定义,比如示例中球体的中心位置。如果我们想要将物体放置于场景中,我们可以定义一个从场景原点到场景中球体的原点的向量,然后把这个向量和球面上每个点的坐标相加。这样我们就将模型放置到了世界坐标系中。
        世界坐标系——将物体置于世界中
        到这儿我们的图形学之旅才真正开始。我们在某处定义一个原点,场景中的每个点都基于从原点到该点的一个向量来定义。虽然场景是3D的,我们还是得用一个4维的坐标来定义每个点[x,y,z,w],代表该点的坐标为[x/w,y/w,z/w]。这种映射称为齐次坐标。使用齐次坐标有一些好处,但是这里不做讨论。只需知道我们使用齐次坐标就够了。
        假设我们要在场景中移动,那么问题来了。如果我们要移动视线,或者移动相机到另一个位置,或者让整个世界围着相机移动。在计算机的世界里,移动整个世界更容易一点,所以我们就这样做,让相机固定不动。模型-视图矩阵(modelview matrix)是一个4x4矩阵,可以用来移动世界中的每一个点,然后让相机固定不动。这个矩阵基本上就是一系列旋转、位移、缩放的集合。我们在世界坐标系中将点和模型-视图矩阵相乘,这将使我们进入观察坐标系(viewing coordinates)。
技术分享
假设我们要分解一个球形。我们可以将球体的中心位置定为本地的原点。这样我们就可以用一个公式来获得球面上的一些点然后将这些点连接成多边形以供绘制。一个常用的公式是S(u,v)=[r sin u cos v,r  sin u sin v,r cos v],u和v的取值范围分别是u∈[0,π],v∈[0,2π],r是球体的半径。就像你在图中看到的那样,球体表面的点被绘制成矩形。我们能够很方便的把它们连成三角形。
        球面上的点位于所谓的模型坐标系。坐标相对于本地的原点定义,比如示例中球体的中心位置。如果我们想要将物体放置于场景中,我们可以定义一个从场景原点到场景中球体的原点的向量,然后把这个向量和球面上每个点的坐标相加。这样我们就将模型放置到了世界坐标系中。
        世界坐标系——将物体置于世界中
        到这儿我们的图形学之旅才真正开始。我们在某处定义一个原点,场景中的每个点都基于从原点到该点的一个向量来定义。虽然场景是3D的,我们还是得用一个4维的坐标来定义每个点[x,y,z,w],代表该点的坐标为[x/w,y/w,z/w]。这种映射称为齐次坐标。使用齐次坐标有一些好处,但是这里不做讨论。只需知道我们使用齐次坐标就够了。
        假设我们要在场景中移动,那么问题来了。如果我们要移动视线,或者移动相机到另一个位置,或者让整个世界围着相机移动。在计算机的世界里,移动整个世界更容易一点,所以我们就这样做,让相机固定不动。模型-视图矩阵(modelview matrix)是一个4x4矩阵,可以用来移动世界中的每一个点,然后让相机固定不动。这个矩阵基本上就是一系列旋转、位移、缩放的集合。我们在世界坐标系中将点和模型-视图矩阵相乘,这将使我们进入观察坐标系(viewing coordinates)。
技术分享

技术分享

我们可以改变这个矩阵以应对不同情况如正交或透视。透视图里有一个消失的点,正交视图没有。通常在绘画里见到的是透视图,正交视图在技术图中中较为多见。因为这个矩阵决定了物体是如何投影到屏幕上的,所以也叫做投影矩阵。t,b,l,r,n,f代表顶部、底部、左侧、右侧、近处、原处的裁剪面的坐标。乘以投影矩阵将使点从观察坐标系前往所谓的裁剪坐标系(clip coordinates)。
技术分享
裁剪坐标系——只绘制能看到的
 
        这个坐标系有点不同,因为它是左手坐标系(在此之前我们一直使用的右手坐标系),而且是从我们之前定义的视锥体映射到一个x,y,z范围都在(-1,1)之间的正方体。
        到现在为止,我们一直追踪场景中的所有点。然而,一旦进入裁剪空间,我们就可以开始裁掉一部分了。还记得坐标从4D到3D的转换吗?我们曾说过,[x,y,z,w]4D=[x/w,y/w,z/w]3D。因为我们只需要位于视锥范围以内的点,我们接下来只需处理符合?1≤x/w≤1或?w≤x≤w的点即可。y和z坐标也一样。这是一个简单的办法分辨一个点是否位于我们视野之内。
        如果某些点位于视锥体内,我们对它们执行透视出发(perspective divide),对每个坐标除以w来将其从4D坐标转换成3D坐标。这些点还是位于左手裁剪坐标系中,但是到了这个阶段,我们称其为规格化设备坐标(normalized device coordinates)。
规格化设备坐标系——计算遮挡关系
 
        你可以把这个想成映射到图像的中间步骤。想象一下所有可能的图像大小,我们并不想渲染成一张图片然后进行各种缩放或拉伸或当图像大小发生改变时重新渲染。规格化设备坐标(NDC)很有用,因为无论图片最终大小是多少,你可以在NDC里面针对性的进行合适的缩放。在NDC里你将看到图片如何被构造。被渲染的图像是视锥体里的物体在近裁剪面上的投影。因此,一个点在Z轴上的值越小,这个点就越近。
        这个阶段,通常我们不再进行矩阵计算,而是应用一个视窗变换。这通常只是拉伸坐标来适应视窗,或最终图像大小。最后一步是通过转换坐标到窗口坐标来绘制图像。
窗口坐标系——将物体缩放到画布
 
        窗口是图像最终被绘制的地方。在这里,我们的3D世界呈现为近裁剪面上的一张2D图像。我们可以使用一系列的线条和多边形算法来绘制最终图像。此时,一些2D效果,如抗锯齿和多边形裁剪,在图片被被绘制之前执行。
        然而,窗口可能有不同的坐标系统。比如,有时图片基于向右为X+,向下为Y+绘制。为了正确绘制图片,有时候可能需要做一些转换。
又回来了——图形渲染管线
 
        上述步骤你不用都亲历亲为。某种程度上,你会使用图形渲染库来定义诸如模型视图矩阵、投影矩阵以及世界坐标系中的多边形之类的东西,渲染库会搞定一切。如果你在设计一个游戏,你不需要在意多边形是如何被绘制的,只需确保它们执行的正确又快速,对吗?
        OpenGL和DirectX之类的库效率很高,而且能够有效利用精密的图形硬件来简单又快速的执行这些计算。它们广泛使用,所以最好适应它们。它们还给你留下很大空间来自定义一些事情,你会为你能做到的某些事感到惊讶的!
结论
        这是一个关于图形学理论的简单概览。渲染过程中后续还有很多步骤发生,但是这应该能给你一个大致的方向,让你在阅读其它文章或论坛里的相关技术时能够理解的更好。
外部链接
如果你对文章内容感兴趣,我推荐以下内容:http://www.scratchapixel.com/lessons/3d-advanced-lessons/perspective-and-orthographic-projection-matrix/perspective-projection-matrix/
                                                                   http://www.songho.ca/opengl/index.html
原文标题:The Total Beginner‘s Guide to 3D Graphics Theory
原文链接:http://www.gamedev.net/page/resources/_/technical/graphics-programming-and-theory/the-total-beginners-guide-to-3d-graphics-theory-r3402

3D图形学理论入门指南(转)