[OpenGL] Normal Mapping 法線映射- 附我的實現 - 台部落
文章推薦指數: 80 %
右側是使用Normal Map的渲染結果,經過了切線空間的變換從而能得到真實的光照效果。
二、背景Background.
請輸入正確的登錄賬號或密碼
註冊
忘記密碼
首頁
opengl
正文
[OpenGL]NormalMapping法線映射-附我的實現
原創
RyuZhihao123
2018-09-0407:51
【更新】我的新博客:www.ryuzhihao.cc,當然這個csdn博客也會更新
本文在新博客中的鏈接:點擊打開鏈接
最近準備填一下坑兒,整理一下之前寫過的一些shader程序。
程序都是在Qt下進行OpenGLES的相關開發,不過對於Shader代碼,不管是何處使用都沒有什麼太大差異。
填坑篇(一):NormalMapping
一、我的實現結果:
(下圖的代碼會在文章末尾給出)
還是慣例貼一下我的程序結果:
左側是未使用NormalMap的情況。
右側是使用NormalMap的渲染結果,經過了切線空間的變換從而能得到真實的光照效果。
二、背景Background
法線貼圖(NormalMapping)多用在CG動畫的渲染以及遊戲畫面的製作上。
我們從具有高細節的模型通過映射烘焙出法線貼圖,貼在低端模型的法線貼圖通道上,使低細節模型也能擁有高細節模型的層次效果,同時可以大大降低渲染時需要的面數和計算內容,從而達到優化動畫渲染和遊戲渲染的效果。
下面的圖片是一個非常出名的例子,能夠很好的說明NormalMapping的強大作用:
第1副圖片是一個高細節的模型,使用了4百萬個三角面片顯示出了非常精細的細節。
第2副圖片中,我們將原先的4百萬個三角面片簡化到了500個三角面片,此時如果不使用NormalMapping技術,模型會變得非常粗糙(低細節)。
在第3副圖像中,我們在簡化後的低細節模型上,應用NormalMapping技術,便可以實現近似於第1副圖片中的精細程度。
但是不要忘記,我們僅僅使用了500個三角面片就實現了4百萬個面片的效果。
這大大降低了渲染的時間。
三、法線圖NormalMap
在NormalMapping技術中,我們試圖在一個平滑的表面(smoothflat)上,展現出粗糙表面的凹凸效果,在這裏我們使用的是法線圖。
我們將物體表面的法線事先存儲在一張法線圖中,將法線圖的(r,g,b)三個分量分別作爲法線向量的(x,y,z),如下圖所示。
在法線圖中,法線一般是近似垂直於表面XOZ的,因此y軸分量要大一些,這也是爲什麼大多數法線圖的顏色偏藍綠色的原因。
此時,我們通過如下代碼,就可以獲取到法線圖中存儲的法線值:
vec3MapNormal=texture2D(normalMap,v_texcoord).xyz;//normalmap中的值
vec3normal=2.0*MapNormal-vec3(1.0,1.0,1.0);//將法線從[0,1]變換到[-1,1];
那麼此時,我們將法線圖讀入頂點着色器,並按照法線圖的rgb直接作爲法線計算光照,就可以得到法線貼圖的初步結果。
如下圖所示:
雖然已經能夠看到凹凸不平的效果,但是不難發現這種做法存在的問題:每個面的光照都像是被光源直射一樣。
這違背了現實中的光照特性。
其原因是:我們直將法線圖的rgb直接取出作爲物體表面的法線,這樣正方體的每一個面的法線並非是與物體表面垂直的,而是所有六個面的法線的大致朝向都相同(即整體平行於y軸,個體有微小偏轉),因此我們這樣做的結果就導致了:所有的面的光照效果都一樣,不管這個面是否被陽光直射。
四、切線空間TangentSpace
爲了解決上面出現的問題,我們需要將法線圖中的切線變換到物體座標系(objectlocalspace)。
這個時候就需要藉助切線空間了。
爲了描述物體表面某位置的切線空間,我們可以用互相垂直的T、B、N三個向量表示:
N(Normal):法向量
T(Tangent):切向量
B(BitTangent):副切向量
對於我們常採用的網格模型,法向量N通常是已知的,但是切向量T和切向量B卻不是那麼輕易能夠獲得的,這裏我們要藉助一定的數學方法進行計算:
下圖是法線圖紋理在變換前的TBN向量的示意圖,可以發現T、B分別平行於紋理圖的U、V軸。
我們可以利用這一特性計算出兩個切向量。
貼圖後的紋理會被應用到三角面片上,我們對於模型中的一個三角面片進行說明(如下圖)。
現在已知三角面片的三個頂點座標:P1(x1,y1,z1),P2(x2,y2,z2),P3(x3,y3,z3),以及紋理座標(u1,v1),(u2,v2),(u3,v3)。
那麼可以求出邊E1和邊E2的向量值,即:
E1=(E1x,E1y,E1z)=(x1-x2,y1-y2,z1-z2)
E2 =(E2x,E2y,E2z)=(x3-x2,y3-y2,z3-z2)
此時,我們假設△U1=(U1-U2),△V1=(v1-v2),△U3=(U2-U2),△V3=(v2-v2), 那麼,我們可以利用簡單的向量知識,建立如下等式:(粗體表示向量)
E1=△U1*T+△V1*B ①
E2 = △U2*T+△V2*B ②
在上式中,只有T、B未知,方程可解。
所以我們只需要在外部利用上面的公式計算出T、B,即可將正副切線作爲屬性值傳入着色器。
下面給出在外部計算T、B的代碼(使用VBO):
for(unsignedinti=0;i
延伸文章資訊
- 1Implementing Normal Mapping using OpenGL/GLSL - Stack ...
That normal map is in tangent-space, but you are treating it as object-space. You need a bitangen...
- 2第十三课:法线贴图
镜面纹理(Specular texture); 用立即模式(immediate mode)进行调试; 利用颜色进行调试; 利用变量名进行调试 ... 今天的内容是法线贴图(normal mapp...
- 3OpenGL 2.1 - GLSL normal mapping - 3D C/C++ tutorials
The normal map used in this tutorial is a Direct3D normal map and before it can be mapped on tria...
- 4Tutorial 13 : Normal Mapping
The basic idea of normal mapping is to give normals similar variations. Normal textures. A “norma...
- 5Normal Mapping - OpenGL ES SDK for Android - GitHub Pages
A normal map is a regular texture but instead of defining what colour a pixel will be on screen, ...