本文主要解決一個(gè)問(wèn)題:
如何使用視差貼圖得到更好的表面凹凸效果?
引言
學(xué)完法線貼圖之后,我們的工具箱中又多了一樣有用的工具。工具嘛,都有自己的長(zhǎng)處和短處。法線貼圖的優(yōu)點(diǎn)是可以通過(guò)變化表面光的散射方向來(lái)增強(qiáng)表面的凹凸細(xì)節(jié),缺點(diǎn)是無(wú)法表現(xiàn)凹凸落差大的表面,比如下面這種:
上面這張圖的凹凸落差可以說(shuō)是非常清晰了。不僅有巨大的落差,還有明顯的遮擋效果,這種渲染效果不是單純的法線貼圖能實(shí)現(xiàn)的了。
為了彌補(bǔ)法線貼圖的不足,為了渲染落差更大的表面,我們就需要另外一種工具,這就是我們今天要學(xué)的視差貼圖(parallax mapping)。不過(guò),視差貼圖需要和法線貼圖一起使用才能有上圖的效果,單純的視差貼圖顯示的效果非常詭異,沒(méi)有什么實(shí)際價(jià)值。
只有視差貼圖的渲染效果慘不忍睹,像是一幅墨跡散開(kāi)的畫(huà)一樣,我就不拿出來(lái)亮瞎你的X眼了
有了視差貼圖后,配合上法線貼圖,我們就能輕松實(shí)現(xiàn)上面的效果。而且,視差貼圖的原理也很簡(jiǎn)單,如果你理解了切線空間的原理,就能很輕松的將這個(gè)工具收入到你的工具箱中。
先來(lái)看一幅對(duì)比圖:
感受到不同沒(méi),右邊的圖就是我們要實(shí)現(xiàn)的效果。好,收拾一下凌亂的思緒,我們就要開(kāi)始學(xué)習(xí)之旅了!
視差貼圖
我們?yōu)槭裁匆靡暡钯N圖?
說(shuō)白了,就是由于計(jì)算機(jī)性能的原因,沒(méi)法計(jì)算那么多面片的渲染信息。但是呢,我們又想要有逼真的顯示效果,從而發(fā)明出來(lái)的一種欺騙眼球的手段。
視差貼圖,本質(zhì)上,是一種位移貼圖(Displacement Mapping)。它的原理,是根據(jù)視線方向,對(duì)當(dāng)前看到的像素位置進(jìn)行一定的坐標(biāo)偏移,顯示偏移過(guò)后的像素顏色。這樣,就能有產(chǎn)生這種凹凸落差很大的視覺(jué)效果。光是文字說(shuō)明不太好理解,不要緊,來(lái)看這張圖:
如果我們是在觀察一個(gè)真實(shí)世界中的凹凸表面(比如磚墻,紅色表示磚塊部分),我們從眼睛的位置看過(guò)去,能看到的是圖上的點(diǎn)B。這很容易理解。但在計(jì)算機(jī)的圖形世界里,因?yàn)槲覀儾粫?huì)把磚墻這種沒(méi)什么用的模型做的很精細(xì),我們實(shí)際的磚墻模型可能就是個(gè)長(zhǎng)方體。這樣,我們的磚墻表面就是一個(gè)非常平整表面,我們能看到的點(diǎn)就是點(diǎn)A。于是,我們就需要通過(guò)某種計(jì)算方法,將真正看到的點(diǎn)A計(jì)算到應(yīng)該看到的點(diǎn)B,對(duì)點(diǎn)B的紋理采樣顯示,這樣,我們才能得到一種真實(shí)世界中的磚墻效果。
那么,我們?cè)撛趺从?jì)算點(diǎn)B的位置呢?一個(gè)令人沮喪的消息是,我們無(wú)法精確計(jì)算出點(diǎn)B的坐標(biāo)。但是,我們可以計(jì)算出一個(gè)靠近點(diǎn)B的坐標(biāo)來(lái)代替,如果這個(gè)替代點(diǎn)離點(diǎn)B足夠近,我們也能得到非常真實(shí)的效果。
如圖所示,假設(shè)點(diǎn)A的高度為H(A),點(diǎn)B的高度為H(B)(圖中未標(biāo)明H(B))。要計(jì)算多少的偏移量才能使A偏移到B呢?在2D平面中,這個(gè)偏移量無(wú)法精確的計(jì)算出來(lái),一個(gè)比較靠譜的近似方法是:從點(diǎn)A開(kāi)始的觀察向量P,將其長(zhǎng)度截?cái)嗟紿(A)大小,向量P終點(diǎn)的位置就是我們要采樣的位置,即圖中的H(P)。
運(yùn)氣好的話,H(P)正好是H(B),運(yùn)氣不好的話就不是。現(xiàn)實(shí)情況是,絕大多數(shù)情況下,我們都不會(huì)有那么好的運(yùn)氣,采樣點(diǎn)P要么比點(diǎn)B近,要么比點(diǎn)B遠(yuǎn),不過(guò)不必?fù)?dān)心,除了這種采樣方法,我們還有其他更精確的采樣方法。
明確原理之后,我們來(lái)看看如何計(jì)算。先計(jì)算向量P的長(zhǎng)度表達(dá)式。根據(jù)向量乘法規(guī)則,假設(shè)點(diǎn)A的法向量Na為(0,0,1),向量P為(Px, Py, Pz),計(jì)算兩個(gè)向量的點(diǎn)積得到的結(jié)果是:
Na * P = |Na| * |P| * cos(x)。
假設(shè)x是Na與向量P的夾角。由此展開(kāi),我們可以得到:
0 * Px + 0 * Py + 1 * Pz = 1 * 1 * cos(x)
計(jì)算角度時(shí),向量P可以轉(zhuǎn)換成單位向量。我們就得到一個(gè)最終的表達(dá)式:
Pz = cos(x)
接著,列出三角表達(dá)式:
cos(x) = Length(Na) / Length(P) => Length(P) = Length(Na) / cos(x) = Length(Na) / Pz
將表達(dá)式中的Na換成H(A),向量P的長(zhǎng)度與H(A)一樣,就得到了最終的表達(dá)式:
Length(P) = H(A) / Pz
要注意的是,之所以能將Na假設(shè)成(0,0,1),是因?yàn)槲覀儺?dāng)前的計(jì)算空間是切線空間。這個(gè)知識(shí)點(diǎn)在上一篇法線貼圖的文章里有詳細(xì)介紹,出于新手保護(hù)的目的,我們來(lái)復(fù)習(xí)一下:
所謂切線空間,就是以表面法向量為z軸,紋理坐標(biāo)UV為系統(tǒng)x軸和y軸所組成的空間。這個(gè)空間存在的唯一目的是在這個(gè)空間中進(jìn)行法線貼圖、視差貼圖等等的工作十分方便。
深度圖vs高度圖
在實(shí)際的應(yīng)用中,我們通常會(huì)用深度圖來(lái)代替高度圖進(jìn)行位移計(jì)算。因?yàn)樵谄矫嫔希疃缺雀叨雀袑?shí)際意義。這點(diǎn)小改變對(duì)我們上述的原理不會(huì)造成任何影響,只是在最后的偏移計(jì)算時(shí),需要把+向量的操作改成-向量。這點(diǎn)細(xì)節(jié)部分我會(huì)在代碼中注明,所以不用擔(dān)心。
我們計(jì)算的向量P現(xiàn)在反過(guò)來(lái)了,如果是高度圖,我們的紋理坐標(biāo)計(jì)算方法是:點(diǎn)A坐標(biāo)+向量P。現(xiàn)在變成深度圖之后,我們的計(jì)算方法是:點(diǎn)A坐標(biāo)-向量P。其余的不變,這點(diǎn)不難理解吧?
代碼實(shí)現(xiàn)
和上一章的法線貼圖計(jì)算方式相比,最大的不同就是我們不在世界空間中計(jì)算了。所以,我們采用的方法是將觀察位置、光源位置、片元位置轉(zhuǎn)換到切線空間,在切線空間中進(jìn)行視差的計(jì)算。講到這里,你應(yīng)該猜到了,沒(méi)錯(cuò),我們就是在上一章中方法2的基礎(chǔ)上進(jìn)行修改。
頂點(diǎn)著色器代碼不需要改變,直接復(fù)制過(guò)來(lái)就行,取一個(gè)新文件名,例如:
withParallaxMap.vs
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
layout (location = 3) in vec3 aTangent;
layout (location = 4) in vec3 aBitangent;
out VS_OUT {
vec3 FragPos;
vec2 TexCoords;
vec3 TangentLightPos;
vec3 TangentViewPos;
vec3 TangentFragPos;
} vs_out;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
uniform vec3 lightPos;
uniform vec3 viewPos;
void main()
{
vs_out.FragPos = vec3(model * vec4(aPos, 1.0));
vs_out.TexCoords = aTexCoords;
mat3 normalMatrix = transpose(inverse(mat3(model)));
vec3 T = normalize(normalMatrix * aTangent);
vec3 N = normalize(normalMatrix * aNormal);
T = normalize(T - dot(T, N) * N);
vec3 B = cross(N, T);
mat3 TBN = transpose(mat3(T, B, N));
vs_out.TangentLightPos = TBN * lightPos;
vs_out.TangentViewPos = TBN * viewPos;
vs_out.TangentFragPos = TBN * vs_out.FragPos;
gl_Position = projection * view * model * vec4(aPos, 1.0);
}
主要工作集中在片元著色器中。先把上一章中方法二片元著色器復(fù)制過(guò)來(lái),改名成withParallaxMap.fs。緊接著,添上接收深度圖的代碼:
uniform sampler2D depthMap;
計(jì)算紋理偏移時(shí),我們將計(jì)算的方法封裝到一個(gè)函數(shù)中,取名ParallaxMapping。這函數(shù)接收兩個(gè)參數(shù)作為輸入,分別是紋理坐標(biāo)和是視向量,計(jì)算完成后,將偏移后的紋理坐標(biāo)作為返回值輸出:
vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir)
{
float depth = texture(depthMap, texCoords).r;
vec2 p = viewDir.xy * depth / viewDir.z * 0.1;
return texCoords - p;
}
最關(guān)鍵的是中間一行計(jì)算p向量的代碼,其中:viewDir.xy表示視向量的紋理坐標(biāo),depth/viewDir.z就是上面的H(A)/Pz,0.1是一個(gè)修正值。因?yàn)槲覀兊挠?jì)算都是近似計(jì)算,所以必須要有一個(gè)修正值來(lái)進(jìn)行調(diào)整,調(diào)整到最合適的修正值。這過(guò)程可能很繁瑣,你可以用一個(gè)uniform變量來(lái)代替,在主函數(shù)中接受鍵盤(pán)的輸入來(lái)調(diào)整這個(gè)數(shù)值,看看最終效果如何。
關(guān)于修正值,有些文獻(xiàn)中會(huì)對(duì)采樣后的深度值進(jìn)行一次修正,一種修正的方程是h = h * s + b。s表示縮放因子,b表示修正值。這里我直接用了一個(gè)數(shù)字來(lái)0.1來(lái)代替。因?yàn)榉凑呀?jīng)是修正值了,具體數(shù)據(jù)無(wú)法精確計(jì)算,還不如直接進(jìn)行調(diào)整找到最好的數(shù)據(jù)。事實(shí)上,連除以viewDIr.z這個(gè)操作都可以省略掉,直接乘上修正值,調(diào)整修正值到最佳效果就好了。
在主函數(shù)中,我們的代碼就變成:
void main()
{
//采樣視差貼圖
vec3 viewDir = normalize(fs_in.TangentViewPos - fs_in.TangentFragPos);
vec2 texCoords = ParallaxMapping(fs_in.TexCoords, viewDir);
// 采樣法線貼圖
vec3 normal = texture(normalMap, texCoords).rgb;
// 轉(zhuǎn)換法向量到[-1,1]范圍
normal = normalize(normal * 2.0 - 1.0); // 此向量是切線空間中的向量
...
// 采樣漫反射
vec3 color = texture(diffuseMap, texCoords).rgb;//fs_in.TexCoords).rgb;
...
// 鏡面高光
//vec3 viewDir = normalize(fs_in.TangentViewPos - fs_in.TangentFragPos);
...
}
在main.cpp中加上一些邊緣代碼后,編譯運(yùn)行,你就能看到這樣的效果:
非常好,這就是我們上面的效果,不過(guò)還有點(diǎn)瑕疵,就在邊緣的部分,請(qǐng)看:
超出紋理坐標(biāo)范圍的坐標(biāo)我們可以直接丟棄,還記得丟棄的代碼嗎?沒(méi)錯(cuò),就是discard:
if(texCoords.x > 1.0 || texCoords.y > 1.0 || texCoords.x < 0.0 || texCoords.y < 0.0)
discard;
將上面的代碼添加到調(diào)用ParallaxMapping函數(shù)之后,再次運(yùn)行程序,現(xiàn)在的效果好多了。
如果顯示不正確,請(qǐng)參考這里的源碼進(jìn)行修改。
等等,這就完了嗎?當(dāng)然不是,這只是最基本的方法,這種方法在我們現(xiàn)在的角度觀察是沒(méi)問(wèn)題,換一個(gè)平一點(diǎn)的角度問(wèn)題就大了。
看到?jīng)],在一個(gè)比較水平的角度上看,或者高度圖的落差一大,表面的顯示一塌糊涂。出現(xiàn)這種情況的原因是我們的采樣方式太粗糙了,只是計(jì)算了一個(gè)采樣點(diǎn)就采樣了。這種粗糙的采樣方式在高度變化劇烈或者角度刁鉆的情況下就崩盤(pán)了。解決的方法也很簡(jiǎn)單,既然采樣一次太少,我們多采樣幾次不就行了?這種多次采樣的方式被稱(chēng)作陡峭視差貼圖。
陡峭視差貼圖(Steep Parallax Map)
陡峭視差貼圖的原理,是設(shè)定一個(gè)采樣層數(shù),每一層都對(duì)相應(yīng)紋理采樣,當(dāng)采樣到小于當(dāng)前層深度的紋理坐標(biāo)時(shí),返回此紋理坐標(biāo)。如圖:
研究一下上面的圖:我們把整個(gè)深度分成了5層,分別是:0.2、0.4、0.6、0.8和1.0。陡峭視差貼圖是這樣進(jìn)行計(jì)算的,首先從第一層,也就是深度值為0.2的那一層開(kāi)始,采樣當(dāng)前的深度值為1.0,這個(gè)值大于當(dāng)前的層深度0.2,所以需要繼續(xù)采樣。然后,采樣第二層(0.4),這層的深度值為0.73,它也大于層深度,所以這個(gè)深度值也不能用,需要繼續(xù)采樣。于是到了第三層(0.6)這次采樣的深度值是0.37,它小于當(dāng)前層深度,所以,這個(gè)紋理坐標(biāo)就會(huì)被采用。
讓我們用代碼來(lái)實(shí)現(xiàn):
vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir)
{
//將整個(gè)深度區(qū)域分為10層
const float numLayers = 10;
//每一層的深度
float layerDepth = 1.0 / numLayers;
//當(dāng)前層深度
float currentLayerDepth = 0.0;
//紋理坐標(biāo)的完整范圍
vec2 p = viewDir.xy * 0.1;
//每一層紋理的變化值
vec2 deltaTexCoords = p / numLayers;
vec2 currentTexCoords = texCoords; //起始紋理坐標(biāo)
float currentDepthMapValue = texture(parallaxMap, currentTexCoords).r; //起始深度
while(currentLayerDepth < currentDepthMapValue)
{
currentTexCoords -= deltaTexCoords; //我們的p向量是指向眼睛的,而紋理值需要往反方向變化,所以這里是-deltaTexCoords
currentDepthMapValue = texture(parallaxMap, currentTexCoords).r;
currentLayerDepth += layerDepth;
}
return currentTexCoords;
}
在我們的實(shí)現(xiàn)中,我們將整個(gè)深度范圍分成了10層。紋理范圍就直接用了0.1這個(gè)修正值,因?yàn)橐呀?jīng)不需要height這個(gè)值了。具體的計(jì)算流程和之前講的原理一樣,就是一層層地采樣比較深度值,如果采樣的深度值小于當(dāng)前的層深度,那么采樣結(jié)束,如果大于,就繼續(xù)采樣。要注意的是紋理坐標(biāo)的變化是減去當(dāng)前的向量變化量,因?yàn)橄蛄縋是指向眼睛的,我們需要往它的反方向增加。
等等,先下載這次運(yùn)行要用的圖片:紋理圖、法線貼圖、視差貼圖。
現(xiàn)在可以編譯運(yùn)行了,趕緊動(dòng)手,你會(huì)看到這樣的效果:
這就明顯沒(méi)有上圖中的塌陷效果了,但是出現(xiàn)了另一個(gè)問(wèn)題:鋸齒,非常明顯的鋸齒。出現(xiàn)鋸齒的原因想必我不說(shuō)你也知道,就是采樣數(shù)量不夠,如果我們把層數(shù)改成100,會(huì)是什么效果:
看吧,鋸齒效果很明顯就消失了,整個(gè)場(chǎng)景看上去都不錯(cuò)。但是!你確定要分100層嗎?每個(gè)坐標(biāo)都來(lái)100層的采樣我們的電腦基本上也就壽終正寢了。我們當(dāng)然不能讓這種事情發(fā)生,于是,聰明的前輩先驅(qū)們想出了2種方法,分別是:視差遮蔽貼圖(Parallax Occlusion Mapping)和浮雕視差貼圖(Relief Parallax Mapping)。這兩種方法都能以相對(duì)100層采樣較小的代價(jià)來(lái)實(shí)現(xiàn)平滑表面的效果,比較來(lái)說(shuō),浮雕視差貼圖的效果更好,但是消耗更大,視差遮蔽貼圖的效果不如浮雕視差貼圖,但它的消耗小。具體的需要就看你的選擇了。出于學(xué)習(xí)目的,這兩種方法我們都要來(lái)實(shí)現(xiàn)一次。先來(lái)看視差遮蔽貼圖。
視差遮蔽貼圖(Parallax Occlusion Mapping)
視差遮蔽貼圖的原理,就是取與交點(diǎn)相鄰的兩個(gè)層的層深度和采樣深度,計(jì)算出兩個(gè)深度值的權(quán)重,根據(jù)權(quán)重采樣兩個(gè)紋理坐標(biāo)之間某個(gè)位置的紋理坐標(biāo),如下圖:
與交點(diǎn)相鄰的層深度和采樣深度分別是0.6(層深度)、0.37(采樣深度)和0.4(層深度)、0.73(采樣深度)。最終的紋理坐標(biāo)也必定在這兩個(gè)坐標(biāo)之間,只是到底取哪個(gè)坐標(biāo)呢?我們的方法,就是取圖中的P點(diǎn)坐標(biāo)。先計(jì)算H(T2)和H(T3)的值,分別是0.33和0.23,T3的權(quán)重就是0.23 / (0.33+0.23)=0.41,然后用T3的紋理坐標(biāo)乘上(1-0.41),T2的紋理坐標(biāo)乘上0.41,兩個(gè)坐標(biāo)值相加,就得到了我們想要的紋理坐標(biāo)。
說(shuō)了這么多,趕緊來(lái)修改陡峭視差貼圖中的源碼,實(shí)現(xiàn)視差遮蔽貼圖的效果吧:
vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir)
{
... //陡峭視差貼圖部分代碼
//前一個(gè)紋理坐標(biāo)點(diǎn)
vec2 prevTexCoords = currentTexCoords + deltaTexCoords;
//當(dāng)前深度差
float afterDepth = currentLayerDepth - currentDepthMapValue;
//前一個(gè)坐標(biāo)點(diǎn)深度差
float beforeDepth = texture(parallaxMap, prevTexCoords).r - (currentLayerDepth - layerDepth);
//當(dāng)前坐標(biāo)點(diǎn)的深度值權(quán)重
float weight = afterDepth / (afterDepth + beforeDepth);
vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight);
return finalTexCoords;
}
前面的代碼不需要改,在后面加上視差遮蔽效果的實(shí)現(xiàn)。先算出前一個(gè)點(diǎn)的紋理坐標(biāo),計(jì)算出兩個(gè)點(diǎn)的深度差,根據(jù)我們之前的原理,計(jì)算出權(quán)重,最后計(jì)算出最終紋理坐標(biāo)。代碼應(yīng)該非常直觀,講這么多已屬?gòu)U話連篇,趕緊編譯運(yùn)行:
怎么樣,效果不錯(cuò)吧,跟100層采樣效果比也有一拼。
浮雕視差貼圖(Relief Parallax Mapping)
浮雕視差貼圖的原理和陡峭視差貼圖類(lèi)似,不過(guò)它更聰明。它是在獲取到了交點(diǎn)左右兩個(gè)相鄰點(diǎn)的紋理坐標(biāo)和深度信息之后(這點(diǎn)和視差遮蔽貼圖類(lèi)似),再對(duì)其進(jìn)行逼近,采用的方法是2分漸進(jìn)。就是確定了左右兩個(gè)坐標(biāo)點(diǎn)之后,取兩坐標(biāo)的中點(diǎn)位置,用這個(gè)坐標(biāo)來(lái)采樣深度信息,如果這個(gè)深度信息小于層深度,那么這個(gè)中點(diǎn)坐標(biāo)就取代原有的左坐標(biāo)點(diǎn);如果這個(gè)深度信息大于層深度,那么這個(gè)中點(diǎn)坐標(biāo)就取代原有的右坐標(biāo)點(diǎn)。然后繼續(xù)取中點(diǎn),再做比較,如此往復(fù)一定次數(shù)之后,采樣到的紋理坐標(biāo)就非常接近真實(shí)坐標(biāo)了。像這樣:
把原理翻譯成代碼就是:
vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir)
{
... //陡峭視差貼圖的源碼
vec2 dtex = deltaTexCoords / 2; //紋理步長(zhǎng)取半
float deltaLayerDepth = layerDepth / 2; //深度步長(zhǎng)取半
//計(jì)算當(dāng)前的紋理和層深度
currentTexCoords += dtex;
currentLayerDepth -= deltaLayerDepth;
const int numSearches = 10; //進(jìn)行10次2分漸進(jìn)
for (int i = 0; i < numSearches; ++i) {
//每次紋理步長(zhǎng)和深度步長(zhǎng)都會(huì)減半
dtex /= 2;
deltaLayerDepth /= 2;
//采樣當(dāng)前紋理
currentDepthMapValue = texture(parallaxMap, currentTexCoords).r;
if (currentDepthMapValue > currentLayerDepth) {
//如果當(dāng)前深度大于層深度,往左逼近
currentTexCoords -= dtex;
currentLayerDepth += deltaLayerDepth;
}
else {
//如果當(dāng)前深度小于層深度,往右逼近
currentTexCoords += dtex;
currentLayerDepth -= deltaLayerDepth;
}
}
return currentTexCoords;
}
代碼非常直觀易懂,不多解釋?zhuān)苯泳幾g運(yùn)行:
效果不錯(cuò),和100層陡峭視差貼圖效果類(lèi)似,和視差遮蔽貼圖的效果比就沒(méi)啥優(yōu)勢(shì)了,因?yàn)槲覀冎贿M(jìn)行了10次2分漸進(jìn),如果多進(jìn)行幾次,比如50次,就能從細(xì)微處看到區(qū)別了。只是,這個(gè)代價(jià)跟100層采樣區(qū)別也不大,所以還是推薦視差遮蔽貼圖,效果又好速度又快。
最后,老規(guī)矩,上傳全部源碼以供參考。
嘮嘮嗑
在學(xué)習(xí)視差貼圖的時(shí)候,我遇到的最大困難就是,看到一段給出的代碼,里面計(jì)算方法跟前面講的原理差了十萬(wàn)八千里,完全不知道為什么要進(jìn)行那一步計(jì)算。就比如說(shuō)除以viewDir.z這個(gè)操作,為啥呢?里面的推導(dǎo)過(guò)程呢?完全沒(méi)有,導(dǎo)致我在這一個(gè)問(wèn)題上卡了3天,真糾結(jié)。所幸最后還是被我腦補(bǔ)出原理來(lái),不得不說(shuō)真是幸運(yùn)。還有一個(gè)問(wèn)題就是計(jì)算偏移的公式不同的資料中不一樣,都能得到準(zhǔn)確的效果,就是實(shí)現(xiàn)的方式每個(gè)作者都有自己的理解。這點(diǎn),在文章中我也給出了自己的見(jiàn)解,算是對(duì)自己有個(gè)交代。如果我的理解有誤,歡迎各位讀者嚴(yán)厲指正。
另外,還有一個(gè)問(wèn)題沒(méi)有解決,就是在進(jìn)行視差遮蔽貼圖計(jì)算的時(shí)候,為什么是前一個(gè)紋理坐標(biāo)乘上當(dāng)前的權(quán)重,當(dāng)前坐標(biāo)卻乘上(1-當(dāng)前權(quán)重)?百思不得其解,還希望路過(guò)的高手可以解答一下,萬(wàn)分感謝!
總結(jié)
回顧一下這一章中學(xué)的內(nèi)容。這一章我們就是用視差貼圖來(lái)實(shí)現(xiàn)法線貼圖不能實(shí)現(xiàn)的凹凸效果明顯的表面。原理是在當(dāng)前看到的位置上進(jìn)行一個(gè)坐標(biāo)偏移,偏移到現(xiàn)實(shí)中應(yīng)該落在的坐標(biāo)上,然后對(duì)這個(gè)坐標(biāo)進(jìn)行紋理采樣,獲得逼真的效果。具體的實(shí)現(xiàn)方式是:
- 采用當(dāng)前紋理深度作為偏移長(zhǎng)度進(jìn)行偏移
- 采用多層漸近的方式獲取最合適的偏移
- 采用多層漸近,再加上線性插值的方式來(lái)獲取最合適的偏移
- 采用多層漸近,再加上2分漸近的方式來(lái)獲取最合適的偏移
并沒(méi)有什么神奇的地方,好了,學(xué)完收工,我們放松一下!