非真实感渲染
14.0
- 尽管游戏渲染一般都是以照相写实主义(photorealism)作为主要目标,但也有许多游戏使用了非真实感渲染(Non-Photorealistic Rendering,NPR)的方法来渲染游戏画面
- 非真实感渲染的一个主要目标是,使用一些渲染方法使得画面达到和某些特殊的绘画风格相似的效果,例如卡通、水彩风格等
14.1 卡通风格的渲染
14.1.0
- 卡通风格是游戏中常见的一种渲染风格。使用这种风格的游戏画面通常有一些共有的特点,例如物体都被黑色的线条描边,以及分明的明暗变化等
- 要实现卡通渲染有很多方法,其中之一就是使用基于色调的着色技术(tone-based shading)
- 除了光照模型不同外,卡通风格通常还需要在物体边缘部分绘制轮廓
14.1.1 渲染轮廓线
- 在实时渲染中,轮廓线的渲染是应用非常广泛的一种效果
- 通常有以下几类方法:
- 基于观察角度和表面法线的轮廓线渲染
- 过程式几何轮廓线渲染
- 这种方法核心是使用2个Pass进行渲染,第一个Pass渲染背面的面片,并使用某些技术让轮廓可见,第二个Pass正常渲染正面的面片
- 优点在于快速有效,并且能适用于绝大多数表面平滑的模型;缺点是不适合类似于立方体这类平整的模型
- 基于图像处理的轮廓线渲染
- 12、13章介绍的边缘检测方法就属于这类
- 优点在于可以适用于任何种类的模型,但也有自身的局限所在,比如一些深度和法线变化很小的轮廓就无法被检测出来,比如桌上的纸张
- 基于轮廓边检测的轮廓线渲染
- 一个最大问题就是无法控制渲染轮廓的渲染风格,对于一些情况,我们希望可以渲染出独特风格的轮廓线,如水墨风等等,就可以使用这种方法来做轮廓线渲染
- 最后一个种类就是混合了上述的几种渲染方法
- 比如首先找到精确的轮廓边,把模型和轮廓边渲染到纹理中,再使用图像处理的方法识别轮廓线,并在图像空间下进行风格化渲染
14.1.2 添加高光
- 卡通风格中的高光往往是模型上一块块分界明显的纯色区域
- 对于卡通渲染需要的高光反射光照模型
14.1.3 实现
- 本节中,将使用过程式几何轮廓线渲染
- 它会在第一个Pass中,使用轮廓线颜色渲染整个边缘的面片,并在视角空间下,把模型顶点沿着法线方向向外扩张一段距离
- 这里将模型顶点沿着法线方向向外扩张一段距离,以此让背部轮廓线可见
- 如果直接使用顶点法线来进行扩展的话,对一些内凹的模型就可能会发生背面面片遮挡正面面片的情况。为了尽可能防止这样的情况,在扩张背面顶点之前,首先对顶点法线的z分量进行一些处理,使它等于一个定值,然后把法线归一化后,再对顶点做扩张
- 这样做的好处在于扩展后的背面更加扁平化,从而降低了遮挡正面面片的可能性
- 卡通风格中的高光,往往是模型上一块块分界明显的纯色区域,为了实现这种效果,就不能使用前面的光照模型
- 之前的Blin-Phon模型使用的是法线点乘光照方向以及视角方向和的一半,再和另一个参数进行指数操作,以得到高光反射的系数
- 对于卡通渲染所需要的高光反射模型,同样需要计算normal和halfdr的点积结果,但不同的是,我们会把该值和一个阈值进行比较,如果小于该阈值,高光反射为0,否则为1,
- 但是这个比较的过程中,会产生锯齿效果,为了防止锯齿效果,使用了smoothstep函数做了一个光滑处理
- w可以选择邻域像素之间的近似导数值,得到后作为阈值
- 乘上step目的是在_SpecularScale为0时可以完全消除高光反射的光照
- Shader:
- 在顶点着色器中把顶点和法线都变换到视角空间下,这时为了描边可以在观察空间中达到最好的效果
14.2 素描风格的渲染
- 在顶点着色器首先计算逐顶点的光照,然后根据光照的结果决定六张纹理的混合权重
- 使用提前生成的素描纹理来实现实时的素描风格的渲染,这些纹理会组成一个色调映射,它们会从左到右逐渐增多纹理中的笔触,以用于模拟不同光照下的漫反射效果
- Shader:
- Hatch 0至Hatch 5对应了渲染时所使用的六张素描纹理,它们的线条密度是逐次增大的
- 由于素描风格往往也需要在物体的周围渲染轮廓线,因此直接使用14.1节中渲染轮廓的那个Pass,使用方法就是使用UsePass命令调用
- 需要计算六张纹理的混合权重,首先在v2f结构体中添加相应的变量,也就是hatchWeight0和hatchWeight1,因为是两个fixed3类型的变量,所以两个足够了
- 为了添加阴影效果,添加了worldPos变量以及SHADOW_COORDS宏来为阴影纹理采样坐标
- 对顶点坐标做基本的变化,变换到裁剪空间下,然后使用_TileFactor得到纹理采样的坐标
- 在计算六张纹理的混合权重前,首先需要去计算逐顶点的光照,因此,使用世界空间下的光照方向和法线方向来得到漫反射系数diff
- 把权重值初始化为0,并把diff缩放到0~7的范围之间,得到hatchFactor
- 通过判断hatchFactor所处的子区间来计算对应的纹理混合的权重
- 最后计算了顶点的世界坐标,并使用TRANSFER_SHADOW来计算阴影纹理的采样坐标
- 在片元着色器部分,得到了六张纹理混合权重后,就可以对每张纹理进行采样,并和它们对应的权重值做一个相乘,以得到每张纹理的采样颜色
- 同时计算了纯白在渲染中的贡献,这段方法用1减去六张纹理的权重来得到的,这是因为素描中往往会有留白的部分,因此我们希望在最后的渲染中,光照最亮的部分是纯白色
- 最后混合各个颜色值,并和阴影值相乘来得到最终的渲染结果
- 效果: