我们从地形和板条箱开始渲染场景,先绘制地形,再绘制板条箱,使地形和板条箱的像素依次存入后台缓冲区。然后使用混合(blending),将水体绘制到后台缓冲区,使水体像素和后台缓冲区中的地形、板条箱像素融为一体。通过一方式,我们可以透过水体看到地形和板条箱。本章我们主要讲解混合技术,它可以将当前的光栅化像素(也称为源像素)与后台缓冲区中的像素(也称为目标像素)混合(融合)在一起。该技术通常用于渲染半透明物体,比如水和玻璃。
在输出合并(output merger,简称OM)阶段中,某些像素片段会被丢弃(例如,未能通过深度测试或模板测试)。未丢弃的像素片段会被写入后台缓冲区。混合(blending)工作是在该阶段中完成的,一个像素可以与后台缓冲区中的当前像素进行混合,并以混合后的值作为该像素的最终颜色。某些特殊效果,比如透明度,就是通过混合来实现的。
透明效果在DirectX中时需要PS和api同时设置才能实现的,如果在c++的api中不开启混合,那么即使在PS中设置了颜色的a值,最终渲染时a也会被丢弃。
在PS里的a值计算很好理解,就是类似
float4 litColor = texColor * (ambient + diffuse) + spec;
litColor.a = texColor.a * g_Material.Diffuse.a;
return litColor;
将最后计算出来的纹理透明度和材质透明度做乘法。
但是实际上,某个像素的颜色算出后会存到后备缓冲区中,通过z深度与其他像素排序,并计算透明度叠加效果,比如可以透过透明的玻璃看到小明,但小明身后的墙壁是看不到的(墙壁没通过深度测试,直接被丢弃),例如,我们设Csrc = 玻璃,Cdst = 小明,C = 最终颜色。设Csrc为当前正在进行光栅化处理的第ij个像素(源像素)的颜色,Cdst为后台缓冲区中的第ij个像素(目标像素)的颜色。当不使用混合时,Csrc会覆盖Cdst的值(假设该值已通过深度/模板测试)并成为后台缓冲区中的第ij个像素的新颜色。但是当使用混合时,Csrc和Cdst会被组合为一个新颜色C并覆盖Cdst的值(即,混合颜色C会成为后台缓冲区中的第ij个像素的新颜色)
还需要在API中设置前后两个像素叠加的方式。所以渲染次序也需要修改,我们先渲染不透明的物体,而对于透明的物体,则按照从远到近的次序渲染。对于本次实例,我们先渲染的篱笆盒,其次是水,因为篱笆盒有一部分在水下,如果反过来,则先渲染水再渲染篱笆盒,因为篱笆盒在水下的部分深度比水面大,无法通过深度测试而被丢弃。所以在绘制透明物体前,要么关闭深度测试,要么对物体到摄像机的先后顺序进行排序,并按从后到前的顺序进行绘制。
下面就要引入混合方程的概念,这些都是API帮我们处理的,不需要修改hlsl。
混合方程
首先Csrc和Cdst都是float3,即rgb
基本方程为
C = Csrc * Fsrc op1 Cdst * Fdst
A = Asrc * Fsrc op2 Adst * Fdst
其中,*表示分量乘法rgb都要算;op则表示一个未知运算符,这个运算符具体是什么,通过api来设定;Fsrc和Fdst,混合因子,跟op一样,也要去api设定。
注意:我们很少混合alpha分量,多数情况下,我们只是对RGB分量进行混合。尤其是在本书中,我们从不混合源alpha值和目标alpha值,虽然alpha值会参与我们的RGB混合操作。这主要是因为我们暂时用不到后台缓冲区中的alpha值。只有当某些算法需要使用目标alpha值时,后台缓冲区中的alpha值才有用。以小明为例,窗户为透明,需要将玻璃的颜色混合到玻璃后的小明身上,使小明出现一种被玻璃阻挡的感觉,但透明度不需要混合,因为小明不是透明的,也不需要透明,其A值永远是1。
渲染状态
RenderStates实际上就是将光栅化阶段描述、混合阶段描述和采样器等等需要额外创建的资源的创建代码与GameApp分隔开,整理下流程。
class RenderStates
{
public:
template <class T>
using ComPtr = Microsoft::WRL::ComPtr<T>;
static bool IsInit();
static void InitAll(ID3D11Device * device);
// 使用ComPtr无需手工释放
public:
static ComPtr<ID3D11RasterizerState> RSWireframe; // 光栅化器状态:线框模式
static ComPtr<ID3D11RasterizerState> RSNoCull; // 光栅化器状态:无背面裁剪模式
static ComPtr<ID3D11SamplerState> SSLinearWrap; // 采样器状态:线性过滤
static ComPtr<ID3D11SamplerState> SSAnistropicWrap;