WebGL學習之法線貼圖

原文地址:WebGL學習之法線貼圖html

實際效果請看demo:紋理貼圖git

法線貼圖

爲了增長額外細節,提高真實感,咱們使用了漫反射貼圖和高光貼圖,它們都是向三角形進行附加紋理。可是從光的視角來看是表面法線向量使表面被視爲平坦光滑的表面。以光照算法的視角考慮的話,只有一件事決定物體的形狀,那就是垂直於它的法線向量。磚塊表面只有一個法向量,表面徹底根據這個法向量被以一致的方式照亮。若是每一個片元都用不一樣的法線會怎樣?這樣咱們就能夠根據表面細微的細節對法線向量進行改變;這樣就會得到一種表面看起來要複雜得多的幻覺: github

表面法線

每一個片元使用了本身的法線,咱們就可讓光照相信一個表面由不少微小的(垂直於法線向量的)平面所組成,物體表面的細節將會獲得極大提高。這種每一個片元使用各自的法線,替代一個面上全部片元使用同一個法線的技術叫作法線貼圖(normal mapping)或凹凸貼圖(bump mapping)。web

以上都是從 LearnOpenGL CN 相關的文章摘抄 法線貼圖。沒辦法,WebGL相關比較深刻的知識你只能去看openGL,好在原理基本相同,WebGL1就是基於openGL es 2.0,WebGL2就是基於openGL es 3.0。算法

法線貼圖

法線貼圖就是用紋理中的顏色向量r、g、b存儲法線向量的x、y、z。不過它們有另外的稱呼:t (切線)、b (副切線)、n (法線),它們組成了一個切線空間,被稱爲TBN座標系。因爲顏色與方向的表示範圍有區別,顏色範圍是[0,1],而做爲表示位置方向的TBN座標系則是[-1,1],那麼從法線貼圖取出來的值要使用的話,得進行轉換。app

// 將法線向量轉換爲範圍[-1,1]
vec3 normal = normalize(normal * 2.0 - 1.0);
複製代碼

法線貼圖偏藍是由於全部法線的指向都偏向z軸(0, 0, 1), 對應於rgb 中的 blue份量,也就是藍色。法線向量從z軸方向向其餘方向輕微偏移,因而顏色也就發生輕微變化,這樣看起來便有了一種深度。例如,你能夠看到頂部顏色傾向於偏綠,這是由於頂部的法線偏向於指向正y軸方向(0, 1, 0)對應於rgb總的 green份量,也就是綠色。 函數

法線貼圖顏色

法線貼圖的優勢是能夠用一個低精度模型表現出很是高的細節,看起來像高精度模型那樣。 學習

高精度模型

只須要500個三角形的簡單網格加上法線貼圖就能達到媲美4M個三角形的精細網格模型的效果,能夠說法線貼圖優點巨大,處理4M個三角形的複雜度簡直不可想象。webgl

但法線貼圖也不是萬能的,它也有缺點。由於它只是改變了物體表面的光照計算方式,因此不適合用在凹凸起伏較大的物體上,這些物體會有遮擋的效果,法線貼圖是沒法實現的。ui

而文章 法線貼圖 裏面有很詳細的原理講解和切線推導過程,最後求出以下的公式,我這裏也再也不敘述了。

公式

着色器

我這裏使用了另一種更加方便的算法,能達到一樣的效果,原理就是使用導數(dFdx/dFdy)求出每一個像素在插值化傳值過來的點的變化率當成一個法線,請看函數 dHdxy_fwd。而後再經過與當前平面的法向量進行叉積 (cross),便可求得同時垂直於這兩個方向的法向量,請看函數 perturbNormalArb,而最終的這個法向量就是咱們所要求的值,很是地高明。下面是glsl 內置的幾個關鍵函數。

dFdx(p) //在x方向的偏導數
dFdy(p) //在y方向的偏導數
cross(p0,p1) //向量p0,p1的叉乘
複製代碼

咱們用到的求導函數 dFdx / dFdy,在WebGL1是須要開啓擴展的,頂點着色器無需變更,主要變更的是片元着色器。具體的計算過程,請看以下片元着色器代碼:

#extension GL_OES_standard_derivatives : enable// 注意要開啓該擴展
//...
uniform sampler2D u_diffMap;
uniform sampler2D u_specMap;
uniform sampler2D u_normMap;
//...
vec2 dHdxy_fwd() {
    vec2 dSTdx = dFdx( v_texcoord );
    vec2 dSTdy = dFdy( v_texcoord );
    float Hll = bumpScale * texture2D( u_normMap, v_texcoord ).x;
    float dBx = bumpScale * texture2D( u_normMap, v_texcoord + dSTdx ).x - Hll;
    float dBy = bumpScale * texture2D( u_normMap, v_texcoord + dSTdy ).x - Hll;
    return vec2( dBx, dBy );
}
vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {
    vec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );
    vec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );
    vec3 vN = surf_norm;
    vec3 R1 = cross( vSigmaY, vN );
    vec3 R2 = cross( vN, vSigmaX );
    float fDet = dot( vSigmaX, R1 );
    fDet *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );
    vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );
    return normalize( abs( fDet ) * surf_norm - vGrad );
}

//...
// 從法線貼圖計算出逐像素法線向量
vec3 normal = perturbNormalArb( -v_position, normal, dHdxy_fwd());
//...
// 總的光照
gl_FragColor = vec4(ambient + diffuse + specular, diffuseColor.a);
複製代碼

最後效果請看demo:紋理貼圖

後記

相關資料 LearnOpenGL CN

相關文章
相關標籤/搜索