CG中的碎片著色計(jì)算(fragment shading calculation以及diffuse material和shiny material)

Note

這是對(duì)MIT Foundation of 3D Computer Graphics第14章的翻譯,本章以簡(jiǎn)潔明了的方式給出了計(jì)算機(jī)圖形學(xué)中光和材料(著色計(jì)算)的基本講解。包含光著色計(jì)算基礎(chǔ)以及diffuse material和shiny material。本書內(nèi)容仍在不斷的學(xué)習(xí)中,因此本文內(nèi)容會(huì)不斷的改進(jìn)。若有任何建議,請(qǐng)不吝賜教ninetymiles@icloud.com。

注:文章中相關(guān)內(nèi)容歸原作者所有,翻譯內(nèi)容僅供學(xué)習(xí)參考。
另:Github項(xiàng)目CGLearning中擁有相關(guān)翻譯的完整資料、內(nèi)容整理、課程項(xiàng)目實(shí)現(xiàn)。


已經(jīng)完成的章節(jié)


材料(Materials)

碎片著色器(fragment shader)的職責(zé)是決定一個(gè)三角形上的點(diǎn),這個(gè)點(diǎn)對(duì)應(yīng)于圖像中的一個(gè)像素。碎片著色器(fragment shader)擁有對(duì)于被插值的變異變量(varying variables)的訪問權(quán)限,同時(shí)還有以u(píng)niform(統(tǒng)一參數(shù))變量形式來自于用戶程序的數(shù)據(jù)。Uniforms變量經(jīng)常被用于描述諸如某種光源位置這類東西,這些值不會(huì)逐像素變化。變異變量(varying variables)經(jīng)常被用于描述點(diǎn)的坐標(biāo)矢量(比方說,關(guān)聯(lián)于眼睛幀(eye frame)),還有用于點(diǎn)的法線,以及描述這個(gè)點(diǎn)上材料屬性的參數(shù)(比如底層的材料色)。碎片著色器(fragment shader)隨后接收這些數(shù)據(jù)并且同時(shí)模擬光如何從這種材料反彈出去,同時(shí)產(chǎn)生一個(gè)圖像中的色彩。本章中,我們將覆蓋最常見的這種用于材料模擬的著色就算(shading calculations)。隨后的章節(jié)中,我們會(huì)探索在碎片著色器(fragment shader)中完成的其它主要計(jì)算方式:紋理映射。

碎片著色(fragment shading)擁有非常豐富的主題,且處于讓計(jì)算機(jī)生成的圖形擁有如它們實(shí)際般那樣細(xì)致和真實(shí)效果的核心位置。在OpenGL渲染中也是一個(gè)核心主題,并且你應(yīng)該計(jì)劃學(xué)習(xí)更多關(guān)于我們?cè)谶@本書中所覆蓋的材料主題。一本好的參考書是CG Tutorial [19];這本書使用CG著色語(yǔ)言而不是我們選擇的GLSL,但是在這兩種語(yǔ)言間的平移幾乎是直接的。另一本好的參考書是Real-Time Rendering [2]。更詳細(xì)的材料描述可以在Digital Modeling of Material Appearance [17]書中被找到。

碎片著色(fragment shading)擁有非常豐富的主題,且處于讓計(jì)算機(jī)生成的圖形擁有如它們實(shí)際般那樣細(xì)致和真實(shí)的效果的核心位置。在OpenGL渲染中也是一個(gè)核心主題,并且你應(yīng)該計(jì)劃要學(xué)習(xí)更多關(guān)于我們?cè)谶@本書中所覆蓋的材料主題。一本好的參考書是CG Tutorial [19];這本書使用CG著色語(yǔ)言而不是我們選擇的GLSL,但是在這兩種語(yǔ)言間的平移幾乎是直接的。另一本好的參考書是Real-Time Rendering [2]。更詳細(xì)的材料描述可以在Digital Modeling of Material Appearance [17]書中被找到。

14.1 基本假設(shè)(Basic Assumptions)

當(dāng)光線照射在一個(gè)物理材料上,會(huì)在多個(gè)外出方向被散射。不同種類的材料以不同的模式散射光線并且這會(huì)導(dǎo)致眼睛或者相機(jī)所看到的不同外觀。有些材料可能看起來明亮,而另一些則顯得模糊。通過模擬這種散射處理,我們能夠針對(duì)一個(gè)被渲染的3D物體給出真實(shí)的物理外觀。

