代码已上传至GitHub,这部分对应的项目为NormalDisplacementSsao。运行项目时可以使用数字键1-3切换渲染的shader,另外按下z键可以看到SSAO生成的遮罩贴图。
这部分内容较多,所以以介绍原理为主。另外在阅读源码时,为了剔除枝干让整体的代码更加一目了然,我用了模板和SFINAE(主要在SceneObject.h),如果看不懂的话也没有关系,不重要,关键是了解这些技术的主要流程。
首先我会对每个技术的原理做简要介绍。如果有时间和心情的话会结合代码分析。
目录
Cubemap
这部分代码可以参看DemoCubeMap这个项目。
定义
Cube mapping 保存6个纹理,可以把它们具象化为一个立方体的六个面。立方体是轴对称的,每一个面都可以正好对应到空间坐标的一个轴方向(±X, ±Y, ±Z,),因此Direct3D提供了D3D11_TEXTURECUBE_FACE用于描述六个面
typedef enum D3D11_TEXTURECUBE_FACE
{
D3D11_TEXTURECUBE_FACE_POSITIVE_X = 0,
D3D11_TEXTURECUBE_FACE_NEGATIVE_X = 1,
D3D11_TEXTURECUBE_FACE_POSITIVE_Y = 2,
D3D11_TEXTURECUBE_FACE_NEGATIVE_Y = 3,
D3D11_TEXTURECUBE_FACE_POSITIVE_Z = 4,
D3D11_TEXTURECUBE_FACE_NEGATIVE_Z = 5
} D3D11_TEXTURECUBE_FACE;
与2D纹理不同,我们无法再通过一个2d纹理坐标来确定Cube mapping中的一个图素。我们需要定义一个3D的纹理坐标,定义上,它是一个从原点出发的方向向量,cube map和这个方向相交的点就是这个3d坐标对应的图素。之前讨论过的texture filtering同样适用于cube map。
在HLSL中,TextureCube 代表了一个Cubemap。
TextureCube gCubeMap;
SamplerState gTriLinearSam
{
Filter = MIN_MAG_MIP_LINEAR;
AddressU = Wrap;
AddressV = Wrap;
};
…
// in pixel shader
float3 v = float3(x,y,z); // some lookup vector
float4 color = gCubeMap.Sample(gTriLinearSam, v);
环境贴图
Cubemap的主要应用之一是环境贴图,即天空盒。
首先我们创建好Cubemap纹理,然后我们需要一系列顶点组成一个球形。由于天空可以认为是非常远的,所以实际上球形的精度并不是很重要。
绘制时,始终认为Camera是原点,因此直接使用球体的本地坐标作为世界坐标来使用。
我们可以认为天空盒的位置是最远的像素,因此直接将w设置为1。但此时必须要修改深度测试时的测试函数,默认是Less,我们需要改为Less_Equal否则w=1的像素会被舍弃,天空盒就无法绘制出来了。
模拟反射
通过环境贴图可以模拟反射,将周围的环境显示在光滑的物体表面。
主要过程是通过眼睛的位置和物体表面的法线,确定反射的方向,根据方向对cube map进行采样,从而把反射出来的图象绘制在物体上。
动态Cubemap
游戏中有许多动态的物体,因此需要通过动态生成环境贴图来模拟反射。一个做法是在反射的物体位置,向周围六个方向,渲染出Cubemap的六个面,这样就能绘制出动态的环境贴图。
法线贴图
物体的表面不总是光滑的,但如果将物体表面的每一个