水的表面显示场景物体倒影的效果。这是把场景渲染到水面上来实现,不是简单的渲染,而要经过反射处理,即要反射渲染目标。
法线贴图是可以应用到3D表面的特殊纹理,有时也称凹凸纹理,它包括了每个像素的高度值,内含许多细节的表面信息,能够在平平无奇的物体上,创建出许多种特殊的立体外形。可以把法线贴图想像成与原表面垂直的点,所有点组成另一个不同的表面。对于视觉效果而言,它的效率比原有的表面更高,若在特定位置上应用光源,可以生成精确的光照方向和反射。
现具体讨论一下实现水效果的步骤:
一:设计一继承模型类的water类,并为其设计一shader文件,可在其构造函数里设置相应的shader文件,在water改写模型类的_Rend()函数,函数内对water进行特殊的渲染。
_Rend()具体操作如下:
先使用当前的shader变量设置纹理
m_pShader->SetTexture("BumpMap",m_pBumpmap);
BumpMap是shader里与C++的接口
再使用IDirect3DDevice9接口设置四个渲染状态:
SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
SetRenderState( D3DRS_ALPHATESTENABLE, FALSE );//关闭alpha测试
SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );//打开alpha混合
最后调用模型类的渲染函数 _Render(),即调用Water的_Render()
如此递归调用water的渲染函数使water的纹理和渲染状态即时更新,使水的效果随场景的变化而变化。
二:设计一监听器ReflectListener和相应的渲染目标mpReflectTarget
(参考“引擎技术之渲染到目标“)
其对应Execute()函数主要负责摄像机信息的设置,以实现水面的反射功能
先看一下水面反射图:
Execute()要设置摄像机的视点在水下对应位置和方向后进行物体的渲染,渲染后再设置为原来地面的视点和方向。
具体实现如下:
先获取当前摄像机的视点OldEye,获取水面高度WaterH,
则水下的摄像机视点NewEye为:OldEye – 2 * ( OldEye – WaterH )
化简得:NewEye = 2 * WaterH – OldEye
获取当前摄像机的方向 Direction ,则其在水面下方向为 –Direction
设置摄像机新的视点和方向后,进行场景物体的渲染:scene->_Render();
渲染后再把摄像机设置为原来的视点和方向。
三:在场景类的渲染函数里,在物体渲染前执行:
渲染目标 mpReflectTarget->Render()
设置 water的纹理 water->SetTexture(mpReflectTarget->GetTexture())
四:现讨论water对应的shader文件的编写。 有关shader的基本设置可参考
“引擎技术研究之shader技术“
1 VS_Scene的实现
VSOut OUT = (VSOut)0;
//矩阵变换
inPosition = mul( inPosition, World );
OUT.vPosition = mul( inPosition, View );
OUT.vPosition = mul( OUT.vPosition, Projection );
//计算水对应的屏幕坐标,从投影坐标到纹理坐标转换
float4 vHPos=OUT.vPosition;
vHPos.xy = -vHPos.xy;
float4 screenpos;
screenpos.xyz = (vHPos.xyz + vHPos.w)*0.5f;
screenpos.w = vHPos.w;
OUT.vProjCoord=screenpos;
//计算绕动纹理坐标
float wavescale=0.5;
float2 uv=inPosition.xz*0.01/wavescale;
float2 fTranslation=float2(0,1)*time*0.005;// float2(0,1) 水波流动的方向
OUT.Wave0.xy = uv + fTranslation*2.0;
OUT.Wave1.xy = uv*2.0 + fTranslation*4.0;
OUT.Wave2.xy = uv*4.0+fTranslation*2.0;
OUT.Wave3.xy = uv*8.0+fTranslation;
return OUT;
2 ComputeBumpTexCoord()函数的实现//为凹凸纹理计算纹理坐标
//vBumpTexA等是通过对应的Wave0即纹理坐标读取出来的像素
float3 vBumpTexA = tex2D(BumpSampler, IN.Wave0).xyz;
float3 vBumpTexB = tex2D(BumpSampler, IN.Wave1).xyz;
float3 vBumpTexC = tex2D(BumpSampler, IN.Wave2).xyz;
float3 vBumpTexD = tex2D(BumpSampler, IN.Wave3).xyz;
//vBumpTexA B C D用于计算最终的纹理坐标抖动量,用来采样反射贴图
float3 vBumpTex = 2*(vBumpTexA+vBumpTexB+vBumpTexC+vBumpTexD)/4
-float3(1,1,1);
float3 vReflBump = vBumpTex.xyz * float3(0.1, 0.1, 1.0);
return vReflBump.xy;
3 PS_Scene的实现
//为凹凸纹理计算纹理坐标
float2 bumpTexcoord=ComputeBumpTexCoord(IN);
//计算最终纹理坐标
float2 vProjUV = (IN.vProjCoord.xy/IN.vProjCoord.w);
float2 uv=float2(1,1)-vProjUV;
float4 color= tex2D(ColorSampler, uv + bumpTexcoord ).rgba;
color.a=0.6f;
return color;
效果图如下: