本來星期一就想寫 不過太忙了 到現在才寫
為什麼要Lighting 我們從標準幾何定義來說 物體是由很多平面構成的 而每個平面由一個法向量來表示 因為一個平面只有一個法向量 所以就會向Flat Shading 一樣 看的到菱角
而通常我們都想要讓物體看起來更平滑 所以我們會用一些偷雞摸狗的方式來達到平滑的 Gouraud跟phong就是不用平面的法向量 用頂點的法向量
這篇是想揭著上一篇 電腦繪圖(一) 提到Phong Shading跟Gouraud Shading 除了這兩個還有一個更簡單的Flat Shading 當然還有很多啦 先講這三個最容易的 而DirectX的Fixed Function Pipeline 就是用 Gouraud 跟Flat Shading指的是算出多邊形的顏色 而Phong Shading跟Gouraud Shading 就是用Phong reflection model 這個模型來算的 除了這個模型還是有其他不少模型像比較早的Lambert’s Law for reflection 還有後來的BDRF等 之後再提
下面舉個例子來說明這三種shading 假如我有一個三角形 三個頂點的顏色各不相同 那得到的平面顏色會是如何?? 1.Flat Shading 這是最簡單的 那個三角形平面的顏色 就是三個頂點顏色平均 然後裡面沒有作線性內插 都填一樣的顏色 那個平面的顏色就都一樣 看起來就像這張圖 可以很清楚的看到菱角 以前的3d遊戲就是用這種shading 每個平面的顏色都一樣 所以還蠻醜的 不過很快 算一個平均 填色
2.Gouraud Shading 講這個shading之前 要先講Phong reflection model 這是phong提出來的一個反射的模型 Phong reflection model 是1975年范先生提出的 後來被Jim Blinn修正 其實看JimBlinn的論文就行了 說結論吧 這個模型主要是說 L打光到點Q會有一個反射R 然後我們觀察者是V n 是法向量 就這張圖所示 光的強度 I=Ambient+ Diffuse + Specular ==> 
Ambient = 光打不到的地方 通常是指環境光 是一個定值 所以通常沒有變化 Diffuse = 光的一個完美的反射大小跟光的入射角有關係 物體上diffuse會均勻分布 Specular = 跟物體表面的粗糙有關 理論上是指反射角那個地方 亮點比較高
因為這個式子中的R向量太難算了 所以後來有一個Jim Blinn改良了這個式子 後來我們所見到的都是用Jim Blinn的方法 不過還是叫Phong reflection model Jim Blinn取一個H 而H = (L + V) /2 因為R跟V的夾角 非常接近H跟N的夾角 所以他就用 NH 代替 RV 式子就變成下面這樣
OK 上面就是 Phong reflection model 從這個model我們可以得到 我們如果要去對一個物體表面打光需要以下這些東西
- Material–>1.Ambient(ka) 2.Diffuse(kd) 3.Specular(ks) 4.Shininess(n)
- 5.表面的法向量
- 光源–> 6.強度 7.光源位置 8.光源方向 9.環境光源
- 觀察者–> 10.觀察者位置 11.觀察方向
接下來要講Gouraud shading了 Gouraud shading是利用三角形的三個點的法向量算出三個點的顏色 然後讓硬體去做雙線性內插(Bi-linear interpolation) 去填中間的顏色 硬體怎麼填色呢 這就是d3d rendering pipeline的東西了 要先看一下 所以Gouraud shading看起來會像這樣(這是沒有specular的) 像是我現在如果要作自己作Gouraud shading 用vertex shader 我只要在vertex shader把三個頂點的顏色算出來 後面交給硬體去雙線性內插出裡面顏色就好 目前為止 大家用的所有PC 都是用Gouraud shading 全世界所有的電腦 包括最具權威的SGI 到目前為止 都是用Gouraud shading做的 也就是說 沒有用GPU 買的再棒的機器都是用這個shading 缺點: 品質不太好 因為如果 這三個點算出來的顏色都一樣 那這個三角形就變成平的了 還有一個嚴重的地方 光只有打在三角形中間 而沒有打在頂點上 那..那個三角形的specular就消失了@@ 所以很有人都把specular關掉
所以Gouraud shading 的Vertex shader code大概長這樣子 因為加specular會不太好看 沒加也感覺不出來 所以我就不加specular了 VS_OUTPUT Norm_Light_1_No_Tex(float4 inPos : POSITION, float3 inNorm : NORMAL) { // initialize the output VS_OUTPUT o = (VS_OUTPUT) 0; // calculate the normal & lighting vectors float4 a = mul(inPos, worldMatrix); float3 l = normalize(mainLightPosition.xyz - a.xyz); float3 n = normalize(mul(inNorm, (float3×3) worldMatrix)); // calculate the N dot L for diffuse float d = saturate(dot(l, n)); // transform the vertex to screen o.pos = mul(inPos, worldViewProj); // perform the lighting o.dif.rgb = ambLgt*amb + d*mainLightColor*dif; // set the opacity o.dif.a = dif.a; return o; } |