Shading(着色)

Visibility/Occulusion(可见性/遮蔽)

Painter’s Algorithm 从后往前画

但是由于自遮挡的关系,有些无法具体描述远近的(如上图左右面的绘制),绘制顺序显得尤为重要。

左,下,右,上,这个顺序结果正确

但是右,上,左,下,由于左下两面的错误遮挡,导致结果错误。

假如一个模型是两个蛇状管道相互缠绕组合而成的,那渲染先后的问题就更加困难,正方体能够区分不同面无非是一个面遮挡另一个,后者不规则的分区有着非单一的复杂遮挡关系,如此看来这种算法局限性也很大

结论: 一定程度上画家算法是有效的,但是sorting layer的问题是难以解决的。

ZBuffer 深度缓存

为了解决画家算法的问题,引入深度缓存

简而言之,对每个像素的深度进行保存,保留最近的那个深度值

  • framebuffer存颜色值
  • depth buffer(z-buffer)存深度值

效果如下图,记z值为positive

具体代码实现算法,记初始zbuffer的值都是无限大,每绘制一个图元都来一次二重循环遍历所有像素,不断装填覆盖更小的z

时间复杂度O(n),设三角形个数为n,三角形之间覆盖的个数为常数个。

zbuffer的算法显然是一个很好的解决方法。

【提高】

MSAA常用于反走样,一个像素中有多个采样点,然后根据采样点的分布率来评判该像素颜色深浅

如果绘制有重叠部分的两个三角形,对于抗锯齿的部分,如果只是对像素进行z-buffer,然后边缘锯齿用MSAA,

结果也还行。

想要更好的结果,我们也许可以对采样点进行zbuffer,毕竟像素是呆板的块状,如果能够进一步细化,或许分布率不会变,却也能让先后关系更精确,进而结果更精确。

Illumination&Shading

BlinnPhong Model

布林冯光照模型

三个相加等于着色结果

着色是局部的

着色不等于阴影(shading != shadow)

n是法线向量,h是半程向量(入射光向量l与眼睛视线向量v的和的单位向量)

以下为布林冯的高光

最终结果

Shading Frequency 着色频率问题

Graphics Pipeline(Real-time rendering)

图形渲染管线,实时渲染管线

Application 应用阶段,输入顶点数据

Geometry 几何阶段

Rasterization 光栅化阶段

  • Shading 着色(shaded fragment)

  • framebuffer (严格来说属于光栅化)

输出屏幕

除了常规的fragment shader,vertex shader

现在还有compute shader(通用),geometry shader(几何shader)等可编程部分

Texture Mapping

每个三角形顶点都有一个uv坐标(0,1),可以反复平铺

Barycentric coordinates(重心坐标)

【注意】 我们首先要区别点的重心坐标和三角形的重心坐标

interporate across triangle在三角形内进行插值,希望三角形内部得到平滑过渡

α,β,γ均大于等于0,重心在三角形内

A点在重心坐标系的重心坐标(α,β,γ)为(1,0,0),B为(0,1,0)

三角形的重心坐标计算公式就是相加除以3,重心坐标为(A.x+B.x+C.x/3,A.y+B.y+C.y/3,A.z+B.z+C.z/3)

三角形内任意一点的重心坐标计算结论如下

结论

【补充】

  • α+β+γ = 1,是重心坐标的性质,同时是重心坐标在ABC平面上的要求
  • 三维中的属性在三维中做插值,否则投影到屏幕再做可能会因为重心不一致而错误;

Texture queries

a pixel on a texture is a texel(纹素)

texture mapping简单应用:通过查uv来将纹理的值作为Diffuse Color

纹理太小,采样后会被拉大texture magnification

最后结果不精确,得到的会是明显的像素块的非连续结果

解决方法:纹素,屏幕分辨率差别大,texture queries的时候,用屏幕像素坐标查询texture的uv,

使用双线性插值(Bilinear Interpolation) 来解决非连续的问题。

水平、竖直都进行下述插值操作,对texel的4个像素进行插值

另有Bicubic的方法,取周围16个像素

1. Point Sample problem

采样纹理的时候为什么会出现这样的走样(artifact)?

当纹理特别大,哪怕一个纹素也包括了很多高频信息,人眼跟不上变化频率,于是产生了走样。

或者,texture queries 的时候,采样一个像素的时候只使用一个采样点,采样频率不够,导致最后结果表现不够精确连续。

增加采样点虽然理论可行,但是计算量太大。

2. Range Query Problem

范围查询不像点查询一样进行采样,而是直接查询一个范围的结果,这样就避免了上述的计算量。

范围查询根据功能可以分为平均,最大,最小….很多种不同类的方式。

引入Mipmap的概念,(fast,approximate,square),分为多级,分辨率逐级减半,D = log(L)

使用mipmap比原来多用了1/3的空间存储

如何使用mipmap?

离人眼近的地方,使用低级别的,分辨率更高的,离人眼远的地方,对精度要求低,可以用分辨率更低的mipmap。

【注】实际操作中,由于mipmap本身是方块,查询方式离散,层与层之间还需再进行插值。本身xy两轴上分别做了插值让纹理适配原像素,然后又在层与层直接插值,称之为三线性插值(trilinear interpolation)

3. mipmap limitation & Anisotropic Filter

mipmap远处会出现过度模糊的问题,

问题在哪?其实还是出在三线性插值上,之前提到mipmap的approximate的特性,本身插值方法就是人为近似的,而且又只能处理方形区域的查询。

引入效果更好的方法。

Anisotropic Filter各向异性过滤(ripmap)

游戏中2x指的是左上角两层的范围,存储量收敛到原来的3倍,游戏中一般都可以开到最高,对性能不会有任何影响。

这种方法由于x,y方向都有拉伸,查询不局限于方形,扩展到矩形

但是如下图,screen上的一块像素表达的内容在texture上也许是斜着的,使用各向异性查询,就要求将整块斜块用矩形包围,最后结果overblur。irregular footprint(覆盖区) in texture的问题

竖着的长条可以解决,但是斜着的就不好解决。

EWA filtering

将查询范围用多个圆包围,多次查询

可以处理不规则图形,但是计算量很大

Application of textures

现代GPU中,texture = memory + range query(filtering)

  1. Environment Map 环境贴图,物体反射周围的光;描述不同方向的光照信息。

    Shperical Map,发生扭曲;Cube Map

  2. Bump/Normal Map

    凹凸贴图如何绘制法线制造凹凸感?

    • 设原来法线为(0,0,1)
    • 求偏导(derivatives),假设u,v变化单位1

    切线逆时针旋转90度得到法线

    得到法线(-dp/du,-dp/dv,1)

    这样,通过凹凸贴图的纹理映射,制造假的法线,实现结果的凹凸。

  3. Displacement Map,位移贴图,真正的移动了原模型,不同于凹凸贴图。

    要求三角形很细,三角形的频率要比纹理高;采样率高,模型足够细致

    DX上有一个方法叫动态细分,即便是模型不是很精细,但是可以根据需要进行选择性移动

其它

Provide Precompute shading

3d textures and volume rendering