例如,圖示\text{Figure14.1}可視化了光線如何從一塊PVC材料上的一個(gè)點(diǎn)反射開去。光線從被矢量\vec{l}所指向的方向射入。團(tuán)狀圖(blob)展示了當(dāng)從塑料上方多個(gè)方向觀察時(shí),這個(gè)點(diǎn)會(huì)顯得多么明亮。更亮的方向都被上色為紅色,同時(shí)團(tuán)狀圖(blob)的形狀在這樣明亮的方向會(huì)延伸地更遠(yuǎn)。更模糊暗淡的方向則被上色為藍(lán)色。注意這個(gè)圖示只是描述了來自特定方向\vec{l}的光的結(jié)果。對(duì)于其它入射方向,我們會(huì)需要一個(gè)不同的圖狀圖(blob)來可視化有差異的最終散射結(jié)果。

Figure14.1.png

Figure 14.1: 團(tuán)狀圖(blob)展示了光線從一塊PVC塑料上如何反彈出去。參考書目[51],??Eurographics and Blackwell Publishing Ltd。

從這個(gè)圖示中,我們看到當(dāng)從圍繞光線的“反射”方向:B(\vec{l})的多個(gè)聚束方向觀看時(shí),塑料顯得最明亮。實(shí)際上,大多數(shù)光在入射方向的反彈方向反射出去(想像光從鏡面反彈出去或者一個(gè)臺(tái)球如何從臺(tái)球桌壁反彈開)。假定一個(gè)任意矢量\vec{w}(不必然為單位態(tài)(unit norm))和一個(gè)單位長(zhǎng)法線矢量\vec{n},我們可以計(jì)算\vec{w}的反射矢量為下面的公式:

\large{ B(\vec{w}) = 2(\vec{w}.\vec{n})\vec{n} - \vec{w} \tag{14.1}}

參看圖示\text{Figure 14.2},這個(gè)圖示提供了方程式的可視推導(dǎo)。

Figure14.2.png

Figure 14.2:反射方向矢量的計(jì)算推導(dǎo)。

在OpenGL中,我們可以在頂點(diǎn)著色器(vertex shader)中在頂點(diǎn)解析時(shí)模擬這種散射處理,同時(shí)僅僅在每個(gè)三角形上借助變異變量(varying variables)插值最終的色彩值。針對(duì)一個(gè)有更高質(zhì)量要求的外觀,我們可以以付出更高的代價(jià),在碎片著色器(fragment shader)中,在每個(gè)像素上模擬這種處理。本章中,我們會(huì)采用這種基于像素的方法。

要正確理解和模擬光散射,我們要真正懂得用來測(cè)量光的正確單位、光散射的真實(shí)物理學(xué)、還有用來建模散射的正確方程式。在21章中我們將用更多細(xì)節(jié)講解這個(gè)主題。更進(jìn)一步,真實(shí)的環(huán)境中光能夠從所有方向朝一個(gè)表面照射來,并且在其被完全吸收前,會(huì)圍繞我們的場(chǎng)景反彈多次。這些問題也會(huì)在21章被講解。本章中,我們將采用計(jì)算一個(gè)合理外觀的更實(shí)際的方式,借助物理激發(fā)但不是物理精確的方式。

在當(dāng)前時(shí)刻,我們將限制我們?cè)谌鐖D示\text{Figure 14.3}中的簡(jiǎn)單環(huán)境中,這里我們假設(shè)所有光來自一個(gè)單點(diǎn)光源,其被光矢量\vec{l}所指向。這個(gè)光束照射到某個(gè)三角形的帶有法線\vec{n}的點(diǎn)\tilde{p}之上。在表面法線和指向光的矢量之間的角度被表示為\theta。本環(huán)境中,我們想要計(jì)算朝向眼睛順著查看矢量(view vector,\vec{v})反射的光的數(shù)量。要處理對(duì)應(yīng)的色彩,我們只會(huì)計(jì)算3個(gè)用RGB表示的反射(RGB坐標(biāo)部件)數(shù)量值。注意RGB的這種用法不是物理精確的(參考后面的19.5節(jié)內(nèi)容)。這些數(shù)量隨后被直接用于這個(gè)像素的RGB數(shù)值。我們會(huì)假設(shè)點(diǎn)和矢量的所有坐標(biāo)被關(guān)聯(lián)于眼睛幀(eye frame)所記錄。

就如以前所提及的(參考圖示\text{Figure 6.1}),法線不必是扁平幾何體的“真正法線”。相反,法線可以被用戶在每個(gè)頂點(diǎn)所指定,同時(shí)可以作為變異變量(varying variable)被插值。以這種方式,在材料模擬中,我們可以使用平滑的變異(varying)法線獲得平滑的外觀。

Figure14.3.png

Figure 14.3:這里我們展示被用在一個(gè)表面點(diǎn)上的多個(gè)矢量去確定這個(gè)點(diǎn)的色彩。

14.2 漫射材料(Diffuse)

