概念
我們經(jīng)常會(huì)聽(tīng)到立方體貼圖、經(jīng)緯圖、全景圖這樣的概念,它們描繪的都是一種能從四周觀(guān)察到周?chē)h(huán)境的高動(dòng)態(tài)范圍(HDR)的圖片,統(tǒng)稱(chēng)為環(huán)境貼圖。
環(huán)境貼圖是一種用于模擬高度反射物體表面反映周?chē)h(huán)境的技術(shù),常見(jiàn)的環(huán)境貼圖主要有三種:立方體貼圖(CubeMap)、經(jīng)緯度全景圖(LatLongMap)以及球面環(huán)境圖(SphereMap)。它們之間本質(zhì)上沒(méi)有什么區(qū)別,只不過(guò)排版布局形式不一樣而已,下面是環(huán)境貼圖常見(jiàn)的集中布局形式。

unity中常用的環(huán)境貼圖是立方體貼圖(CubeMap),它采用的是十字形的布局形式,
用一個(gè)簡(jiǎn)單的比喻來(lái)解釋立方體貼圖的形成:物體的中心位置架設(shè)一臺(tái)全景相機(jī),在場(chǎng)景中表現(xiàn)為一個(gè)長(zhǎng)方體, 長(zhǎng)方體的每個(gè)面都會(huì)拍攝它正前方的場(chǎng)景圖像 , 這樣就以相機(jī)為中心,就能獲取到它上、下、前、后、左、右六個(gè)方向的圖像, 這六張圖片會(huì)被存儲(chǔ)為一個(gè)立方體貼圖(CubeMap),提供給具有反射材質(zhì)的物體使用。

設(shè)置
unity中新建立方體貼圖有兩種方法:第一種是將hdr格式的全景圖片導(dǎo)入項(xiàng)目中,設(shè)置圖片的Texture Shape 為cube,這種方式是最方便快捷的。

第二種使用一種最古老的方式:create/Legacy/Cubemap,這種方式需要準(zhǔn)備六張同一位置不同方向角度(前后、上下、左右)的圖片,指定后unity會(huì)為自動(dòng)生成一張立方體貼圖。


采樣
環(huán)境貼圖的采樣過(guò)程也很好理解,一束光照射到物體表面時(shí)會(huì)產(chǎn)生反射,反射光線(xiàn)會(huì)與立方體的其中一個(gè)面產(chǎn)生一個(gè)交點(diǎn),只需要對(duì)這個(gè)交點(diǎn)進(jìn)行采樣,就能得到頂點(diǎn)的顏色像素值。

立方體貼圖有六個(gè)面,首先需要判斷頂點(diǎn)的反射光線(xiàn)與哪個(gè)面相交,這個(gè)判斷方法非常簡(jiǎn)單:頂點(diǎn)坐標(biāo)哪個(gè)分量的絕對(duì)值最大,頂點(diǎn)反射光就與哪個(gè)面相交。
比如頂點(diǎn)坐標(biāo)V(-0.5,0.3,-0.1),分量絕對(duì)值最大是-5,因此這個(gè)頂點(diǎn)的反射光與-x指向的面相交。

知道了相交面,下一步就是求相交點(diǎn),相交面標(biāo)準(zhǔn)化,它的其中一個(gè)面的分量值是確定的。比如,立方體貼圖-x軸指向的面,它的x軸坐標(biāo)是-1,那么只要讓反射矢量的坐標(biāo)乘以某個(gè)標(biāo)量,讓矢量的x分量也等于-1,矢量的yz分量就是交點(diǎn)的yz值。
舉個(gè)例子反射矢量是(-0.5,0.3,-0.1),乘以2后等于(-1,0.6,-0.2),那么相交點(diǎn)的坐標(biāo)就是(-1,0.6,-0.2)。
由于立方體貼圖采樣只關(guān)注方向而忽略了位置,因此它在平坦反射表面上的效果很不真實(shí),相對(duì)的,它在曲面上可以取得較好的視覺(jué)效果。
代碼
下面就來(lái)看看在代碼中如何實(shí)現(xiàn)立方體貼圖的采樣。根據(jù)采樣原理的理解,要獲取到頂點(diǎn)的切線(xiàn)空間坐標(biāo)、光源方向、法線(xiàn)方向和反射方向。具體代碼如下:
half3 normal_dir = normalize(i.normal_world);
half3 normalData = UnpackNormal(tex2D(_NormalMap,i.uv));
//切線(xiàn)方向
half3 tangent_dir = normalize(i.tangent_world);
// 雙切線(xiàn)方向
half3 binormal_dir = normalize(i.binormal_world);
// 法線(xiàn)方向
normal_dir = normalize(tangent_dir * normalData.x + binormal_dir * normalData.y + normal_dir*normalData.z);
// 觀(guān)察方向
half3 view_dir = normalize(_WorldSpaceCameraPos.xyz - i.pos_world);
// 反射方向
half3 reflect_dir = reflect(-view_dir, normal_dir);
得到反射方向后,直接使用texCUBE方法,傳入反射矢量,對(duì)貼圖進(jìn)行采樣
// properties
_CubeMap("Cube Map", Cube) = "white"{}
//var
samplerCUBE _CubeMap;
float4 _CubeMap_HDR;
//frag
half4 colorCubeMap = texCUBE(_CubeMap,reflect_dir);
half3 env_color = DecodeHDR(colorCubeMap,_CubeMap_HDR);
通過(guò)以上兩步,基本能實(shí)現(xiàn)對(duì)立方體貼圖采樣的功能。

立方體貼圖擁有豐富的顏色細(xì)節(jié),直接采樣產(chǎn)生的效果可能會(huì)過(guò)曝,因此通常會(huì)配合Bloom 、ACES ToneMapping等后期技術(shù)來(lái)使用。

反射探針
講了這么多立方體貼圖底層技術(shù)的實(shí)現(xiàn),但實(shí)際項(xiàng)目中可能并不需要我們這么做。在Unity項(xiàng)目中,我們可以為物體添加反射探針(ReflectionProbe)來(lái)實(shí)現(xiàn)物體的反射效果,反射探針就是根據(jù)立方體貼圖的技術(shù)原理封裝而成的。
默認(rèn)情況下,Unity設(shè)置了一個(gè)全局反射探針,我們只需要在window-rendering-light這里點(diǎn)擊"Generate Lighting",項(xiàng)目就會(huì)自動(dòng)烘焙一張?zhí)炜蘸凶拥牧⒎襟w貼圖保存在場(chǎng)景目錄下

可在Environment中調(diào)節(jié)全局反射探針的效果。

另外,也可以"右鍵/light/reflection Probe"中添加局部反射探針,點(diǎn)擊Bake,此時(shí)反射探針會(huì)自動(dòng)生成一個(gè)天空盒子的環(huán)境貼圖。

但此時(shí),反射探針并不能反射周?chē)渌矬w,如果要反射其他物體,那么其他物體需要勾選“reflection Probe Statics”,然后重新Bake


在優(yōu)先級(jí)上,局部反射探針的優(yōu)先級(jí)別要高于全局反射探針。