3d图形数学.docx
- 文档编号:24222525
- 上传时间:2023-05-25
- 格式:DOCX
- 页数:37
- 大小:1.22MB
3d图形数学.docx
《3d图形数学.docx》由会员分享,可在线阅读,更多相关《3d图形数学.docx(37页珍藏版)》请在冰豆网上搜索。
3d图形数学
图形管道概述
我们将讨论渲染一幅带有基本光照的单个图像的大体过程,这里不考虑动画和全局光照,如阴影和辐射度。
此外,注意这里只从概念上讲解通过图形管道的数据流,其顺序并不是固定的。
实践中,我们也许会为了性能的优化而并行或乱序执行一些任务。
比如,考虑到不同的渲染API,我们可能首先变换和照明所有顶点,然后才进一步的处理(进行裁剪和剔除),或者会并行处理二者,也可能在背面剔除之后再进行光照会得到更高效率。
还有一个我们将不详细讨论的要点,即工作负担如何在CPU与渲染硬件间分配。
正确地组织渲染任务,以求得最大的并行效果对高效渲染是至关重要的。
考虑上述简化,就得到了图形管道中数据流的概况,如下所示:
(1)建立场景:
开始渲染之前,需要预先设定对整个场景有效的一些选项。
比如,要建立摄像机位置,或者更具体些,要选择进行渲染的出发点---视点,渲染的输出---视图。
还需要设定光照与雾化选项,同时准备z缓冲。
(2)可见性检测:
选好了摄像机,就必须检测场景中哪些物体是可见的。
可见性检测对实时渲染极为重要,因为我们不愿意浪费时间去渲染那些根本看不到的东西。
(3)设置物体级的渲染状态:
一旦发现某物体潜在可见,就到了把它实际绘制出来的时候。
每个物体的渲染设置可能是不同的,在渲染该物体的任何片元之前,首先要设置上述选项,最常见的此类选项是纹理映射。
(4)几何体的生成与提交:
接着实际向API提交几何体,通常提交的数据是种种形式的三角形,或是独立的三角形,或是索引三角网格与三角带。
此阶段,我们可能会应用LOD,或者渐进式生成几何体。
(5)变换与光照:
一旦渲染API得到了三角形数据,由模型空间向摄像机空间的顶点坐标转换与顶点光照计算即开始。
(6)背面剔除与裁剪:
然后,那些背对摄像机的三角形被去除("背面剔除");三角形在视椎外的部分也被去除,称作裁剪---这可能导致产生多于三个边的多边形。
(7)投影到屏幕空间:
在3D裁剪空间中经裁剪产生的多边形,被投影到输出窗口的2D屏幕空间里。
(8)光栅化:
当把裁剪后的多边形转换到屏幕空间后,就到了光栅化阶段。
光栅化指计算应绘制三角形上的哪些像素的过程,并为接下来的像素着色阶段提供合理的插值参数(如光照和纹理映射坐标)。
(9)像素着色:
最后,在管道的最后阶段计算三角形的色彩,此过程称作"着色"。
接着把这些颜色写至屏幕,这是可能需要alpha混合与z缓冲。
下面的伪代码描述了渲染管道,为了达到概观的目的,大量细节被省去了。
同时,由于渲染平台和API的不同,实践中会有许多不同的形式。
Listing 15.1:
Pseudocode for the graphics pipeline
// First, figure how to view the scene
setupTheCamera();
// Clear the zbuffer
clearZBuffer();
// Setup environmental lighting and fog
setGlobalLightingAndFog();
// Get a list of objects that are potentially visible
potentiallyVisibleObjectList = highLevelVisibilityDetermination(scene);
// Render everything we found to be potentially visible
for (all objects in potentiallyVisibleObjectList)
{
// Perform lower-level VSD using bounding volume test
if (!
object.isBoundingVolumeVisible())
continue;
// Fetch or procedurally generate the geometry
triMesh = object.getGeometry()
// Clip and render the faces
for (each triangle in the geometry)
{
// Transform the vertices to clip space, and perform vertex-level lighting
clipSpaceTriangle = transformAndLighting(triangle);
// Is the triangle backfacing?
if (clipSpaceTriangle.isBackFacing()) continue;
// Clip the triangle to the view volume
clippedTriangle = clipToViewVolume(clipSpaceTriangle);
if (clippedTriangle.isEmpty())
continue;
// Project the triangle onto screen space and rasterize
clippedTriangle.projectToScreenSpace();
for (each pixel in the triangle)
{
// Interpolate color, zbuffer value, and texture mapping coords
// Perform zbuffering and alpha test
if (!
zbufferTest())
continue;
if (!
alphaTest())
continue;
// Shade the pixel.
color = shadePixel();
// Write to the frame buffer and zbuffer
writePixel(color, interpolatedZ);
}
}
}
设定视图参数
渲染场景之前,首先必须建立摄像机和输出窗口。
即必须决定从哪个位置进行观察渲染(视点位置、方向、缩放)以及把渲染结果送到哪里(屏幕上的目标矩形区域)。
上述二者中,输出窗口较为简单,故先讨论输出窗口。
指定输出窗口
我们不一定要把图像渲染到整个屏幕。
比如,一个分屏的多人游戏,每个玩家只占据显示屏幕的一部分。
输出窗口即指输出设备中图像将要渲染到的那部分,如图15.1所示:
窗口位置由左上角像素(winPosx,winPosy)给出,整数winResx、winResy是以像素为单位的窗口大小,如此定义,使用窗口大小而不是右下角的坐标,可避免整数像素坐标系带来一些麻烦。
同时要注意窗口的实际物体大小和像素大小的区别。
要知道我们不一定在屏幕上渲染,也许只是将渲染结果保存到一个TGA文件里,或是AVI的一帧,也许只是渲染到一个纹理上---作为主渲染器的一个子过程而已,因此,名词"帧缓冲"一般指用来保存我们正渲染图像的那块内存。
像素纵横比
不管是渲染到屏幕还是缓冲区,我们必须知道像素的纵横比。
它是像素高对宽的比值,一般为1("方形"像素),不过并非总是如此。
下面给出其计算公式(公式5.1):
pixPhys指像素物理尺寸。
一般来说,度量单位并无关系,比例才是重要的。
devPhys是显示设备的物理高与宽比,尺寸可能是英寸、英尺、picas等,但也只有比例才是重要的。
比如,标准的桌面显示器,尺寸各异但却拥有相同的比值4:
3---视区宽大于高约33%。
另一个常见比例是高清晰电视和DVD上的16:
9。
整数devResx和devResy是x、y方向的像素比,如640x480指devResx=640,devResy=480。
如前所述,比值为1的方形像素最为常见。
如标准桌面显示器,有4:
3的物理纵横比,而许多常见解析度:
320x240,640x480,800x600,1024x768,1600x1200也都是4:
3,因此像素是方形的。
注意计算中未用到窗口的尺寸及位置,这是合理的,窗口性质不影响像素的物理属性。
但是,窗口尺寸在视场问题中十分重要,而位置对摄像机到屏幕的映射是关键。
视锥
视锥是摄像机可见的空间体积,看上去像截掉顶部的金字塔,如图15.2所示:
视锥是由6个裁剪面围成的。
构成视锥的4个侧面称为上、左、下、右面,它们对应着输出窗口的四边。
为防止物体离摄像机过近,设置近剪面,从而去除金字塔形的顶端。
同理,也设置了视野的远端,因为太远的物体实际上太小而不可见,故可有效而安全地去掉。
视场与缩放
摄像机同其他物体一样有位置和朝向,同时它还具有"视场"这一额外的属性。
另一名词"缩放"你也许已经很熟悉,直观上,你早就知道放大和缩小。
但拉近时,物体显大;拉远时,物体显小,这太常见了。
视场是视锥所截的角。
实际上需要两个角:
分别对应水平视场和垂直视场。
这里只在2D中讨论其中一个,图15.3从上方显示了视锥,精确的展示了水平视场角,坐标轴的标记用的是摄像机空间。
缩放表示物体实际大小和物体在90。
视场中显示大小的比。
所以大比值表示放大,小比值表示缩小。
比如,2.0的缩放表示物体在屏幕上比用90。
视场时大两倍。
缩放的几何解释如图15.4所示:
应用基本三角知识,就能推导出缩放和视场角之间的转换公式:
在3D中,需要两个缩放值,一个水平的,一个垂直的。
可以随意给值,但如果二者比例不恰当,图像便像被拉伸过似的(好比宽银幕电影在电视上播出)。
为了维持恰当的比例,缩放要和输出窗口的尺寸对应:
假设输出为正常比例,许多渲染引擎允许仅用一个视场角(或zoom值)设定摄像机,然后自动计算另一个。
例如,可以指定水平视场角,自动计算垂直视场角,反之亦然;或者指定视场角中较大的一个,自动计算较小的。
模型与世界空间
物体最开始由物体空间(和物体相连的坐标空间)来描述,其中常见的信息包括顶点位置和表面法向量。
物体空间又称作模型空间或局部空间。
可将坐标从模型空间中转换到世界空间中,此过程称作模型变换。
通常,光照计算使用世界空间,但其实使用什么坐标空间无所谓,只要确保几何体与光线在同一空间即可。
摄像机空间
通过视变换,顶点从世界空间变换到摄像机空间,此空间也称作眼睛空间。
摄像机空间是原点在投影中心的3D坐标系统,一个轴平行于摄像机拍摄方向且垂直于投影平面,另一个轴由上、下裁剪面相交得到,还有一轴由左、右裁剪面相交得到。
如果我们考虑的是透视投影,那么一个轴可视为水平,另一个则可视为垂直的。
左手坐标系中,常约定摄像机朝向+z,而+x和+y指向右和上方向(透视投影情况下)。
这是非常直观的,如图15.5所示。
右手坐标则指定-z为摄像机朝向。
裁剪空间
从摄像机空间,顶点接着又被变换到裁剪空间,又名标准视体空间(thecanonicalviewvolumespace),该变换对应的矩阵称为裁剪矩阵。
目前为止,顶点还是"纯粹"的3D向量,即它们只有三个坐标值,或者加上第四个分量w,并且w总为1。
裁剪矩阵改变了这个现状,它将有用信息放入w中,它主要做两件事:
(1)为透视投影准备向量,由除以w来实现。
(2)规格化x,y,z,使它们可以w比较,用于裁剪。
裁剪矩阵的第一个目的是为透视投影准备向量而将坐标值除以w,4D齐次向量由除以w而对应到3D向量:
裁剪矩阵的一个目的就是计算正确的w值,以得到正确的投影,我们已经知道如何投影到垂直于z轴且距原点为d的平面(形如z=d的平面)。
投影平面在视锥内的矩形部分将映射到屏幕,如果改变d,投影平面将前后移动;在一个真正的摄像机中,这样变化焦距将产生放大、缩小的效果。
但对计算机内的投影平面不会如此,增大焦距,像也会变大,但是"底片"(就是投影平面在视锥内的部分)也变大了。
因为它们变化的比例一致,所以渲染出的图像不变。
因此,计算机图形学中,缩放完全由视锥的形状空间,d值并不重要。
所以,我们可以任意选择一个d值并一直使用它,对我们来说最方便的值是d=1。
如果这是裁剪矩阵唯一的目的,即计算正确的w值,那么它可简化如下:
将它乘以形如[x,y,z,1]的向量再进行透视除法,得到:
现在已经知道如何用矩阵求得w的值。
这里,你也许发现似乎只要除以z就可完成上述工作。
没错,的确可以只用z而不涉及w,但4D坐标可以表达更多的摄像机要求,包括一些"奇异"的形式,比如投影面不垂直于摄像机指向;另一个原因是它使得z裁剪(近面和远面裁剪)和x、y裁剪形式一致,从而更好地使用硬件。
一般来说,使用齐次坐标4x4矩阵更紧凑和优雅。
无论如何,多数API都使用它,这才是最重要的。
裁剪矩阵的另一个目的是规格化x、y、z分量,使得6个裁剪面有一致的简单形式。
符合下列简单不等式的点在视锥外:
bottom y<-w
top y>w
left x<-w
right x>w
near z<-w
far z>w
公式15.3 裁剪空间中的视锥
反之,视锥内的定满足下列不等式:
-w≤x≤w
-w≤y≤w
-w≤z≤w
任何不满足这些不等式的点都要被裁减掉。
我们用摄像机的缩放值对x、y进行缩放,从而使上、左、右、下4个剪切面处于正确位置。
对于近、远两个剪切面,使得对近剪切面z/w=-1,远剪切面z/w=1。
设zoomx、zoomy分别为水平、垂直缩放值,设n、f分别为近、远两个剪切面的距离。
下面的矩阵可完成上述计算:
所谓"openGL风格",是指近裁剪面到远裁剪面的z值在[-w,+w]之间,其他API(如DirextX)调整z值到区间[0,w]。
换言之,如果满足下式,那么点在裁剪面外:
near z<0
far z>w
而在视锥以内的点则满足:
0 ≤z≤w
此时剪切矩阵稍有不同(公式15.5):
屏幕空间
一旦用视锥完成了几何体裁剪,即可向屏幕空间投影,从而对应于真正的屏幕像素。
注意输出窗口不一定占有整个屏幕,只不过,通常情况下希望屏幕坐标系和渲染设备坐标系一致。
显然,屏幕空间是2D的,于是要进行一次3D到2D的映射以得到正确的2D坐标。
下列公式概括这一过程:
除以w,并调整x、y以映射到如图15.6所示的输出窗口:
注意y前面的负号,因为裁剪空间中+y向上,而屏幕空间的+y向下。
zscreen和wscreen呢?
因为屏幕是2D的,它们并无意义。
但也不能简单地丢弃它们,在z缓冲和透视校正中,它们还会有用。
光照与雾化
标准光照模型是局部模型中的一种----即当处理一个物体时,不考虑其他物体的影响。
物体也不向别的物体投下影子,实际上,物体自身无法生成影子,影子是使用全局光照模型生成的。
色彩的数学
计算机中的色彩常用RGB色彩模型表示,这里R表示红,G表示绿,B表示蓝,其精度因平台与渲染状态而异。
我们视RGB为0至1间的值,并且也不考虑其中各分量到底占用多少二进制位。
在计算机图形学中,色彩常被视为数学实体。
我们用黑体的小写罗马字母表示色彩符号,如c,与向量符号相同,但由于二者上下文不同,所以不会混淆。
可以认为色彩存在于一个3D单位立方体"色彩空间"中,如图15.7所示:
黑、白两色十分重要,分别由RGB(0,0,0)和(1,1,1)表示,并且用特殊的符号0、1分别标识,灰色都在黑白色之间的那条连线上。
色彩可以加、减、乘以标量,和向量运算一样。
两个色彩也可以作"按位乘"运算,运算记作
。
色彩运算有时会使一个或多个RGB分量超出[0,1]的界限(比如,计算光照时,出现极强的光亮),此时简单地加以截断。
根据情况,也许在计算中每一步都进行截断,也许中间允许超界,而在最后截断。
如果只有一个分量超界,截断运算可能会引起色彩混乱。
比如,色彩(1,2,1)其实是(0.5,1,0.5)的一个更亮的表示,类似某种绿色。
简单地加以截断后,绿色消失只剩余白色。
更聪明的办法是除以最大色彩值以单位化。
此例中,均除以最大分量值2。
当然,开始时就防止越界(比如,调整光强以使越界不发生)才是最好的办法。
光源
为进行渲染,必须向图形API描述场景中的光。
下面是一个光源类型的简单列表:
点光源,平行光,聚光灯,环境光。
我们将讨论大多数渲染API都支持的常见光源。
点光源是向四面八方发射光线的单点,又称全向光或球状光。
点光源有方向和色彩,此色彩同时表示色调与亮度,点光源还有一个辐射衰减半径,控制照亮的范围。
图15.8展示了3DS如何表示点光源。
光强通常由光源中心向辐射前进方向不断衰减,最终为0。
点光源可代表许多常见发光物,如灯泡、电灯、火把等。
平行光是从无限远处射来的点光源的光线,场景中所有光线皆为平行的。
平行光源没有位置的概念,也无衰减。
太阳是平行光的典型代表(目前还不考虑太阳的位置来渲染场景)。
聚光灯指从特定光源向特定方向射出的光,比如信号灯、车头灯等。
它们有位置、方向、甚至还有辐射距离的概念,其照亮区域为圆锥形或金字塔形。
圆形聚光灯有一个圆形的"底",其宽度由辐射衰减角给出(注意与辐射衰减距离的区别)。
并且,有一个描述高亮区的内角度,如图15.9:
方形聚光灯形成金字塔形状,而不是圆锥形。
方形聚光灯非常有趣,因为常被用来投影图像,比如电影放映。
最后,环境光指不属于任何光源而照亮整个场景的光。
不考虑环境光,则物体的影子将完全是黑的,因为它们不被任何光源照亮。
现实中,这类物体常被间接照亮,环境光是积累这种间接光照最根本的方法。
标准光照方程----概述
光照方程定义了标准光照模型,用于计算单个像素的色彩。
标准方程可简写为公式15.7:
Clit=Cspec+Cdiff+Camb
公式15.7 标准光照方程
其中,
(1)Clit是打开光照情况下计算颜色值的结果,这与关闭光照(注意:
相当于用最强光照照射)情况下计算的结果不同。
和我们日常生活中的"光照"一词不同,计算机图形学中的"光照"是指取关闭光照情况下的纹理颜色值进行计算,一般情况下得到的结果比原纹理颜色值暗。
(2)Cspec是镜面反射分量。
(3)Cdiff是散射分量。
(4)Camb是环境分量。
物体外观主要取决于以下四点因素:
(1)物体表面的性质,即材质属性。
(2)表面的方位与朝向,朝向常用单位法向量表示。
(3)照射来的各光源性质。
(4)观察者位置。
方程中的三个组成部分,分别考虑了上述因子的不同组合。
镜面反射分量
标准光照方程的镜面反射分量指由光源直接经物体表面反射入眼睛的光线,如图15.10:
n为表面法向量
v指向观察者
l指向光源,对方向光源,l为定值
r为镜像向量,即l对n镜像之结果
θ为r和v的夹角,由r.v给出,描述镜像的方向性。
镜面反射使物体看上去有光泽,粗糙表面因反射率不高,所以缺乏此类效果。
镜面反射的强度取决于物体、光源和观察者。
所有向量均为单位向量,如图15.11所示,r由2(n.l)n-l给出。
下列等式给出镜面反射的Phong模型:
mgls为材料的光泽度,也称作Phong指数,它控制亮斑的范围,小的mgls带来大而平滑的光斑,大的mgls带来小而亮的光斑。
完全的反射面,如玻璃,有非常大的mgls----只有反射光进入眼睛,不完全光反射面,如苹果的表面,有较大的亮斑。
另一个有关亮度的值为mspec,即材料的反射颜色,对整个材料来说一般是一个不变的灰度值。
mgls控制光斑的大小,mspec控制光斑的强度。
强反射面有大的mspec值,粗糙些的表面则有较小的mspec值。
如果你愿意,可用一个光泽图控制物体的反射,如同纹理控制物体颜色一样。
sspec是光源的镜面反射颜色,控制光本身的色彩与强度。
对于方形聚光灯,此值可能来自投影光照面。
sspec常等于光的漫反射颜色sdiff。
图15.12显示了mgls和mspec如何影响物体的镜面反射。
图中,mspec从最左列到最右列由黑至白变化,而指数mgls在上方第一行最大,向下递减。
注意到,最左面一列的头像看上去一样,因为镜面反射强度为0,对光照没有任何贡献。
如果观察者离物体的距离远大于物体的尺寸,可以仅计算v一次,然后认为它对整个物体是一个常量,同样道理对光源和l也适用。
(其实对平行光源,l本身就是固定的。
)但由于n是变化的,仍需计算r----这是一个应该尽量避免的计算。
Blinn模型通过计算一个稍微不同的角度来避免这个计算。
如图15.13所示:
Blinn模型使用h,表示v、l的中间量,由标准化v、l的平均值求得:
h=(v+l)/||v+l||
公式15.9 Blinn模型中间向量h的计算
Blinn模型和Phong模型一样,只不过θ表示n与h的夹角:
此方程便于硬件的实现,特别是当光源与观察者均远离物体时,此时h被视为常数仅需计算一次。
我们忽略的一个细节是有时cosθ小于0,此时简单地令镜面反射为0即可。
漫反射分量
与镜面反射类似,漫反射分量也刻画直接照射物体的光线。
漫反射反映的是散开的随机方向上的反射,这是由物体表面的粗糙引起的,相反镜面反射则反映良好的反射。
图15.14比较了良好反射表面和粗糙表面。
漫反射不依赖于视点位置,因为它本身就是随机,光源与物体的相对位置反而显得更重要。
例如:
若假定光线射入眼睛的几率一定,则由于垂直于光线的面在单位面积受光多于一个斜射的面,因为入射眼睛的光线更多,如图15.15所示:
注意上述两种情况中,光线间的垂直距离都相等。
(由于错觉,右边的也许显得远些,但量一下,你就发觉实际情况了。
)然而,注意右面的图,其命中目标时分得更开些,那么单位面积接受的光线将少些。
从图上的示点线可看出左边受光9个单位,而右边只有6个单位,尽管二者面积一样。
这正是赤道气候比两极暖和的原因,因为地球是圆的,太阳光在赤道有较直接的照射。
漫反射光服从Lambert法则:
反射光强正比于法向量与光线夹角的余弦,用点乘计算余弦,
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 图形 数学