漫射材料,像粗糙的木頭,當(dāng)從各個(gè)方向\vec{v}觀看時(shí)會(huì)顯得一樣亮。如此,當(dāng)在一個(gè)漫射材料上計(jì)算一個(gè)點(diǎn)的色彩,我們完全不需要使用\vec{v}矢量。當(dāng)光線從“上面”照射漫射材料時(shí),它們會(huì)顯得更加明亮。而當(dāng)光線以一種掠過角度照射時(shí),這些材料就顯得模糊。這是因?yàn)椋▍⒖?1章)照射在一小塊固定尺寸的漫射材料上的入射光子的數(shù)量成比例于cos(\theta) = \vec{n}.\vec{l}。基于這些假設(shè),我們可以用下面的碎片著色器(fragment shader)計(jì)算漫射材料的色彩 - 這個(gè)著色器我們也在之前的6.4節(jié)中使用過。

#version 330

uniform vec3 uLight;

in vec3 vColor; 
in vec3 vNormal; 
in vec4 vPosition;

out fragColor;

void main(){ 
    vec3 toLight = normalize(uLight - vec3(vPosition)); 
    vec3 normal = normalize(vNormal); 
    float diffuse = max(0.0, dot(normal, toLight)); 
    vec3 intensity = vColor * diffuse; 
    fragColor = vec4(intensity.x, intensity.y, intensity.z, 1.0); 
}

normalize函數(shù)的調(diào)用被用于確保被插值的矢量擁有單位態(tài)(unit form)。max函數(shù)的調(diào)用被用于保證我們不會(huì)嘗試產(chǎn)負(fù)光-當(dāng)位于那些背離光源點(diǎn)上時(shí)。漫射計(jì)算的值被用于調(diào)和內(nèi)生的表面色-這種色彩被通過vColor頂點(diǎn)屬性傳入。

我們借助這種shader繪制我們的簡(jiǎn)單立方體圖示,比如\text{Figure 5.1},還有在\text{Figure 14.4}中的卵形形狀。

這種著色器可以被輕松地以多種方式擴(kuò)展。

  • 本章中,光本身沒有被給定其自身的一種色彩。這也可以被輕松地使用另一個(gè)uniform變量附加的方式建模。
  • 多光源也可以被輕松添加進(jìn)來。
  • 真實(shí)世界中,光圍繞一個(gè)場(chǎng)景反彈多次,并且因此總是有光從每個(gè)方向照射每個(gè)表面。這種情況可以被非常粗魯?shù)乇唤T谖覀兊挠?jì)算中-僅需將某種常量環(huán)繞色(ambient color)加到當(dāng)前的強(qiáng)度(intensity)上即可。
  • 有時(shí),建模來自于一個(gè)(被一個(gè)矢量所描述,而不是來自于一個(gè)點(diǎn)的)方向的光會(huì)更加方便。在這種情形中,當(dāng)建模這一種方向光源時(shí),矢量toLight會(huì)作為一個(gè)uniform變量被傳入。

Figure14.4.png

Figure 14.4: 一個(gè)被漫射方式著色的卵形形狀。

14.3 光亮材料(Shiny)

很多材料,就像塑料,不是漫射特征;針對(duì)固定數(shù)量的入射光分布,當(dāng)從某些方向觀察時(shí),它們看起來更明亮;而從其它方向觀察時(shí)它們就顯得模糊。圓形的由這種材料制成的對(duì)象只要表面法線指向“正好正確”在其表面就會(huì)出現(xiàn)明亮的高亮。這種針對(duì)一種特定材料的反射行為的確切形式可以被測(cè)量。反射行為也常??梢越柚拔㈢R面”理論[14]被預(yù)測(cè)。這種理論中,你可以想象材料由多種微小的真正鏡面構(gòu)成,這些鏡面以某種可統(tǒng)計(jì)的方式與光線交互。

一種簡(jiǎn)單但某種意義上又合理的常常用于實(shí)踐的計(jì)算,其僅僅計(jì)算光線的反射矢量B(\vec{l}),然后再計(jì)算它與查看矢量\vec{v}的角度。當(dāng)反射和查看矢量被良好對(duì)齊時(shí),我們就繪制一個(gè)非常亮的像素。當(dāng)它們不被良好對(duì)齊時(shí),我們就繪制一個(gè)模糊的像素。

還有一種甚至更簡(jiǎn)單的方式可以獲得相同的效果,就是首先計(jì)算半路矢量(halfway vector)\vec{h} = normalize(\vec{v}+\vec{l}),隨后再測(cè)量半路矢量與法線\vec{n}之間的角度\phi。參考圖示\text{Figure 14.5}。,只有當(dāng)\vec{v}\vec{l}良好對(duì)齊時(shí),矢量\vec{h}與矢量\vec{n}也才會(huì)對(duì)齊。我們借用點(diǎn)積計(jì)算\phi的cosine值,這會(huì)給出我們一個(gè)位于[0..1]之間的值,這個(gè)值會(huì)隨著矢量\vec{h}與矢量\vec{n}的分離而衰減。要建模光亮材料(shiny material),我們會(huì)讓亮度隨著角度快速衰減,因而我們隨之提升\cos(\phi)為一個(gè)正數(shù)冪形式(下面代碼中使用數(shù)字64)。

