Unity眼球shader筆記

正面

側(cè)面

虹膜大小參數(shù)

瞳孔大小參數(shù)

折射率參數(shù)

視差程度參數(shù)

模型

眼球結(jié)構(gòu)

我們可以明顯看到眼球前方有一個(gè)凸起的部位,包含了角膜房水等結(jié)構(gòu),因此在建模時(shí)需要體現(xiàn)出這一效果。



UV映射直接采用了Y軸平面映射。


虹膜大小調(diào)整

這里將模型自帶的UV首先轉(zhuǎn)換成關(guān)于原點(diǎn)中心對稱,比較方便計(jì)算。然后將UV到原點(diǎn)長度大于設(shè)定值的返回鞏膜顏色,小于設(shè)定值的我們做虹膜的渲染,并且使用一個(gè)新的UV(虹膜邊緣UV長度為0.5,中心依然為原點(diǎn))方便之后的計(jì)算。

float2 sUV = i.uv - float2(0.5, 0.5);
float2 sUVIris = sUV / _IrisRadius / 2;

虹膜視差效果

由于虹膜和瞳孔的部分是處于角膜下一定距離,并且房水和角膜作為介質(zhì)有一定的的折射率,因此這里我們需要引入一個(gè)視差效果來給予眼球立體感。

我將所有的計(jì)算首先轉(zhuǎn)換到模型空間下。這里如果將虹膜視為一個(gè)平面,(模型采用平面投影UV,不過此處為了讓計(jì)算更簡單一點(diǎn)采用表面法線和虹膜平面的交點(diǎn)作為原UV)我們可以得到如下的光路圖:


只要求得了偏移量x以及偏移方向,我們就能得到偏移后的UV,使用此UV采樣貼圖就可得到折射后產(chǎn)生的視差效果。

具體計(jì)算方式是:


在有些情況下這里的UpR可能成為一個(gè)負(fù)值,不過最終我們得到的偏移量是一個(gè)標(biāo)量,這個(gè)矯正需要用偏移方向向量來做。至于式子中的d,可以用一個(gè)高度紋理采樣,越靠近虹膜邊緣處折射越小,我這里是在shader里直接計(jì)算了球面減去平面的高度差。

float cosUpN = dot(objectNormal, float3(0,1,0));
float sinUpN = sqrt(1 - cosUpN * cosUpN);
float cosVN = dot(objectNormal,  objectViewDir);
float sinVN = sqrt(1 - cosVN * cosVN);
float sinRN = sinVN / _IOR;
float cosRN =  sqrt(1 - sinRN * sinRN);
float cosUpR = sinUpN * sinRN + cosUpN * cosRN;
float2 sUVIris = sUV / _IrisRadius / 2;
float d1 = sqrt(1 - sUVIris.x * sUVIris.x - sUVIris.y * sUVIris.y) - 0.86603;
float d2 = sqrt(0.3025 - sUVIris.x * sUVIris.x - sUVIris.y * sUVIris.y) - 0.22913;
float2 deltaUV = _Distortion * (d2 - d1) * sinRN / cosUpR * uvDirection;

一開始我直接將偏移方向設(shè)定為視線方向在虹膜平面上的投影方向向量,不過這實(shí)際上是不對的,因?yàn)閷?shí)際偏移方向也是和眼球表面法線相關(guān)的。



如圖所示,圖中偏移方向應(yīng)該向左,然而視線在平面上的投影是向右的。這個(gè)問題花費(fèi)了我不少時(shí)間排除……最終我使用的計(jì)算方式是:

float3 T = cross(objectViewDir,objectNormal);
float3 D = cross(float3(0,1,0),T);
float2 uvDirection = float2(D.x,D.z);

這里采用了叉乘的方式矯正了VN空間關(guān)系導(dǎo)致的UV偏移方向反轉(zhuǎn)。

使用得到的偏移量乘以偏移方向作為UVOffset,就能夠產(chǎn)生比較令人信服的折射效果。

最后我們會得到一些虹膜紋理并不存在的UV(UV長度大于0.5),于是我們還要引入一張虹膜的遮罩mask讓這些片段去采樣鞏膜紋理,以形成自然過渡的虹膜邊緣。

瞳孔大小調(diào)節(jié)

我們需要對偏移后的虹膜UV做一定的remap處理達(dá)到這個(gè)效果。首先求UV到原點(diǎn)的長度,以及方向向量,然后經(jīng)過一定對長度的處理得到一個(gè)新的長度,乘回方向向量就是我們要的原始UV數(shù)據(jù)。

float2 RemapUV(float2 uv)
{
    float lengthUV = length(uv);
    float2 uvNormalized = uv / lengthUV ;
    float newLength = 0;
    if (lengthUV  < _PupilRadius) 
    {
        newLength = lengthUV / _PupilRadius * 0.14;
    } else
    {
        newLength = (lengthUV - _PupilRadius) / (0.5 - _PupilRadius) * 0.36 + 0.14;
    }
    return uvNormalized * newLength;
}

上一段代碼中的0.14是原始紋理中瞳孔的半徑,這和我們采用的虹膜紋理有關(guān),需要手動調(diào)節(jié)。

高光

這個(gè)沒什么好說的,我目前只簡單的在Base Pass和Add Pass中寫了Phong高光公式。


Github:https://github.com/techizgit/UnityPlayground

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容