zhouqijie

采用深度缓冲的三角形光栅化基础

三维场景的渲染步骤:

设置虚拟场景。
设置虚拟摄像机。
设置光源。
设置物体表面的视觉特性。
着色方程。



场景描述

场景由物体组成,物体可以是不透明的(opaque),光不能透过物体。可以是透明的(transparent),光能透过物体不发生散射(scatter)。也可以是半透明的(translucent),光能透过物体但是会被散射至各个方向。

电影产业里,表面通常由一些矩形面片(patch)所组成,而每个面片则是由少量的控制点定义的三维样条构成(例如贝塞尔曲线、非均匀有理B样条、N面片)。

有些电影渲染引擎采用细分曲面(subdivision surface)定义几何形状。每个表面由控制多边形网格(如同样条)表示,但是这些多边形会使用Catmull-Clark算法逐步细分为更小的多边形。无论摄像机多近都能再细分,使得边缘线光滑。

实时渲染一般选用三角形网格,它有以下优点:

  1. 它是最简单的多边形。
  2. 它必然是平坦的。
  3. 它经过多次转换后依然是三角形。
  4. 几乎所有商用图形加速硬件都是为三角形光栅化设计的。

镶嵌是指把表面分割为一组离散多边形的过程,这些多边形通常是三角形或四边形。如果是镶嵌为三角形,就称为三角化(trianglulation)

游戏开发者通常会尝试以一串不同版本的三角形网格链去逼近此理想的三角形对像素密度,每个版本称为一个层次细节(level-of-detail, LOD)。第一个LOD称为LOD0,代表最高程度的镶嵌,在摄像机最接近时使用。后续的LOD镶嵌程度不断变低。

有些引擎会将动态镶嵌(dynamic tessellation)技术应用到可扩展的网格上,例如水面和地形。在这种技术中,网格通常以高度场(height field)表示(CRE:例如地理高程图),而高度场则在某种规则栅格模式上定义。

渐进网格(progressive mesh)是另一种动态镶嵌以及LOD技术。运用此技术时,当物体接近摄像机时采用单个最高分辨率网格(LOD0)。当物体远离摄像机时,网格自动密铺,其方法是把某些棱收缩为点。此过程能够自动生成半连续的LOD链。

绕转顺序和剔除
绕转顺序定义表面方向有两种方式,分别是顺时针(CW)逆时针(CCW)
多数底层图形API提供了基于缠绕顺序来剔除(culling)背面三角形的方法。

三角形表
定义网格的最简单方法,以每三个顶点为一组列举。

索引化三角形表
三角形表中会有很多重复的顶点数据,会浪费内存和GPU资源。所以多数渲染引擎会采用索引化三角形表(indexed triangle list)这种更有效率的数据结构。
每个顶点只列举一次,然后用16位整数索引来定义三角形。(三角形数据就分为了顶点数组和索引数组)

三角形条带和三角形扇形
有时会用到两种特殊的网格数据结构,分别为三角形条带(triangle strip)三角形扇形(triangle fan)。它们不需要索引缓冲但同时能降低顶点重复度。
GPT:灵活性不如索引化三角形。虽然减少了顶点重复,但是对于几何拓扑结构的严格要求,实际应用中很难充分利用这些方式的优势。

顶点缓存优化
三角形条带的缓存一致性(cache coherency)非常好。
索引化三角形表也可以通过顶点缓存优化器(vertex cache optimizer)来重新排列三角形次序提升缓存一致性。

三角形网格的顶点数据通常被指定在一个局部坐标系,称为模型空间(model space)或者局部空间(local space)或者物体空间(object space)

使用网格组成完整场景时,会在一个共同坐标系放置多个网格,此坐标系称为世界空间(world space)

每个网格可在场景中多次出现,每个这样的物体称为网格实例(mesh instance)。每个网格实例包含共享网格数据的引用,此外也包含一个变换矩阵,用来把个别实例的顶点从模型空间转换到世界空间。



描述表面的视觉性质

