每天一個shader技巧 day1 - 從圖片生成法線貼圖 shadertoy實現(xiàn)
前置知識
法線貼圖,圖形學(xué),向量運算,導(dǎo)數(shù)等
算法
先不去想如何從一張紋理去生成法線,因為圖片是一個二維的存儲結(jié)構(gòu),處理起來的話會比較復(fù)雜,寫成方程的形式則是 f(u,v) = pixel color?
我們進(jìn)行一個降維處理,假設(shè)我們現(xiàn)在有段曲線,f(x) = y,這樣變量就變?yōu)橐痪S的了。那么對于一個曲線,我們?nèi)绾潍@取曲線一個點上的法線呢,自然可以想到對該函數(shù)求一階導(dǎo),因為某個點一階導(dǎo)的結(jié)果為過這個點切線的斜率,那么該點的法線的斜率與切線的斜率乘積?,F(xiàn)在我們知道點,知道斜率,自然可以求得法線的方程。

擴展到二維的話,也是同理,先對一個點的一個方向求出法線,再對另一個方向求出法線即可。但是!我們既然在這個過程中會求得兩個切線,那直接做一個叉乘即可獲得垂直于這兩個向量的第三個向量,即是法線,所以思路就清晰了。
求切線->算法線
但是按這個思路對一張紋理進(jìn)行運算會有一個問題,那就是紋理儲存的是RGB值,這個值和法線是沒有關(guān)系的,所以我們需要一張描述該紋理的高度圖,然后用該高度圖去求法線向量。
那沒有高度圖怎么辦呢,那就用一個神奇的公式,獎圖片轉(zhuǎn)為灰度圖(不要問我這個公式怎么來的,只能說基于經(jīng)驗)
代碼如下
看看效果


求切線
有了高度圖之后就要求切線了。因為一張圖是由許多個像素點組成的,所以是離散的,那么就需要用到導(dǎo)數(shù)的證明過程那個極限的寫法了。
但是在實際計算中,我們要的是切線向量,而這個求的是斜率,不是我們想要的。那么切線向量怎么構(gòu)造呢,回到一維的情況,假設(shè)我們有切線方程?,那么切線向量則是
。
那么回到二維的情況,我們也可以分別對x, y去進(jìn)行一個切線向量的構(gòu)造
獲得這兩個向量后進(jìn)行一個叉乘便可獲得法線向量了。代碼如下
代碼中有幾個要注意的點,首先是生成法線的函數(shù),多了兩個參數(shù)分別來scale采樣的步長和高度。步長好理解,控制生成法線的精度。那為什么需要高度的scale系數(shù)呢,因為對圖片進(jìn)行采樣的時候,我們所選取的步長是非常小的(1/1024),但是高度差卻很大(最大可能到1.0),這是遠(yuǎn)大于
的,這就會導(dǎo)致切線向量過于接近Z軸,計算出來的法線x,y的值就很大了,所以需要引入一個scale系數(shù)來進(jìn)行高度差的縮放。
最后將處于(-1,-1,-1)到(1,1,1)的法線轉(zhuǎn)化到(0,0,0)到 (1,1,1)的空間下
