天行健 君子当自强而不息

3D图形数学(7)

几何体的生成与提交

 

几何体的生成与提交

一旦知道哪些物体可见(或至少潜在可见),即可将其生成并提交到图形处理器。该阶段完成以下任务:

(1)细节层次(LOD)选择

(2)渐进式生成几何体

(3)向图形API提交数据

 

LOD选择和渐进式生成

一般我们希望以最大可能的三角形数量描绘物体以求得最佳的视觉效果,但不幸的是,较多的三角形一般意味着低帧率。我们必须在可接受的外表和帧率间做出折中选择。LOD在一定程度上可两全其美,基本思路是离摄像机远的物体只使用较少的多边形,此时并不降低视觉效果。

如何得到三角形数量较少的三角网?一种简单方法(从程序员角度)是让美工直接制作一个,然后根据物体离摄像机的远近(或屏幕分辨率大小)选用合适的LOD。问题是就在它由远及近改变的那一刻,这种方法会有一种跳动效果,当然我们希望把这种视觉上的不连续性降低到最低限度----好的三角网也许会有更大帮助。

一种克服跳动的方法是引入连续LOD。这种系统中,不同级别LOD包含的三角形数目几乎是连续的,我们可以产生任意多三角形的网格。渐进式网格技术就是一种这样的网格消减技术,但需要注意生成连续LOD的开支可能会很显著。而使用离散LOD,网格是现成可用的,渲染时可立即投送,我们所要做的就是决定用哪个网格。所以,即使实际的网格是用网格的消减技术生成的,离散LOD还是在实践中经常使用。

有时候几何体并非由美工创建,而是由计算机生成,这称为程序建模。分形地形图是程序建模的好例子,植物也可以自动创建,有时LOD也用在此类建模算法中。

 

向API投送几何体

多数API希望某种形式的三角网格输入,如单个三角形,索引三角网格,三角带或三角扇等。无论哪种形式,数据的核心都是顶点,三角形不过是顶点合适的连接方式。从另一方面说,API并不需要超过三角形级别的数据。

API根据操作的不同接受不同的数据格式。(当我们说“API如何时”,是指整个图形子系统,不论操作是由软件完成还是硬件完成的。)

在简化的情况下,顶点的数据一般分为三类:

(1)位置:描述顶点的位置,可以是3D向量或者有深度信息的2D屏幕坐标。如果采用3D向量,还需要用模型、视图变换做向屏幕映射的工作。另一个骨骼动画中使用的高级技术是skinning,顶点坐标由若干骨骼给出。

(2)光照和雾化:为了渲染,顶点一般都带有色彩值,然后由这些值插值计算三角形中各点的颜色。我们可以指定这些值,或者让API计算合适的光照值。如果让API计算光照,通常要给出顶点法向量。无论如何,颜色均为RGBalpha的元组。如果直接指定颜色,经常使用一个32位的ARGB值,每分量8位,或者为每个分量使用一个单独的值。如使用硬件雾化,还要指定各点的雾化强度,可以手动指定这个值,也可由API计算。

(3)纹理映射坐标:使用纹理映射时,每个顶点必须要有纹理映射坐标。最简单的情形下,只需要纹理图的2D坐标,常记为(u, v)。当使用多重纹理时,每个纹理都需要一个坐标。有时,可以阶段式生成纹理坐标(如向表面投射一道光线)。或者,可以阶段式地拷贝纹理坐标。在这种情况下,就可以不指定纹理坐标。

简单的说,投送顶点并没有一个简单的格式。事实上,存在许多变种,如DirectX有可变顶点格式的概念,可使你自定义格式,以最方便的顺序保存任何想要的信息。

有了这些之后,让我们给出几个C++结构,记录上面提到的常用格式。

最常见的是3D坐标,表面法向量和纹理映射坐标,需要API进行光照的静态纹理映射网格常使用这种格式。

    // Untransformed, unlit vertex
   
struct RenderVertex 
    {
        Vector3 p; 
// position
   
    Vector3 n; // normal
   
    float u,v; // texture mapping coordinates
   
};

另一种常用的格式,是用来显示2D物体或HUD(head up display)的,含有屏幕坐标和预定义的光照。虽然数据是2D的,但仍然带有某种形式的深度信息。

    // Transformed and lit vertex
   