物体表面视觉性质通常包含:法线(normal)、漫反射颜色(diffuse color)、光滑度(smoothness)、反射率(reflectivity)、折射率(refractive index)、透明度等。

光的颜色是由其强度(intensity)以及波长决定的。

光和物体交互

  1. 光可被吸收(absorb)
  2. 光可被反射(reflect)
  3. 光可以在物体中传播(transmit),过程中通常会被折射(refract)
  4. 通过很窄的缺口时,会被衍射(diffract)

多数写实渲染引擎会处理前三项行为,而衍射通常会被忽略,应为多数场景中衍射效果不明显。

光的反射可以是漫反射(diffuse),这是指入射光会往所有方向平均散射。反射也可以是镜面反射(specular),这是指入射光会直接被反射,或者反射时展开成很窄的锥形。

当光穿过物体时候,可能会被散射(例如半透明物质),部分被吸收(例如彩色玻璃),或者被折射(例如三棱镜)。

光也能进入半固态表面,在表面下面反弹再从另一个位置离开表面,称为次表面散射(subsurface scattering,SSS)(例如皮肤、蜡等物质)。

颜色空间和颜色模型

颜色模型是测量颜色三维坐标系统。计算机中最常用的颜色模型是RGB模型。

当颜色存储于位图时,可使用多种不同的颜色格式(color format)(例如RGB565和RGB888等)。

不透明度和alpha通道:

常会在RGB颜色矢量之后再补上一个名为alpha的通道。用来度量物体的不透明度。

RGB颜色格式扩展包含alpha通道后,就称为RGBA或者ARGB颜色格式。

要描述表面的视觉特性,最简单的方法就是把这些特性记录在表面的离散点上。网格的顶点是存储表面特性的便利位置,这种存储方式称为顶点属性(vertex attribute)

在一个典型三角形网格中,每个顶点通常可能包含的属性:

  1. 位置向量。
  2. 法向量。
  3. 切向量。(和法向量和副切向量共同定义切线空间,用于计算多种逐像素光照例如法线贴图和环境贴图)
  4. 漫反射颜色。
  5. 镜面颜色。
  6. 纹理坐标。
  7. 蒙皮权重。

顶点属性通常存储于C struct或者C++ class的数据结构中。这样的数据结构的布局称为顶点格式

struct Vertex
{
    Vector3 position;//位置向量
    Vector3 normal;//法向量
    Color diffuse;//漫反射颜色
    Vector2 uv0;//纹理坐标0
    Vector2 uv1;//纹理坐标1
    U8 k[4];//关节索引(4个)  
    F32 weight[3];//关节权重(第4个由其他3个求得)
}

渲染三角形时,三角形的内点要最终成为屏幕上的像素,也就是说我们需要逐像素的属性,而非逐顶点属性。

要取得网格表面的逐像素属性,最简单的方法是对顶点属性进行线性插值(linear interpolation, Lerp)

当把线性插值施于顶点颜色时,这就是高氏着色法(Gouraud Shading)

如果三角形比较大,以逐顶点方式可能会太过粗糙。有时也会引发一些视觉问题(比如高光如果用逐顶点就会出现问题)。

要克服顶点表面属性的限制,可以使用纹理贴图。纹理中的每个单独像素称为纹素(texel),用来和屏幕的“像素”进行区分。

常见的纹理种类有漫反射贴图、法线贴图、光泽贴图、环境贴图、AO贴图、置换贴图等。

纹理坐标
纹理坐标通常使用两个归一化的数值(u,v)表示。这些坐标的范围从左下角(0,0)到右上角(1,1)。

纹理寻址模式
如果纹理再0~1范围之外,可以用如下几种方式处理:

  1. 缠绕模式(wrap mode)。(纹理在各个方向上不断重复)
  2. 镜像模式(mirror mode)。类似缠绕,但是再奇数倍的纹理会形成镜像。
  3. 截取模式(clamp mode)。边缘延申。
  4. ※边缘颜色模式(border color mode)。用户指定一个颜色,在0~1之外使用。

