天行健 君子当自强而不息

3D图形数学(1)

新建网页 1

 

图形管道概述

我们将讨论渲染一幅带有基本光照的单个图像的大体过程,这里不考虑动画和全局光照,如阴影和辐射度。

此外,注意这里只从概念上讲解通过图形管道的数据流,其顺序并不是固定的。实践中,我们也许会为了性能的优化而并行或乱序执行一些任务。比如,考虑到不同的渲染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所示:

窗口位置由左上角像素(winPosxwinPosy给出,整数winResxwinResy是以像素为单位的窗口大小,如此定义,使用窗口大小而不是右下角的坐标,可避免整数像素坐标系带来一些麻烦。同时要注意窗口的实际物体大小和像素大小的区别。

要知道我们不一定在屏幕上渲染,也许只是将渲染结果保存到一个TGA文件里,或是AVI的一帧,也许只是渲染到一个纹理上---作为主渲染器的一个子过程而已,因此,名词"帧缓冲"一般指用来保存我们正渲染图像的那块内存。

 

像素纵横比

不管是渲染到屏幕还是缓冲区,我们必须知道像素的纵横比。它是像素高对宽的比值,一般为1("方形"像素),不过并非总是如此。下面给出其计算公式(公式5.1):

pixPhys指像素物理尺寸。一般来说,度量单位并无关系,比例才是重要的。devPhys是显示设备的物理高与宽比,尺寸可能是英寸、英尺、picas等,但也只有比例才是重要的。比如,标准的桌面显示器,尺寸各异但却拥有相同的比值4:3---视区宽大于高约33%。另一个常见比例是高清晰电视和DVD上的16:9。整数devResxdevResy是x、y方向的像素比,如640 x 480devResx=640,devResy=480。

如前所述,比值为1的方形像素最为常见。如标准桌面显示器,有4:3的物理纵横比,而许多常见解析度:320 x 240640 x 480800 x 600,1024x 7681600 x 1200也都是4:3,因此像素是方形的。

注意计算中未用到窗口的尺寸及位置,这是合理的,窗口性质不影响像素的物理属性。但是,窗口尺寸在视场问题中十分重要,而位置对摄像机到屏幕的映射是关键。

 

是摄像机可见的空间体积,看上去像截掉顶部的金字塔,如图15.2所示:

视锥是由6个裁剪面围成的。构成视锥的4个侧面称为上、左、下、右面,它们对应着输出窗口的四边。为防止物体离摄像机过近,设置近剪面,从而去除金字塔形的顶端。同理,也设置了视野的远端,因为太远的物体实际上太小而不可见,故可有效而安全地去掉。

 

视场与缩放

摄像机同其他物体一样有位置和朝向,同时它还具有"视场"这一额外的属性。另一名词"缩放"你也许已经很熟悉,直观上,你早就知道放大和缩小。但拉近时,物体显大;拉远时,物体显小,这太常见了。

视场是视锥所截的角。实际上需要两个角:分别对应水平视场和垂直视场。这里只在2D中讨论其中一个,图15.3从上方显示了视锥,精确的展示了水平视场角,坐标轴的标记用的是摄像机空间。

缩放表示物体实际大小和物体在90视场中显示大小的比。所以大比值表示放大,小比值表示缩小。比如,2.0的缩放表示物体在屏幕上比用90视场时大两倍。缩放的几何解释如图15.4所示:

应用基本三角知识,就能推导出缩放和视场角之间的转换公式:

在3D中,需要两个缩放值,一个水平的,一个垂直的。可以随意给值,但如果二者比例不恰当,图像便像被拉伸过似的(好比宽银幕电影在电视上播出)。为了维持恰当的比例,缩放要和输出窗口的尺寸对应:

假设输出为正常比例,许多渲染引擎允许仅用一个视场角(或zoom)设定摄像机,然后自动计算另一个。例如,可以指定水平视场角,自动计算垂直视场角,反之亦然;或者指定视场角中较大的一个,自动计算较小的。

posted on 2008-03-04 19:47 lovedday 阅读(2287) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论