struct RenderVertexTL 
    {
        Vector3 p;        
// screen space position and depth
   
    float w;        // 1/z
   
    unsigned argb;    // prelit diffuse color (8 bits per component – 0xAARRGGBB)
   
    unsigned spec;    // prelit specular color
   
    float u,v;        // texture mapping coordinates
   
};

最后一个例子是某种3D顶点,但不需要图形API的光照引擎照亮,它自带预定义的光照。这种格式经常用于特效,如爆炸、火焰、发光物等,以及调试用物体如包围盒、路点、标记等。

    // Untransformed, lit vertex
   
struct RenderVertexL
    {
        Vector3 p;        
// position
   
    unsigned argb;    // prelit color (8 bits per component – 0xAARRGGBB)
   
    unsigned spec;    // prelit specular color
   
    float u,v;        // texture mapping coordinates
   
};

变换和光照

网格被提交到API之后,接下来的操作就是变换与光照(经常用T&L表示),图形管道的该阶段其实包含大量顶点级别的计算。基本上,所有顶点级别的计算都可以在本阶段进行,但最常见的操作有:

(1)物体空间顶点位置变换到裁剪空间

(2)使用光照设置及法向量计算光照

(3)根据顶点位置计算顶点级雾浓度

(4)阶段式产生纹理映射坐标

(5)在骨骼动画中,用skinning技术计算顶点值

当然,根据不同的渲染上下文和提交的数据类型,某些操作不会执行。

当前图形API给予T&L阶段完全的灵活性。自第八版开始,DirectX支持顶点着色,其实就是运行在硬件上的小段代码。这些代码操作单个顶点,接受几何提交阶段发送来的任意多输入,并产生任意多输出到裁剪/光栅化阶段。典型的输入如顶点位置、法向量、光照前的颜色、纹理映射坐标等。可能的输出包括顶点坐标转换(摄像机空间或裁剪空间),Gourand着色,纹理坐标,雾浓度等。经常,输入只是简单地通过顶点着色,并映射成合适的输出(如纹理映射坐标,预计算的光照),或顶点着色执行一些运算产生全新的输出,如变换顶点位置、雾浓度、动态光照、或阶段式生成纹理映射坐标。

 

变换到裁剪空间

模型空间到裁剪空间的转换常以矩阵乘法实现。概念上,顶点经过一系列变换,如下所示:

(1)模型转换到世界空间

(2)视图变换将世界空间转换到摄像机空间

(3)摄像机空间转换到裁剪空间

乘法顺序如下:

vclip = vmodel(Mmodel->world)(Mworld->camera)(Mcamera->clip)

实现中并没有做三步乘法,实际上,变换矩阵是连接好的,顶点的变换不需要做三次矩阵乘法。根据硬件的设计和光照方法,可以将所有矩阵连接成两个或一个矩阵。如果能够访问T&L硬件(如顶点着色),则可以直接施加精确的控制。如果不能,就必须依赖API让它作所有的优化。

 

顶点光照

理想的情况应该使用Phong着色,先对表面法向量插值而后像素点计算光照。实际上,我们却不得不多用Gourand着色,先计算顶点的光照而后插值生成多边形中各点的光照。

当在顶点级计算光照时,无法直接用公式15.14,因为mdiff不是一个顶点级材质属性,通常是由纹理定义这个值。为了使公式15.14更适合插值,必须进行变换以分离mdiff,同时,可以假设mamb等于mdiff

用上面的光照方程,就可以在顶点级插值计算光照。对于每个顶点,我们计算两个值,vdiffvspecvdiff包含公式15.16的环境与散射分量,vspec包含镜面分量:

上述值都是逐顶点计算,然后对整个三角形插值。于是对每个像素,光照公式如下:

如前所述,mspec经常为常量,但也可以用光泽图定义。

应使用哪个坐标空间计算光照?可以在世界空间内进行。此时,顶点坐标、法向量都要转换到世界空间,以进行光照计算,接着顶点坐标转至裁剪空间。或者,可以将光照放到模型空间中计算,因为光照总比顶点较少,结果是总体减少了向量----矩阵乘法计算。第三种可能是在摄像机空间内计算。如果你不通过顶点着色直接控制T&L管道,API会为你做出这种选择。

posted on 2008-03-11 08:58 lovedday 阅读(1326) 评论(0)  编辑 收藏 引用


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


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论