纹理格式

常见的格式有TGA、PBG、BMP、TIFF等。
多数显卡及图形API都支持压缩纹理,例如DirectX的DXT或S3纹理。压缩纹理使用更少的内存而且渲染也更高效。

纹理密度和多级渐远纹理

当一个屏幕像素对应一个纹素时,我们称纹素密度为1。

当纹素密度远小于1时,每个纹素就会显著地比屏幕像素大,破坏游戏的真实感。(CRE:看到明显的马赛克纹理)
当纹素密度远大于1时,许多纹素会影响单个屏幕像素,产生摩尔纹(moire banding pattern)和闪烁现象。

我们希望无论物体远近,都能维持纹素密度接近1。要准确地维持此约束是不可能的,但是可以使用多级渐远纹理(mipmapping)技术来逼近。 其方法是,对于每张纹理我们建立较低分辨率位图的序列,每张位图的长宽都是上一张的一半。使用多级渐远纹理时,图形硬件根据三角形和摄像机距离选择合适的级数,以尝试维持纹素密度接近1。

纹理过滤

采样时,像素中心可以落入纹理空间的任何位置,包括两个或以上纹素之间的边缘。因此,图形硬件通常需要采样出多于一个纹素,并把采样结果混合以得出实际的采样纹素颜色,此过程称为纹理过滤(texture filtering)

多数显卡指出以下的纹理过滤种类:

  1. 邻近(nearest)。
  2. 双线性(bilinear)。(像素中心周围的4个纹素加权平均)
  3. 三线性(trilinear)。(双线性施加于两个mipmap)
  4. 各向异性(anisotropic)。(根据视角对一个梯形范围内的纹理进行采样)

材质(material)是网格视觉特性的完整描述。包括贴到网格的纹理设置、选用哪个着色器、着色器的输入参数、以及控制图形硬件本身的功能参数。

虽然顶点属性从技术上来说也是表面特性描述的一部分,但是顶点属性并不算是材质的一部分。

三维模型通常会使用多于一个材质。因此一个网格通常会被切割成子网格,每个子网格对应一个材质。



光照基础

光照是所有计算机图形渲染的中心。

着色(shading)一词,通常是光照加上其他视觉效果的泛称。

最简单的模型只考虑直接光照(direct lighting),光发射后,碰到物体反射,然后直接进入摄像机。这种简单模型又称为局部光照模型(local illumination model)

要达到真正的照相写实,就必须考虑到间接光照(indirect lighting),即光被多个表面反射后才进入摄像机。照顾到间接光照的模型称为全局光照模型(global illumination model)

全局光照模型能够完全由单一数学公式描述,此公式称为渲染方程(the rendering equation)或者着色方程(shading equation)。从某种意义上说,所有渲染技术都可视为此渲染方程的完全或部分分解,尽管每种技术的基本方法、假设、简化方式、逼近方式有所不同。

游戏渲染引擎中最常用的光照模型就是Phong光照模型。此模型把从表面的光分解为三个独立项:

  1. 环境光(ambient)。模拟场景中的整体该光照水平,是间接反射光的粗略估计。
  2. 漫反射(diffuse)。模拟直接光源在物体表面的均匀反射。
  3. 镜面反射(specular)。模拟会在光滑表面看到的光亮高光。

$I = (k_{ambient} ⊗ A) + \sum_{i}[k_{diff}(N·L_i) + k_{spec}(R_i·V)^α] ⊗ C_i$

Blinn-Phong光照模型

Blinn-Phong光照模型是Phong光照模型的变种,二者在计算镜面反射时有些差别。定义了一个中间向量(halfway-vector)H,它是视线方向V和光线方向L的角平分线方向。Blinn-Phong的镜面分量为$(R·V)^α$,不同于Phong模型的$(N·H)^α$。

BRDF图表

在Phong反射模型中,其3个项是通用的双向反射分布函数(BRDF)的特例。
BRDG是沿视线方向V的向外(反射)辐射与沿入射光线L的进入辐射之比。