Figure14.5.png

Figure 14.5: 此處我們展示了用于給一個(gè)閃亮材料著色的矢量。

把這些思路全部匯總到一起,我們得到如下的著色器(shader):

#version 330

uniform vec3 uLight; 

in vec3 vColor; 
in vec3 vNormal; 
in vec4 vPosition;

out fragColor;

void main(){ 
    vec3 toLight = uLight - vec3(vPosition);
    vec3 toV= -normalize(vec3(vPosition)); 
    toLight = normalize(toLight); 
    vec3 h = normalize(toV + toLight); 
    vec3 normal = normalize(vNormal);

    float specular = pow(max(0.0, dot(h, normal)), 64.0); 
    float diffuse = max(0.0, dot(normal, toLight)); 
    vec3 intensity = vec3(0.1,0.1,0.1) + vColor * diffuse + vec3(0.6,0.6,0.6) * specular;

    fragColor = vec4(intensity.x, intensity.y, intensity.z, 1.0);
}

矢量\vec{v}的坐標(biāo),(此處存儲(chǔ)在一個(gè)名稱為toV的變量中)實(shí)際上很容易計(jì)算,因?yàn)?,在眼睛坐?biāo)中,眼睛的位置和原點(diǎn)重合。同時(shí)也要注意,在本代碼中,反射部件為白色,此處高亮處匹配入射光的色彩,而無關(guān)材料色。

我們已經(jīng)使用了這種著色器(shader)去繪制我們的卵形形狀,就如在圖示\text{Figure 6.1}中,同時(shí)還有本章在圖示\text{Figure 14.6}中。

Figure14.6.png

Figure 14.6: 一個(gè)光亮著色的卵形

14.4 各向異性材料(Anisotropy)

前面兩種材料模型,還有很多其它的,擁有各項(xiàng)同性(isotropy)的屬性。這意味著沒有針對(duì)表面的具有優(yōu)先級(jí)的“粒度”。有關(guān)聯(lián)的因素是光矢量、查看矢量、和法線矢量的幾何特征。作為對(duì)比,有些材料,比如拉絨的金屬,以各向異性的方式展現(xiàn)行為。如果你拿著一塊扁平的這種材料然后圍繞其法線旋轉(zhuǎn),材料的外觀會(huì)改變。建模這種材料時(shí),我們會(huì)假設(shè)一個(gè)優(yōu)先的正切矢量\vec{t}以某種方式被包括在模型中,同時(shí)被傳給著色器(shader)。參考圖示\text{Figure 14.7}。下面我們展示一個(gè)著色器(shader,在[42]中有描述),基于Kajiya和Kay[33]推導(dǎo)的模擬光從各向異性的毛絨表面反射的方程式。這些方程式基于表面由微小的圓柱體構(gòu)成來推導(dǎo);這個(gè)推導(dǎo)的細(xì)節(jié)超越本書的范圍。

#version 330 

uniform vec3 uLight; 

in vec3 vColor; 
in vec3 vNormal; 
in vec4 vPosition; 
in vec4 vTangent; 
out fragColor; 

void main(void){
    vec3 toLight = normalize(uLight - vec3(vPosition)); 
    vec3 toV = -normalize(vec3(vPosition)); 
    toLight = normalize(toLight); 
    vec3 h = normalize(toV + toLight); 
    vec3 normal = normalize(vNormal);

    vec3 vTangent3 = vec3(pvTangent);

    vTangent3=normalize(cross(vNormal,vTangent3));

    float nl = dot(normal, toLight); 
    float dif = max(0,.75 * nl+.25);
    float v = dot(vTangent3, h); 
    v = pow(1.0 - v * v, 16.0);

    float r = pColor.r * dif + 0.3 * v; 
    float g = pColor.g * dif + 0.3 * v; 
    float b = pColor.b * dif + 0.3 * v;

    fragColor = vec4(r, g, b, 1);
}

我們已經(jīng)使用這個(gè)著色器渲染了圖示\text{Figure 14.8}中的卵形形狀。

Figure14.7.png

Figure 14.7: 建模各向異性的材料,我們也需要包括一個(gè)優(yōu)先的正切矢量。

Figure14.8.png

Figure 14.8: 以各向異性方式著色(anisotropically shaded)的卵形

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容