介绍
本文介绍了渲染树的几个手段,分别是
- 轮廓裁剪(Silhouette Clipping)增加了树枝和树干外形的细节
- 阴影贴图提供了逐树叶级的真实感自我遮挡
- 使用双叶面光照模型和HDR来优化光照
- 半透明覆盖(alpha to coverage)来抗锯齿
轮廓裁剪
对于下图黄框中的内部细节,使用浮雕映射(relief mapping)来处理。对于蓝框中的轮廓细节,使用了一种称为轮廓裁剪的技术,在垂直于视线向量的方向上挤压物体的轮廓。类似于浮雕映射,这些轮廓对高度图进行光线追踪,以决定哪个像素实际上被轮廓所遮挡。
- 轮廓挤出
根据逐顶点的法向量和视线向量的点积,插值找到三角形边上点积为0的点。在找到的点利用几何着色器构造两个三角形进行挤出。
- 高度追踪
挤出后的顶点的基切线和纹理坐标不需要变化。为每个顶点计算视线向量。如下图所示,在像素Shader中,从点A开始逐步沿着视线向量的反方向移动来改变它的纹理坐标,最终到达B点。然后执行浮雕映射中的高度追踪查找。当发现交点时,就计算该片元的漫反射光照和阴影,否则该片元进行抗锯齿或者丢弃。算法的改进手段有把线性和二分查找结合(Policarpo2004)或预计算来加速跟踪(Dummer2006)
- 轮廓LOD:随树离相机距离变远时,轮廓的宽度逐渐减为0,对远处的树不进行轮廓处理。
阴影
SpeedTree中的阴影是离线预计算的,因此存在没有自遮挡和阴影不真实的问题,有以下解决手段:
- 树叶自遮挡
此处讨论的树叶情况是树叶广告牌,总是朝向相机,使用的方法是阴影映射。当渲染树叶时,树叶朝向相机。当渲染阴影贴图时,它们转而朝向光源。绕中心点旋转会出现下图(a)中只有一半树叶片接受阴影的情况,为了避免这种情况使用(b)中旋转后向光源移动的方法。
但直接对树叶平面片投射阴影会使得阴影像延长的条纹。解决方法是在视线方向使用偏移系数来改变阴影的位置,偏移系数存储在纹理中。使用新的树叶位置,把被遮挡的像素投射到光照空间并对阴影进行采样。
- 级联阴影贴图:将视锥体分割为几个区域并分别为每个区域将阴影贴图渲染到不同的纹理。为了减少CPU负担,不同的级联使用不同频率的更新。
树叶光照
- 双边光照
当树叶从后面被照亮时,树叶的主要亮度来自于透射的光,色彩会轻微变黄或变红。
使用的策略是在像素着色器生成偏黄的树叶颜色(Diffuse.g * 0.9, Diffuse.g * 1.0, Diffuse.g * 0.2),并根据视线方向和光线方向的夹角μ,在原来颜色和计算的黄色之间插值,μ越接近于π,树叶颜色越黄,镜面高光也越小。
- 镜面高光
首先使用普通的镜面反射,但远处的树会闪闪发光,因此根据距离来减少镜面反射。其次,为了实现叶片如下图的反射效果,使用一个粗糙的V型法向量贴图把树叶分成两半。最后,为了避免镜面反射细节太多造成闪烁,使用粗糙的mipmap层次的贴图使得反射光更加平滑柔和。
半透明覆盖
半透明覆盖把像素着色器输出的alpha值转化为应用于MSAA渲染目标的子像素分辨率上的覆盖掩码。当MSAA分解被使用时,结果会是一个透明的像素。使用半透明覆盖时,可以极大减少方块状区域和硬边缘,尤其是动态时可以减少它们造成的闪烁。
为了在LOD之间视线交叉衰减,使用不同alpha测试值的两个LOD被同时绘制。半透明覆盖来执行交叉衰减,虽然可能会有透明度的问题,但很难被注意。
半透明衰减也可以对生成的轮廓边进行反锯齿。为了实现该目的,在轮廓边上生成一个窄的边界区域,它使轮廓的不透明度从完全不透明变为透明。在高度追踪期间,记下对高度图的最小损失并根据这个值来改变像素的不透明度。如果视线向量和高度图相交则alpha为1,如果不相交且最小损失小于屏幕空间的1.5个像素,则逐渐衰减到0,否则直接为0.