个人的Directx9研究总结 (2)
Directx9的小型游戏引擎第二篇。由于1月份旧电脑显卡爆掉,因此新电脑配置环境花了不少时间。
继续上篇,本篇为一些关于Shader的经验总结
1.NormalMapping方式实现Bump map
Bump map是一种表现凹凸感的方式,其中比较早期的方法是使用一张Height Map来移动网格的顶点从而表现顶点。而现在更为广泛的使用方法是使用一张Normal Map,对这张纹理采样。在转换为3D空间向量坐标代替原本的法线。这种方法可以让低模野获得高模的质感,配合高光下更明显。
如图,左边是使用NormalMapping,右边是使用原法线的情况。可以看出左边的质感有较大提升。
但是仅仅这样是无法反映任何角度下光照情况的,光使用新法线仅能对应原本法线n指向z的正方向的情况。而模型的法线是定义在模型空间中,指向四面八方的。因此完整的方法是在切线空间(tangent space)里计算光照。
// 模型法线
float3 N = normalize(mul(In.Normal, (float3x3) mtxWorldInverseTranspose));
// 模型切线
float3 T = normalize(mul(In.Tangent, (float3x3) mtxWorldInverseTranspose));
// 叉乘计算副切线
float3 B = cross(N, T);
// TBN逆矩阵
float3x3 TBNMatrix = float3x3(T.x, B.x, N.x,
T.y, B.y, N.y,
T.z, B.z, N.z);
// 切线空间半向量
outVS.THalfVector = mul(halfVector, TBNMatrix);
// 切线空间光线向量
outVS.TLightDir = mul(lightdirection, TBNMatrix);
切线空间中由三条垂直的向量:法线切线和副切线构成。由这三条线构成的TBN矩阵。
之后可以用采样取得的法线与TBN相乘来把法线转换到世界坐标系中,或者使用TBN的逆矩阵计算光照来让光照变量都在一个坐标系中完成计算。第一种方法比较简单但是需要在像素着色器中才能完成计算,第二种方法的则可以都在顶点着色器完成计算。上面代码采用第二种方法。
Directx9中采用法线贴图的步骤:
- D3DVERTEXELEMENT9宣言中带法线和切线的顶点。
- D3DXComputeTangentFrameEx计算模型中法线和切线。
- Shader中计算TBN矩阵
2.IBL与MatCap
IBL(Image-Based Lighting)就是基与图像的光照,早期立方体贴图通常用来做天空盒或者物体的环境贴图,那么既然立方体贴图可以做环境贴图。不也可以把漫反射的结果保存下来,然后直接使用吗?这就是IBL的想法。使用纹理来反映光照结果。下图是Unity Matcap Shader用的图片。
如图,无纹理情况下漫反射计算后结果
如图,漫反射结果乘上根据法线采样的纹理
而MatCap算是一个进阶用法,使用Unity的人应该比较了解。MatCap继承了IBL的思想,不过不是使用需要六张纹理的立方体贴图。而且一张球状的纹理。再通过把模型法线转换到2D空间,使用该2D向量作为球状纹理的UV坐标。
// 根据模型法线计算Matcap用UV坐标
float2 normaltoUV = float2(In.WNormal.xy * 0.5f + 0.5f);
不过仅仅这样只适用于静止的摄像机,所以再让法线再乘上观察矩阵让视线移动纹理也跟着移动。就有了相对比较好的视觉效果。
如图,原本贴图结合MatCap,左边是加算,右边是乘算。
MatCap用很低的成本就可以体现需要复杂计算的光照效果,与移动端或者卡通渲染等非真实渲染等结合效果很好。
Directx9中采用Matcap的步骤:
方法同立方体贴图做环境贴图,只需要多一部计算UV坐标的步骤即可
3.PostProcessing 屏幕后处理
运用RTT可以把当前场景渲染成一张纹理。而渲染成一张纹理后便可以使用各种后期处理(PostProcessing),后处理都是基于像素着色器,修改纹理从而产生各种各样效果。
只需要消耗很少的资源就可以达成各种各样效果。
例如反色:
outPS.Color = float4(1.0f - originalcolor.r,
1.0f - originalcolor.g,
1.0f - originalcolor.b,
1.0f);
使用卷积的锐化效果:
运用后处理可以在不改变素材的情况下制造各种风格化的画面效果。
Directx9中采用PostProcessing的步骤:
RTT渲染当前场景到一张纹理,之后对该纹理使用像素着色器来进行后处理。