BRDF可以显示为一个半球图表,其中距离原点的径向距离代表从该角度观察到的反射光强度。

静态光照:在网格的顶点预计算光照然后把结果存储在顶点漫反射颜色属性中。也可以逐像素预计算光照,把结果存储于光照贴图(light map)中。

环境光(ambient light):对应Phong模型中的环境项。

平行光(directional light):模拟无限远的光源,例如太阳。

点光源(point light):又称为全向光,有一个特定位置并向所有方向均匀辐射。

聚光(spot light):发射光线受限于圆锥范围的点光源。

面积光(area light):面积非零的光源,会产生本影(umbra)半影(penumbra)

自发光(emission):发光表面可用自发光贴图模拟,纹理颜色以完全强度发射,不受附近的光照环境所影响。



虚拟摄像机

计算机图形学中,虚拟摄像机比现实摄像机和人眼简单得多。我们把摄像机当成理想的焦点,并有一个矩形虚拟感光表面–称为成像矩形–悬浮在焦点前不远处。成像矩形由感光的像素栅格组成。

虚拟摄像机的焦点,是观察空间(view space)或称摄像机空间(camera space)的三维坐标系的原点。摄像机通常“看着”观察空间中的正z轴(左手坐标系)或者负z轴(右手坐标系)。

当要渲染三角形时,其顶点首先从模型空间变换到世界空间,然后从世界空间变换到观察空间。

CRE:模型空间、世界空间、观察空间的坐标系不一定都采用左手/右手坐标系。

常用的有透视投影(perspective projection)以及正交投影(orthographic projection)

摄像机能够看到的空间范围称为观察体积(view volume)

当使用透视投影时,观察体积的形状是截断的四角锥,此形状有其独特名称–平截头体(frustum)

透视及正交投影能把点从观察空间变换至一个称为齐次裁剪空间(homogeneous clip space)的坐标系。

裁剪空间是用来把摄像机空间的观察体积转换成标准的观察体积,转换后的体积独立于分辨率和长宽比。

GPT:齐次裁剪空间一般不采用NDC。

屏幕的宽度和高度之比称为长宽比(aspect ratio)

OpenGL以左下为原点,DX以左上为原点。

最终渲染后的影像会存储在一个名为帧缓冲(frame buffer)的颜色位图缓冲中。

渲染引擎一般会维护至少两个帧缓冲。其中,显示硬件扫描一个帧缓冲时,渲染引擎则更新另一个帧缓冲,称为双缓冲法(double buffering)

渲染目标(RenderTarget)
任何供渲染引擎绘制图形的缓冲都称为渲染目标(RenderTarget)
渲染引擎除了使用帧缓冲外,还会使用许多种类的屏幕外(off-screen)渲染目标。

要在屏幕上产生一个三角形影像,我们需要给三角形范围内的像素填充数据。此过程称为光栅化(resterization)

填充像素进帧缓冲时需要通过许多测试。如果测试不通过就会被丢弃。

画家算法不能处理互相穿插的三角形。要正确地实现三角形的遮挡关系而不需要理会渲染顺序,渲染引擎会使用称为深度缓冲(z-buffer/depth-buffer)的技术。

深度缓冲是全屏缓冲,每个像素通常含有24位深度值和8位模板值,总共32位。

深度冲突和W缓冲

深度缓冲的精确度是有限的,两个足够接近的平面,其两个深度值会变成相同的离散值,出现称为深度冲突(z-fight)的噪点效果。

GPT:z缓冲(Z-buffer)和w缓冲(W-buffer)是两种用于深度测试的技术。
GPT:Z缓冲是非线性的,对于靠近摄像机的对象有较高的精度。对于远处的对象,深度精度较低,这可能导致深度冲突(z-fighting)。
GPT:W缓冲存储每个像素的深度值(w值),w值是观察空间中的深度值。W缓冲在整个深度范围内保持线性分布,因此可以避免Z缓冲的深度精度问题。硬件支持较少,因此W缓冲的实现和优化较复杂。

(END)