这个例子主要说明ParallaxOcclusionMapping,同时采用了另外两种方法作为对比:Bump mapping和Parallax mapping with offset limiting。
从简单到复杂为BUMPMAP、PM、POM,本文按此顺序说明。
Bump mapping
这种方法就是简单的法向贴图。
VS输入为pos、tex、normal
需要计算出输入到PS的vLightTS和vViewTS,其中vViewTS计算镜面反射光使用。
世界空间:
vLightWS=g_LightDir
vViewWS=g_vEye-vPositionWS
转换到切线空间:
vLightTS = mul( vLightWS, mWorldToTangent );
vViewTS = mul( mWorldToTangent, vViewWS );
PS输入为tex、vLightTS、vViewTS
计算结果通过公用函数ComputeIllumination:
// 采样法向贴图得到像素法向
float3 vNormalTS = normalize( tex2D( tNormalHeightMap, texCoord ) * 2 - 1 );
// 采样纹理贴图
float4 cBaseColor = tex2D( tBase, texCoord );
// Compute diffuse color component:
float3 vLightTSAdj = float3( vLightTS.x, -vLightTS.y, vLightTS.z );
float4 cDiffuse = saturate( dot( vNormalTS, vLightTSAdj )) * g_materialDiffuseColor;
// Compute the specular component if desired:
float4 cSpecular = 0;
if ( g_bAddSpecular )
{
float3 vReflectionTS = normalize( 2 * dot( vViewTS, vNormalTS ) * vNormalTS - vViewTS );
float fRdotL = saturate( dot( vReflectionTS, vLightTSAdj ));
cSpecular = saturate( pow( fRdotL, g_fSpecularExponent )) * g_materialSpecularColor;
}
// Composite the final color:
float4 cFinalColor = (( g_materialAmbientColor + cDiffuse ) * cBaseColor + cSpecular ) * fOcclusionShadow;
return cFinalColor;
Parallax mapping with offset limiting
这种方法可以参考文章。这篇原始论文里面描述了这个带限制的视差偏移技术的原理。改算法原理是计算纹理坐标的时候考虑到渲染像素的高度,计算合适的纹理坐标,而不是直接采用vertexshader提供的纹理坐标。
这种方法的计算方法与上面的bumpmap基本相同,仅有一点区别:在调用ComputeIllumination计算像素值之前,根据物体存储在法向贴图中的高度信息(alpha分量),对纹理坐标进行偏移。
具体计算过程如下(只列出与bumpmap不同部分)
// alpha通道保存的是高度信息
float fCurrentHeight = tex2D( tNormalHeightMap, i.texCoord ).a;
// 根据程序设置计算当前高度的实际偏移值:
float fHeight = fCurrentHeight * g_fHeightMapScale + sfHeightBias;
// 如果需要限制偏移值,把这行注掉
fHeight /= vViewTS.z;
// 根据视觉方向在切线空间中的坐标值进行偏移,制造视差效果:
float2 texSample = i.texCoord + vViewTS.xy * fHeight;
最后给出一个例子运行中的一个可能数据:
假设物体是一个法向正对摄像机的平面,可以分3种情况说明:
1.对于摄像机正对的平面上的点,此时vViewTS=(0,0,1)
2.对于摄像机正对点左边的点,此时vViewTS的一个可能值为(0.1,0,0.9),根据下面算法,这个点计算纹理坐标的时候取一个稍微偏右的坐标值
3.对于摄像机正对点右边的点,此时vViewTS的一个可能值为(-0.1,0,0.9),根据下面算法,这个点计算纹理坐标的时候取一个稍微偏左的坐标值
另一个调试例子:
g_LightDir:{-0.5, 0, -0.866}
物体沿y轴转90°,这样正面对着摄像机,对可见点调试如下:
vNormal{0, 0, -1}
vTangent{1, 0, 0}
vBinormal{0, 1, 0}
vLightTS{-0.5, 0, 0.866}
ParallaxOcclusionMapping
上面的方法PM计算一个大致的便宜,不是很准确,POM则计算精确的偏移纹理坐标,同时还可以生成阴影,缺点是算法复杂度大,PS中需要采样周围纹理进行计算,相应的论文:
PPT:Practical Parallax Occlusion Mapping for Highly Detailed Surface Rendering
论文:Dynamic Parallax Occlusion Mapping with Approximate Soft Shadows
转载一篇博客。里面讲的挺好