代碼
陡峭視差映射?Steep Parallax Mapping
float2 SteepParallaxMapping(TEXTURE2D_PARAM(heightMap, sampler_heightMap), half3 viewDirTS, half scale, float2 uv)
{
? //determine number of layers from angle between V and N
? const float minLayers = 5;
? const float maxLayers = 15;
? float numLayers = lerp(minLayers, maxLayers, abs(dot(half3(0, 0, 1), viewDirTS)));
? //height of each layer
? float layerHeight = 1.0 / numLayers;
? //depth of current layer
? float currentLayerHeight = 0;
? //shift of texture coordinates for each iteration
? half2 dtex = scale * viewDirTS.xy / viewDirTS.z / numLayers;
? //current texture coordinates
? float2 currentTextureCoords = uv;
? //get first depth from heightmap
? float heightFromTexture = 1 - SAMPLE_TEXTURE2D(heightMap, sampler_heightMap, currentTextureCoords).g;
? for(int i = 0; i < 15; i++)
? {
? ? ? if(heightFromTexture <= currentLayerHeight)
? ? ? ? break;
? ? ? //to the next layer
? ? ? currentLayerHeight += layerHeight;
? ? ? //shift texture coordinates along vector viewDirTS
? ? ? currentTextureCoords -= dtex;
? ? ? //get new depth from heightmap
? ? ? heightFromTexture = 1 - SAMPLE_TEXTURE2D(heightMap, sampler_heightMap, currentTextureCoords).g;
? }
? return currentTextureCoords - uv;
}
浮雕視差映射?Relief Parallax Mapping
float2 ReliefParallaxMapping(TEXTURE2D_PARAM(heightMap, sampler_heightMap), half3 viewDirTS, half scale, float2 uv)
{
? // determine required number of layers
? const float minLayers = 10;
? const float maxLayers = 15;
? float numLayers = lerp(minLayers, maxLayers, abs(dot(half3(0, 0, 1), viewDirTS)));
? // height of each layer
? float layerHeight = 1.0 / numLayers;
? // depth of current layer
? float currentLayerHeight = 0;
? // shift of texture coordinates for each iteration
? half2 dtex = scale * viewDirTS.xy / viewDirTS.z / numLayers;
? // current texture coordinates
? float2 currentTextureCoords = uv;
? // depth from heightmap
? float heightFromTexture = 1 - SAMPLE_TEXTURE2D(heightMap, sampler_heightMap, currentTextureCoords).g;
? for(int i = 0; i < 15; i++)
? {
? ? ? if(heightFromTexture <= currentLayerHeight)
? ? ? ? break;
? ? ? // to the next layer
? ? ? currentLayerHeight += layerHeight;
? ? ? // shift texture coordinates along vector viewDirTS
? ? ? currentTextureCoords -= dtex;
? ? ? // get new depth from heightmap
? ? ? heightFromTexture = 1 - SAMPLE_TEXTURE2D(heightMap, sampler_heightMap, currentTextureCoords).g;
? }
? ///////////////////////////////////////////////////////////
? // Start of Relief Parallax Mapping
? // decrease shift and height of layer by half
? half2 deltaTexCoord = dtex / 2;
? float deltaHeight = layerHeight / 2;
? // return to the mid point of previous layer
? currentTextureCoords += deltaTexCoord;
? currentLayerHeight -= deltaHeight;
? // binary search to increase precision of Steep Paralax Mapping
? const int numSearches = 5;
? for(int j = 0; j < numSearches; j++)
? {
? ? ? // decrease shift and height of layer by half
? ? ? deltaTexCoord /= 2;
? ? ? deltaHeight /= 2;
? ? ? // new depth from heightmap
? ? ? heightFromTexture = 1 - SAMPLE_TEXTURE2D(heightMap, sampler_heightMap, currentTextureCoords).g;
? ? ? // shift along or agains vector V
? ? ? if(heightFromTexture > currentLayerHeight) // below the surface
? ? ? {
? ? ? ? currentTextureCoords -= deltaTexCoord;
? ? ? ? currentLayerHeight += deltaHeight;
? ? ? }
? ? ? else // above the surface
? ? ? {
? ? ? ? currentTextureCoords += deltaTexCoord;
? ? ? ? currentLayerHeight -= deltaHeight;
? ? ? }
? }
? // return results
? //parallaxHeight = currentLayerHeight;? ?
? return currentTextureCoords - uv;
}
視差遮蔽映射 Parallax Occlusion Mapping
float2 ParallaxOcclusionMapping(TEXTURE2D_PARAM(heightMap, sampler_heightMap), half3 viewDirTS, half scale, float2 uv)
{
? // determine optimal number of layers
? const float minLayers = 10;
? const float maxLayers = 15;
? float numLayers = lerp(minLayers, maxLayers, abs(dot(half3(0, 0, 1), viewDirTS)));
? // height of each layer
? float layerHeight = 1.0 / numLayers;
? // current depth of the layer
? float currentLayerHeight = 0;
? // shift of texture coordinates for each layer
? half2 dtex = scale * viewDirTS.xy / viewDirTS.z / numLayers;
? // current texture coordinates
? float2 currentTextureCoords = uv;
? // depth from heightmap
? float heightFromTexture = 1 - SAMPLE_TEXTURE2D(heightMap, sampler_heightMap, currentTextureCoords).g;
? for(int i = 0; i < 15; i++)
? {
? ? ? if(heightFromTexture <= currentLayerHeight)
? ? ? ? break;
? ? ? // to the next layer
? ? ? currentLayerHeight += layerHeight;
? ? ? // shift texture coordinates along vector viewDirTS
? ? ? currentTextureCoords -= dtex;
? ? ? // get new depth from heightmap
? ? ? heightFromTexture = 1 - SAMPLE_TEXTURE2D(heightMap, sampler_heightMap, currentTextureCoords).g;
? }
? /////////////////////////////////////////////////////////
? // previous texture coordinates
? half2 prevTCoords = currentTextureCoords + dtex;
? // heights for linear interpolation
? float nextH? ? = heightFromTexture - currentLayerHeight;
? float prevH? ? = 1 - SAMPLE_TEXTURE2D(heightMap, sampler_heightMap, currentTextureCoords).g
? ? ? ? ? ? ? ? ? ? ? ? ? - currentLayerHeight + layerHeight;
? // proportions for linear interpolation
? float weight = nextH / (nextH - prevH);
? // interpolation of texture coordinates
? float2 finalTexCoords = prevTCoords * weight + currentTextureCoords * (1.0-weight);
? return finalTexCoords - uv;
? //return currentTextureCoords - uv;
}
優(yōu)缺點總結(jié)
陡峭視差映射?Steep Parallax Mapping
如果層數(shù)很多,那性能就會低袖裕。但如果層數(shù)少曹抬,就會有明顯的鋸齒現(xiàn)象產(chǎn)生,可以根據(jù)攝像機(jī)向量V和多邊形法向N之間的夾角來動態(tài)的決定層的數(shù)量急鳄。
浮雕視差映射?Relief Parallax Mapping
可以使用較少的層數(shù)進(jìn)行一階段陡峭視差檢索
二階段的二分查找算法仍然消耗性能谤民,但相較陡峭視差映射可以得到更精確的結(jié)果
視差遮蔽映射 Parallax Occlusion Mapping
視差遮蔽映射可以使用相對較少的采樣次數(shù)產(chǎn)生很好的結(jié)果。但視差遮蔽映射比浮雕視差映射更容易跳過高度圖中的小細(xì)節(jié)疾宏,也更容易在高度圖數(shù)據(jù)產(chǎn)生大幅度的變化時得到錯誤的結(jié)果张足。
原理
視差映射技術(shù)的主要任務(wù)是修改紋理坐標(biāo),讓平面看起來像是立體的坎藐。主要計算都是在Fragment Shader中進(jìn)行为牍。看看下面的圖片顺饮。水平線0.0表示完全沒有凹陷的深度吵聪,水平線1.0表示凹陷的最大深度。實際的幾何體并沒改變兼雄,其實一直都在0.0水平線上吟逝。圖中的曲線代表了高度圖中存儲的高度數(shù)據(jù)。
設(shè)當(dāng)前點(譯者:原文中用的是Fragment赦肋,片元块攒。)是圖片中用黃色方塊高亮出來的那個點,這個點的紋理坐標(biāo)是T0佃乘。向量V是從攝像機(jī)到點的方向向量囱井。用坐標(biāo)T0在高度圖上采樣,你能得到這個點的高度值H(T0)=0.55趣避。這個值不是0庞呕,所以點并不是在表面上,而是凹陷下去的程帕。所以你得把向量V繼續(xù)延長直到與高度圖定義出來的表面最近的一個交點住练。這個交點我們說它的深度就是H(T1),它的紋理坐標(biāo)就是T1愁拭。所以我們就應(yīng)該用T1的紋理坐標(biāo)去對顏色和法線貼圖進(jìn)行采樣讲逛。
所以說,所有視差映射技術(shù)的主要目的岭埠,就是要精確的計算攝像機(jī)的向量V和高度圖定義出來的表面的交點盏混。
陡峭視差映射?Steep Parallax Mapping
如圖所示把表面的深度切分成等距的若干層蔚鸥。然后從最頂端的一層開始采樣高度圖,每一次會沿著V的方向偏移紋理坐標(biāo)许赃。如果點已經(jīng)低于了表面(當(dāng)前的層的深度大于采樣出的深度)止喷,停止檢查并且使用最后一次采樣的紋理坐標(biāo)作為結(jié)果。
陡峭視差映射的工作方式在下面的圖片上舉例图焰。深度被分割成8個層启盛,每層的高度值是0.125。每層的紋理坐標(biāo)偏移是V.xy/V.z * scale/numLayers技羔。從頂層黃色方塊的位置開始檢查。
以下參照下圖給出算法的執(zhí)行步驟:
層的深度為0卧抗,高度圖深度H(T0)大約為0.75藤滥。采樣到的深度大于層的深度,所以開始下一次迭代社裆。
沿著V方向偏移紋理坐標(biāo)拙绊,選定下一層。層深度為0.125泳秀,高度圖深度H(T1)大約為0.625标沪。采樣到的深度大于層的深度,所以開始下一次迭代嗜傅。
沿著V方向偏移紋理坐標(biāo)金句,選定下一層。層深度為0.25吕嘀,高度圖深度H(T2)大約為0.4违寞。采樣到的深度大于層的深度,所以開始下一次迭代偶房。
沿著V方向偏移紋理坐標(biāo)趁曼,選定下一層。層深度為0.375棕洋,高度圖深度H(T3)大約為0.2挡闰。采樣到的深度小于層的深度,所以向量V上的當(dāng)前點在表面之下掰盘。我們找到了紋理坐標(biāo)Tp=T3是實際交點的近似點摄悯。
浮雕視差映射?Relief Parallax Mapping
浮雕視差映射升級了陡峭視差映射。
算法一階段采用陡峭視差映射得到交點前后的兩個層庆杜,和對應(yīng)的深度值射众。在下面的原理圖中這兩個層分別對應(yīng)紋理坐標(biāo)T2和T3。
算法在二階段采用二分法來進(jìn)一步改進(jìn)你的結(jié)果晃财,每一次搜索迭代可以使精確度提升一倍叨橱。
以下參照下圖給出了算法的執(zhí)行步驟:
在陡峭視差映射之后典蜕,我們知道交點肯定在T2和T3之間。真實的交點在圖上用綠點標(biāo)出來了罗洗。
設(shè)每次迭代時的紋理坐標(biāo)變化量ST愉舔,它的初始值等于向量V在穿過一個層的深度時的XY分量。
設(shè)每次迭代時的深度值變化量SH伙菜,它的初始值等于一個層的深度轩缤。
把ST和SH都除以2。
把紋理坐標(biāo)T3沿著反方向偏移ST贩绕,把層深度沿反方向偏移SH火的,得到此次迭代的紋理坐標(biāo)T4和層深度H(T4)。
(*)采樣高度圖淑倾,把ST和SH都除以2馏鹤。
如果高度圖中的深度值大于當(dāng)前迭代層的深度H(T4),則將當(dāng)前迭代層的深度增加SH娇哆,迭代的紋理坐標(biāo)沿著V的方向增加ST湃累。
如果高度圖中的深度值小于當(dāng)前迭代層的深度H(T4),則將當(dāng)前迭代層的深度減少SH碍讨,迭代的紋理坐標(biāo)沿著V的相反方向增加ST治力。
從(*)處循環(huán),繼續(xù)二分搜索勃黍,直到規(guī)定的次數(shù)宵统。
最后一步得到的紋理坐標(biāo)就是浮雕視差映射取到的近似結(jié)果。
視差遮蔽映射 Parallax Occlusion Mapping
視差遮蔽映射(POM)是陡峭視差映射的另一個改進(jìn)版本溉躲。
算法一階段采用陡峭視差映射得到交點前后的兩個層榜田,和對應(yīng)的深度值。在下面的原理圖中這兩個層分別對應(yīng)紋理坐標(biāo)T2和T3锻梳。
算法二階段對一階段獲得的兩個紋理偏移值進(jìn)行插值箭券。如原理圖所示,POM使用相交之后的層深度(0.375疑枯,陡峭視差映射停止迭代的層)辩块,上一個采樣深度H(T2)和下一個采樣深度H(T3)。從圖片中你能看到荆永,視差遮蔽映射的插值結(jié)果是在視向量V和H(T2)和H(T3)高度的連線的交點上废亭。這個交點已經(jīng)足夠接近實際交點(標(biāo)記為綠色的點)了。