目錄
一 為什么要使用法線貼圖
二 二種不同法線方式的使用
2.1 插值法線
2.1 法線貼圖
本章節源碼?點擊此處
一 為什么要使用法線貼圖
法線貼圖我們可以使用更少的頂點表現出同樣豐富的細節。高精度網格和使用法線貼圖的低精度網格幾乎區分不出來。所以法線貼圖不僅看起來漂亮,它也是一個將高精度多邊形轉換為低精度多邊形而不失細節的重要工具。
我們知道不是所有的物體表面都是光滑平整的平面,比如我么要繪制一個人臉,但人臉會由,頭發,五官,甚至是微表情等組成這些都是具有凹凸感的,那我們該如何體現出這種凹凸感呢?
- 第一種方式: 傳入大量的頂點坐標,讓大量的頂點來幫我們實現人臉中的各個細節。但這無疑是會增大額外的開銷。
- 第二種方式:?我們使用法線貼圖,來為每個像素上都設置不同的法線值,這樣實現的效果和大量的頂點坐標幾乎一樣。
如下圖所示: 左邊第一個是傳入了上百萬的頂點坐標實現的效果,而最中間的是傳入了500個頂點實現的效果,最后一個是在中間的基礎上運用的法線貼圖實現的效果,他們的效果幾乎是一樣的。
二 二種不同法線方式的使用
- 通俗的理解法線的作用是在計算光照時根據角度的不同,反映在顏色上的變化,比如如果法線與光線垂直那么就會光照效果最大,顏色最亮。
- 一般有兩種不同方式來傳遞給GPU法線,一種是采樣插值法線,也就是為每個頂點傳遞它的法線,另一種是采用法線貼圖。
2.1 插值法線
- 觀察下面這個圖我們是使用插值法線得到的效果,我們傳入的是6個頂點也就是2個三角形,而這6個頂點位于同一個平面,我們使用插值法線一般也是會插入6個法線向量,但同一個平面我們此時傳入的法線其實是相同的,那會造成什么問題呢?
- 例如,磚塊的表面。磚塊的表面非常粗糙,顯然不是完全平坦的:它包含著接縫處水泥凹痕,以及非常多的細小的空洞。如果我們在一個有光的場景中看這樣一個磚塊的表面,問題就出來了。下圖中我們可以看到磚塊紋理應用到了平坦的表面,我們并看不到磚縫以及磚塊表面的一些細節或者說不是很明顯。
- 對于插值法線,同一個片段上我們可以理解為法線是相同的,以光的視角來說,這個表面就是完全平坦的,即使本身提供的紋理圖片上可能會看到一些磚縫但這并不能完全體現出這種凹凸不平的平面的細節。
- 我們可以使用一種技術,讓每個頂點擁有自己單獨的法線,當光源照射到頂點時,根據法線的不同就能體現出更過的細節。替代一個面上所有fragment使用同一個法線的技術叫做法線貼圖(normal mapping)或凹凸貼圖(bump mapping)
2.1 法線貼圖
原理
- 在使用法線貼圖前我們應該了解一下法線貼圖的原理。其實就是用一個2D的紋理來保存每個片段的法線.
- 紋理中的rgb就剛好對應了法線的xyz坐標
- 這也就是我們為什么看到下面這個紋理大部分是藍色,而在磚縫中表現的偏綠色,是因為這個紋理貼圖大部分是指向z軸的也就是rgb中的b(藍色),而磚縫中就偏向于綠色也就是指向y軸。
- 這里有個值得注意的點,紋理中的像素顏色是0-1之間的,我們需要把它轉換到標準坐標中的-1-1之間。
vec3 normTemp = vec3(texture(texture_normal1,TexCoords));norm = normalize(normTemp * 2.0 -1.0);
- 這樣我們在片段著色器中,就能夠獲取到每個片段上對于的法線值了(其實就是紋理顏色值)。
效果:
- 這樣我們就能看到這個墻壁的諸多細節了。
三 代碼開關
- 在源碼中我們使用一個開關bIsTexture,當我們點擊dockwidgt中的插值法線時,該值為false,則采用默認的插值法線來計算,如果點擊法線貼圖改值為true,采用法線貼圖來渲染
vec3 norm;if(bIsTexture == true){vec3 normTemp = vec3(texture(texture_normal1,TexCoords));norm = normalize(normTemp * 2.0 -1.0);}else{norm = normalize(Normal);}