海城網(wǎng)站制作建設(shè)高效統(tǒng)籌疫情防控和經(jīng)濟(jì)社會(huì)發(fā)展
回顧昨天并計(jì)劃今天
在這期節(jié)目中,主要講解了光照的概念,并進(jìn)一步討論了法線貼圖光照的實(shí)現(xiàn)。節(jié)目的內(nèi)容大致分為幾個(gè)部分:
- 光照的基礎(chǔ)概述:討論了光的工作原理以及如何在編程圖形時(shí)需要考慮光照問(wèn)題。盡管這些概念并沒(méi)有深入到具體細(xì)節(jié),但重點(diǎn)是讓觀眾理解光照的基礎(chǔ),如何將其引入到圖形渲染中。
- 法線貼圖光照的實(shí)現(xiàn):節(jié)目接下來(lái)的目標(biāo)是實(shí)現(xiàn)法線貼圖光照。首先會(huì)解釋法線貼圖的工作原理和如何進(jìn)行計(jì)算。法線貼圖的實(shí)現(xiàn)本身并不復(fù)雜,重點(diǎn)是如何將其正確地計(jì)算并應(yīng)用到渲染中。
- 代碼實(shí)現(xiàn)的計(jì)劃:雖然有很多可能讓光照效果更加精準(zhǔn)和復(fù)雜的方式,但目標(biāo)是首先實(shí)現(xiàn)基礎(chǔ)的法線貼圖光照。
黑板:法線貼圖
在這一期中,講解了法線貼圖的基本概念及其在光照中的應(yīng)用。以下是內(nèi)容的詳細(xì)總結(jié):
-
光照與表面的關(guān)系:光照的基本原理是,光源將光照射到表面上,而當(dāng)我們討論光照如何與表面相互作用時(shí),需要知道光線的入射方向以及觀察光線的方向。通過(guò)這些信息,可以確定表面上某一點(diǎn)的光照效果。
-
表面特性的影響:當(dāng)光照照射到表面時(shí),表面看起來(lái)的效果(如光亮、粗糙、濕潤(rùn)等)取決于光線反射的形態(tài)。不同的表面會(huì)在不同的角度下反射不同的亮度,這種反射亮度的分布決定了表面看起來(lái)的特性。
-
法線的重要性:在圖形學(xué)中,法線的概念至關(guān)重要,因?yàn)樗鼪Q定了如何計(jì)算表面反射的亮度。法線是表面在三維空間中的方向,決定了表面如何與光線發(fā)生交互。光線的入射角度和觀察角度相對(duì)于表面的法線角度非常重要,只有知道了表面的法線方向,才能正確計(jì)算光照反射的亮度。
-
光照與法線的關(guān)系:光照的反射強(qiáng)度取決于光線與表面法線之間的角度。如果表面發(fā)生變化,法線的方向也會(huì)發(fā)生變化,這會(huì)導(dǎo)致光照強(qiáng)度的變化。法線越接近光線的方向,表面反射的亮度就越強(qiáng),反之則越暗。因此,了解和控制表面的法線方向是實(shí)現(xiàn)真實(shí)光照效果的基礎(chǔ)。
-
法線貼圖的作用:法線貼圖的目的是通過(guò)為表面上的每個(gè)點(diǎn)指定一個(gè)法線方向來(lái)模擬細(xì)微的表面細(xì)節(jié),而不需要改變表面的幾何形狀。通過(guò)法線貼圖,可以實(shí)現(xiàn)更加豐富和細(xì)致的光照效果,使得表面看起來(lái)更加真實(shí)。
總結(jié)來(lái)說(shuō),本期的重點(diǎn)是講解如何通過(guò)法線貼圖來(lái)控制光照反射,并解釋了法線在光照計(jì)算中的重要性。法線的方向直接決定了光照的強(qiáng)度和分布,從而影響到表面的視覺(jué)效果。
黑板:法線的重要性
法線在光照計(jì)算中的重要性被詳細(xì)解釋,以下是內(nèi)容的總結(jié):
-
光照方程與法線的關(guān)系:在光照計(jì)算中,基本的單位是代表微小區(qū)域的點(diǎn),這些點(diǎn)可以視為屏幕上的像素或紋理元素。為了計(jì)算光照的反射,我們需要確定每個(gè)點(diǎn)的表面朝向,這個(gè)方向就是法線。法線是一個(gè)單位向量,即它的長(zhǎng)度總是為1,并且指向垂直于表面的方向。
-
法線的定義:法線向量是一個(gè)指向與表面相切的平面的垂直方向的向量。每個(gè)表面上的點(diǎn)都有一個(gè)法線,表示該點(diǎn)表面上的“朝向”。例如,如果一個(gè)表面處于某個(gè)方向,法線就指向該表面垂直的方向。
-
法線的幾何意義:對(duì)于一個(gè)表面上的特定點(diǎn),法線就是該點(diǎn)上表面的切平面與表面垂直的方向。如果考慮一個(gè)平面與表面在該點(diǎn)接觸,那么法線就指向垂直于這個(gè)平面的方向。法線的方向會(huì)根據(jù)表面在不同位置的形態(tài)而變化,即每個(gè)點(diǎn)的法線方向都會(huì)有所不同。
-
法線的重要性:法線被用來(lái)確定表面如何與光線相互作用,從而影響光照反射的強(qiáng)度。法線的方向直接影響計(jì)算光照反射時(shí)的角度,進(jìn)而影響最終的視覺(jué)效果。它是計(jì)算光照反射時(shí)不可或缺的一個(gè)因素。
綜上所述,法線是計(jì)算表面光照反射的重要組成部分。它通過(guò)指示表面在特定點(diǎn)的方向,幫助確定光照的反射強(qiáng)度和視覺(jué)效果。
黑板:我們需要什么來(lái)基于這個(gè)法線計(jì)算光照
為了能夠基于法線計(jì)算光照,需要進(jìn)行兩項(xiàng)工作。首先,需要有一個(gè)表面描述,這個(gè)描述告訴我們?nèi)绾翁幚砉饩€在法線周?chē)姆瓷?。其?#xff0c;需要有一個(gè)公式,能夠利用我們現(xiàn)有的輸入(表面的法線、觀察方向和光線方向),計(jì)算出最終的光照值。這些輸入和表面描述將被用來(lái)輸出一個(gè)光照值,通常是 RGB 色彩值,表示在該點(diǎn)的顏色。
除此之外,光照的描述可能也需要包括其他信息,比如光源的特性。光源可能具有不同的屬性,因此,除了法線、視角和光線方向,還可能需要考慮光源的具體描述,以準(zhǔn)確計(jì)算最終的光照效果。這就是計(jì)算法線貼圖光照的基本步驟。
黑板:法線貼圖在這個(gè)方程中的位置
為了計(jì)算光照,需要引入法線貼圖的概念。在這一過(guò)程中,法線貼圖作為一種存儲(chǔ)每個(gè)像素法線的方式,幫助解決了如何在沒(méi)有直接描述表面形狀的情況下進(jìn)行光照計(jì)算的問(wèn)題。每個(gè)法線是一個(gè)單位長(zhǎng)度的向量,指向表面外部。為了減少存儲(chǔ)空間,法線貼圖只需要存儲(chǔ)法線的兩個(gè)分量,因?yàn)楦鶕?jù)法線的單位長(zhǎng)度性質(zhì),第三個(gè)分量可以通過(guò)數(shù)學(xué)公式推算出來(lái)。
法線貼圖中的每個(gè)紋理單元(Texel)將存儲(chǔ)法線向量的x、y和z分量。這些值通過(guò)RGB顏色編碼方式存儲(chǔ),其中RGB的0到255的范圍對(duì)應(yīng)法線向量的0到1的范圍。這樣,在繪制精靈時(shí),除了RGB值之外,還能提取出法線信息,從而計(jì)算出反射的光照。
雖然在計(jì)算上相對(duì)簡(jiǎn)單,但法線貼圖需要額外的資產(chǎn)加載過(guò)程,即需要有人制作這些法線貼圖,或者從其他地方獲取它們。盡管如此,一旦擁有法線貼圖,計(jì)算過(guò)程就能高效地進(jìn)行。
黑板:表面的描述
接下來(lái)的內(nèi)容涉及到如何處理不同類(lèi)型的表面。為了增加視覺(jué)上的趣味性,可能需要能夠模擬不同的表面類(lèi)型,比如一些表面是光滑的,而另一些則是啞光的。為了實(shí)現(xiàn)這一點(diǎn),首先需要計(jì)算出表面的法線信息,并結(jié)合其他因素來(lái)計(jì)算光照。
在這個(gè)過(guò)程中,光照計(jì)算會(huì)使用到法線貼圖來(lái)確定每個(gè)表面的朝向。通過(guò)結(jié)合光源方向和視角方向,可以計(jì)算出相應(yīng)的光照值。由于沒(méi)有3D場(chǎng)景的完整描述,因此無(wú)法進(jìn)行完全真實(shí)的光照模擬,但是可以構(gòu)造一個(gè)“虛擬世界”,通過(guò)這種方式生成合成的光照效果。這是為了彌補(bǔ)缺少3D描述的問(wèn)題。
表面的描述除了法線外,還需要考慮一些其他參數(shù),比如表面的光滑度或光澤度??梢允褂靡粋€(gè)從0到1的值來(lái)表示表面的光澤度,其中0代表完全啞光,1代表非常光滑或非常反射。這些值將決定表面如何與光源交互,從而影響最終的光照效果。在初步實(shí)現(xiàn)中,控制表面光澤度將是一個(gè)主要的調(diào)控手段。
黑板:光源的描述
在處理光照描述時(shí),遇到了一個(gè)問(wèn)題,即如何設(shè)計(jì)光源。最簡(jiǎn)單的做法是將光源直接放置在場(chǎng)景中,但為了實(shí)現(xiàn)更豐富的效果,可以考慮一些更復(fù)雜的方案。例如,如果我們考慮將光線指向地面,可以查看地面區(qū)域的顏色,這樣就能夠模擬從地面反射的光。類(lèi)似地,當(dāng)光線指向上方時(shí),可以想象天空紋理或者室內(nèi)天花板作為光源。
對(duì)于水平指向的光線,它可能會(huì)照射到場(chǎng)景中的其他物體,從而產(chǎn)生反射光。由此可以提出一個(gè)想法,使用一個(gè)三平面方案來(lái)處理光照計(jì)算:地面、天空以及其他物體的反射區(qū)域。初步考慮時(shí),可以簡(jiǎn)化處理,暫時(shí)忽略水平方向的光照,專(zhuān)注于地面和天空兩個(gè)方向的光照。
這種方案的核心是通過(guò)向上(天空)和向下(地面)這兩個(gè)方向來(lái)獲取光照值,以此來(lái)推測(cè)場(chǎng)景中的光照情況。這個(gè)方法仍然是一個(gè)假設(shè),目的是為了探索如何為游戲中的物體計(jì)算光照值,并嘗試找到一種有效的實(shí)現(xiàn)方式。
黑板:提案
在處理法線值時(shí),可以通過(guò)Z軸的值來(lái)決定采樣方向。假設(shè)我們用Z值來(lái)表示光線指向的上下方向,Z值的不同會(huì)影響我們從上方和下方的光源進(jìn)行采樣的方式。具體來(lái)說(shuō),當(dāng)光線指向向上時(shí),采樣天空紋理;而指向向下時(shí),采樣地面紋理。隨著光線從向上指向轉(zhuǎn)變?yōu)橄蛳轮赶?#xff0c;我們會(huì)改變采樣的區(qū)域。
為了實(shí)現(xiàn)這一點(diǎn),可以使用一個(gè)三平面方案:上方、中央和下方平面。初步假設(shè),如果我們進(jìn)行均勻光照處理,Z值會(huì)影響我們?cè)谶@三者之間的采樣。例如,當(dāng)Z值為-1時(shí),完全采樣下方;Z值為1時(shí),完全采樣上方;而Z值為0.5時(shí),意味著我們?cè)谥醒肫矫孢M(jìn)行采樣。這樣,通過(guò)在這三者之間進(jìn)行線性混合,可以實(shí)現(xiàn)不同方向的光照貢獻(xiàn)。
法線的其他參數(shù)將決定我們?cè)谶@三個(gè)平面之間如何進(jìn)行采樣。假設(shè)我們將這些平面視作圖片,并向這些平面投射光線,基于光線的方向和當(dāng)前位置,我們可以計(jì)算出光線在哪個(gè)平面上交點(diǎn),從而獲取對(duì)應(yīng)的顏色值。
盡管中間平面還不確定如何處理,但上方和下方的處理是相對(duì)簡(jiǎn)單的。我們可以通過(guò)計(jì)算這些投射值,得出最終的光照結(jié)果。重要的是,光線的方向會(huì)影響最終的采樣結(jié)果,比如當(dāng)光線指向上方時(shí),永遠(yuǎn)不會(huì)與下方平面交集;同樣,當(dāng)光線指向下方時(shí),也不會(huì)與上方平面交集。
這個(gè)方法本質(zhì)上是一種簡(jiǎn)化的技巧,因?yàn)槲覀儫o(wú)法計(jì)算出真實(shí)的三維光照效果,但它的目的是盡量實(shí)現(xiàn)接近真實(shí)的光照效果,通過(guò)合理的假設(shè)和采樣來(lái)達(dá)到預(yù)期的視覺(jué)效果。
黑板:基于表面描述的模糊處理
在描述表面光澤度時(shí),提出了一種方法,將零到一的光澤度值與模糊處理結(jié)合起來(lái)。對(duì)于非常光滑的表面,反射的光線非常精確,幾乎能夠清晰地看到光源的細(xì)節(jié),如窗戶的輪廓或陽(yáng)光的反射;而對(duì)于較為暗淡的表面,光線反射則比較模糊,只能看到光的顏色和一些輕微的光暈。為了模擬這種效果,可以通過(guò)對(duì)上方和下方的圖像進(jìn)行模糊處理來(lái)實(shí)現(xiàn)。
具體做法是,對(duì)于每一個(gè)光源(上方和下方),先對(duì)其圖像進(jìn)行多次模糊處理。例如,可以從一個(gè)256x256的圖像開(kāi)始,然后逐漸下采樣到128x128、64x64等,最終獲得多個(gè)不同模糊級(jí)別的圖像。使用這個(gè)模糊圖像時(shí),利用零到一的光澤度值來(lái)決定選擇哪兩個(gè)模糊圖像進(jìn)行插值。例如,光澤度為0時(shí)選擇最模糊的圖像,光澤度為1時(shí)選擇最清晰的圖像,在中間時(shí)則根據(jù)需要進(jìn)行插值。
為了簡(jiǎn)化計(jì)算,可以將光澤度值離散化,僅通過(guò)幾個(gè)二進(jìn)制位來(lái)表示選擇的模糊圖像,這樣就不需要進(jìn)行復(fù)雜的插值運(yùn)算,可以顯著提高效率。通過(guò)這種方式,盡管并不進(jìn)行精確的動(dòng)態(tài)變化(如光澤度在渲染過(guò)程中變化),但仍然可以在不增加大量計(jì)算負(fù)擔(dān)的情況下實(shí)現(xiàn)較為理想的效果。
這種方案雖然可能不完美,但作為一種實(shí)驗(yàn)性的光照近似方法,可以用于2D游戲的開(kāi)發(fā)中,尤其是在沒(méi)有復(fù)雜光照模型的情況下,嘗試找到一種有效的解決方案。
介紹法線貼圖的概念
首先,需要在管道中引入法線貼圖的概念,因?yàn)槟壳斑€沒(méi)有這個(gè)功能。當(dāng)系統(tǒng)處理中有紋理時(shí),需要在紋理之外引入一個(gè)法線貼圖,并指向該法線貼圖。在整個(gè)流程中,法線貼圖需要被傳遞,并在渲染器中提取,以便在繪制三角形時(shí)使用。
在處理時(shí),可以將法線貼圖作為參數(shù)傳遞,并在后續(xù)的繪制過(guò)程中加載該貼圖。此時(shí),可以利用之前處理的工作來(lái)采樣法線貼圖。理論上,法線貼圖也應(yīng)該使用雙線性插值進(jìn)行混合,這將引入一些問(wèn)題,因?yàn)椴逯颠^(guò)程中需要考慮法線貼圖的每個(gè)采樣點(diǎn),確保法線的平滑過(guò)渡和準(zhǔn)確性。
黑板:法線必須是正常的
在處理法線貼圖時(shí),需要注意一個(gè)問(wèn)題:法線必須是單位向量。如果直接將法線進(jìn)行線性插值,可能會(huì)導(dǎo)致結(jié)果不符合規(guī)范,因?yàn)椴逯岛蟮姆ň€向量長(zhǎng)度可能會(huì)不正常,變得過(guò)短。這意味著,如果我們直接進(jìn)行線性插值,結(jié)果的法線將不再是標(biāo)準(zhǔn)法線,可能無(wú)法正確地反映表面的方向。
假設(shè)有兩個(gè)法線,一個(gè)指向某個(gè)方向,另一個(gè)指向不同的方向,若對(duì)它們進(jìn)行50%的線性插值,得到的法線向量會(huì)顯得比預(yù)期的要短,偏離了單位向量的標(biāo)準(zhǔn)長(zhǎng)度。如果不對(duì)法線進(jìn)行規(guī)范化處理,這樣的插值結(jié)果會(huì)導(dǎo)致計(jì)算錯(cuò)誤,因?yàn)榉ň€必須保持單位長(zhǎng)度。
因此,處理法線時(shí)需要特別小心。通常,線性插值用于顏色時(shí)效果較好,但對(duì)法線進(jìn)行插值時(shí),必須在插值后進(jìn)行法線的重新規(guī)范化,確保最終的法線向量是單位長(zhǎng)度,這樣才能正確反映表面法線的方向。因此,插值法線時(shí)需要考慮將結(jié)果重新投影到單位球面上,以保持法線的正確性。
介紹 Unpack4x8 并解包 Texels
為了提高代碼的復(fù)用性,可以將紋理采樣的操作封裝成一個(gè)內(nèi)聯(lián)函數(shù),這樣就不需要每次都重復(fù)相同的解包過(guò)程。具體做法是創(chuàng)建一個(gè)內(nèi)聯(lián)函數(shù) unpack4x8
,它接受一個(gè)打包值(packed value
),然后進(jìn)行解包操作,返回解包后的結(jié)果。
通過(guò)這種方式,可以將解包的邏輯集中到一個(gè)地方,避免在代碼中多次編寫(xiě)相同的解包代碼。這種做法不僅讓代碼更簡(jiǎn)潔,還提高了可維護(hù)性。函數(shù)定義如下:
inline v4 Unpack4x8(uint32 Packed) {v4 Result = {// 獲取源像素的紅色通道值(real32)((Packed >> 16) & 0xFF),// 獲取源像素的綠色通道值(real32)((Packed >> 8) & 0xFF),// 獲取源像素的藍(lán)色通道值(real32)((Packed >> 0) & 0xFF),// 獲取源像素的Alpha通道值(范圍0-255)((real32)((Packed >> 24) & 0xFF))};// 返回解包后的結(jié)果return Result;
}
之后,可以在代碼中替換所有的解包操作,使用 unpack4x8
函數(shù)進(jìn)行調(diào)用,例如:
v4 TexelA = Unpack4x8(TexelPtrA);
這樣,每次需要進(jìn)行紋理采樣時(shí),都可以調(diào)用 unpack4x8
函數(shù),而不需要重復(fù)寫(xiě)解包的具體實(shí)現(xiàn)。這種封裝方式使得代碼更加簡(jiǎn)潔和易于維護(hù)。
類(lèi)似地解包法線
在處理法線貼圖時(shí),解包的過(guò)程與紋理貼圖類(lèi)似,可以創(chuàng)建一個(gè)與紋理貼圖相同的解包函數(shù),只不過(guò)將 Texel
類(lèi)型替換為 Normal
類(lèi)型。通過(guò)這種方式,可以將法線貼圖的解包過(guò)程封裝成一個(gè)通用的函數(shù),簡(jiǎn)化代碼。
然而,使用32位值解包時(shí),會(huì)得到四個(gè)值,而實(shí)際上我們只需要最多三個(gè)值,甚至可能只需要兩個(gè)值。那么,剩下的第四個(gè)值該怎么處理呢?這個(gè)問(wèn)題需要考慮如何處理這個(gè)多余的值。一般來(lái)說(shuō),解包后的數(shù)據(jù)通常會(huì)存儲(chǔ)成三個(gè)分量(X、Y、Z),而第四個(gè)分量可能是冗余的,除非它有特定的用途,比如用于其他計(jì)算。
因此,在實(shí)現(xiàn)解包函數(shù)時(shí),可以考慮只返回需要的三個(gè)分量,忽略多余的第四個(gè)分量,或者根據(jù)具體需求對(duì)其進(jìn)行處理。這樣可以確保解包后的數(shù)據(jù)更加精確,并減少不必要的數(shù)據(jù)浪費(fèi)。
黑板:我們常常將表面描述烘焙進(jìn)法線貼圖中
在圖形處理中,遇到這種情況時(shí),通常會(huì)將表面描述信息與法線貼圖一起存儲(chǔ)。如果有一個(gè)四通道的法線貼圖,可以將 X 和 Y 分量存儲(chǔ)在前兩個(gè)通道中,Z 分量存儲(chǔ)在第三個(gè)通道,而將最后一個(gè)通道(即 Alpha 通道)用來(lái)存儲(chǔ)表面的光澤度信息。這樣,即使法線貼圖的第四個(gè)分量是冗余的,也能有效地利用它存儲(chǔ)光澤度,表示表面在該點(diǎn)的光滑程度。
通過(guò)這種方式,可以繪制一個(gè)既具有反射部分又具有非反射部分的單一精靈,這樣的做法為圖形設(shè)計(jì)帶來(lái)更多的靈活性。例如,可以在一個(gè)圖像中同時(shí)處理光滑和粗糙表面,提升圖形效果的多樣性和表現(xiàn)力。
線性插值法線
在這里,首先可以進(jìn)行一個(gè)線性插值(lerp)操作,將 Texel 值進(jìn)行插值處理,然后對(duì)法線值進(jìn)行同樣的插值。這樣,插值后的法線可以直接用于后續(xù)的處理。接下來(lái),可以決定是否需要對(duì)法線進(jìn)行更復(fù)雜的操作,例如重新歸一化(renormalize)或其他處理,但此時(shí)已經(jīng)得到了可以使用的法線。
在進(jìn)行光照計(jì)算時(shí),可以使用法線與其他輸入顏色進(jìn)行哈達(dá)瑪積(Hadamard product)運(yùn)算,用于計(jì)算表面的光照效果。通過(guò)這樣的方式,法線在整個(gè)渲染管線中可以用于進(jìn)一步的光照查找和其他相關(guān)的圖形處理。
介紹環(huán)境貼圖
為了計(jì)算光照效果,需要引入更多的位圖,并且這將導(dǎo)致我們需要處理不同模糊級(jí)別的多種位圖。為了處理這些位圖,一個(gè)簡(jiǎn)單的 loaded_bitmap
類(lèi)型可能并不合適,因?yàn)槲覀冃枰幚聿煌哪:潭?#xff0c;因此需要一種結(jié)構(gòu)化的系統(tǒng)來(lái)管理這些位圖。
這個(gè)系統(tǒng)可能包含多個(gè)不同模糊程度的位圖。舉例來(lái)說(shuō),我們可以為環(huán)境貼圖(evironment_map)引入一個(gè)結(jié)構(gòu),其中包含不同的細(xì)節(jié)層級(jí)(levels of detail,LOD)。例如,假設(shè)我們有四個(gè)不同的模糊層級(jí),每個(gè)層級(jí)代表不同的模糊程度。這個(gè)系統(tǒng)將包含多個(gè)貼圖,并為每個(gè)貼圖提供不同模糊級(jí)別的內(nèi)容。
在實(shí)現(xiàn)時(shí),首先我們需要處理位圖的寬度和高度??紤]到這些位圖的大小需要是2的冪次方(powers of 2),可以采用這種方式來(lái)簡(jiǎn)化對(duì)位圖的包裹(wrapping)和裁剪(clamping)操作。每個(gè)貼圖的寬度和高度將采用2的冪次方,例如256x256或512x512等,確??梢愿咝У剡M(jìn)行處理。
此外,支持矩形貼圖也是可能的,盡管這可能會(huì)增加復(fù)雜性。為了簡(jiǎn)化,暫時(shí)可以將寬度和高度都設(shè)為2的冪次方,并允許矩形尺寸。每個(gè)貼圖的細(xì)節(jié)層級(jí)(如第0層細(xì)節(jié)圖)可以是一個(gè)特定的2的冪次方大小,之后其他層級(jí)的大小會(huì)逐漸縮小。
這一過(guò)程中需要處理的環(huán)境貼圖包括上面、下面和中間等不同方向的位圖,這樣可以為不同的方向提供不同的環(huán)境效果。然而,需要注意的是,這種方式可能會(huì)導(dǎo)致非常高的計(jì)算開(kāi)銷(xiāo),尤其是在沒(méi)有優(yōu)化的情況下,性能會(huì)非常差,即使引入了優(yōu)化,計(jì)算開(kāi)銷(xiāo)依然很大。因此,盡管這種方法很靈活,但實(shí)際應(yīng)用時(shí)需要考慮到性能問(wèn)題。
從法線的 Z 軸設(shè)置 tEnvMap
在處理環(huán)境貼圖時(shí),首先需要根據(jù)法線向量來(lái)確定采樣的方向。法線向量的Z分量決定了是否朝上或朝下,因此可以利用Z分量來(lái)幫助決定選擇哪個(gè)環(huán)境貼圖層級(jí)。
為了進(jìn)行這種采樣,首先需要定義一個(gè)名為 tEnvMap
的變量,用于表示環(huán)境貼圖的混合方式。這個(gè)變量基于法線的Z值來(lái)確定混合的方式。具體來(lái)說(shuō),如果法線的Z分量大于或小于0.5,就會(huì)選擇不同的環(huán)境貼圖層級(jí)。通過(guò)這種方式,可以根據(jù)法線的方向靈活地從環(huán)境貼圖中選擇合適的部分進(jìn)行混合。
然而,在處理某些特定情況下,Z值恰好等于0.5時(shí),可能會(huì)遇到一些問(wèn)題,因?yàn)檫@種情況的計(jì)算結(jié)果并不明確,可能導(dǎo)致混合計(jì)算變得有些不確定。因此,這種方式雖然簡(jiǎn)單直接,但在實(shí)際應(yīng)用中仍然需要考慮這些邊界情況。
黑板:我們可能需要將其切換到接近 0.25
在選擇環(huán)境貼圖時(shí),可以考慮通過(guò)調(diào)整法線的Z分量來(lái)決定使用哪一層環(huán)境貼圖。為了更精確地控制混合方式,可能會(huì)將Z值的閾值設(shè)置為小于0.5的值,比如0.25,意味著只有那些指向較低方向的法線才會(huì)選擇底部的貼圖。這樣,如果法線的Z值小于0.5,就選擇底部環(huán)境貼圖;如果大于0.5,則選擇頂部環(huán)境貼圖;而中間區(qū)域的法線則只使用中間的環(huán)境貼圖。
這種方法靈活地根據(jù)法線的方向來(lái)選擇合適的環(huán)境貼圖層級(jí),但具體的閾值和實(shí)現(xiàn)方式仍然需要在實(shí)際開(kāi)發(fā)過(guò)程中進(jìn)行微調(diào)。
設(shè)置環(huán)境貼圖查找
在處理環(huán)境貼圖時(shí),首先根據(jù)法線的Z值決定使用哪個(gè)貼圖。如果Z值小于0.25,就會(huì)選擇底部的貼圖;如果大于0.75,則選擇頂部的貼圖;在其他情況下,使用中間的貼圖。為了平滑過(guò)渡,使用法線值(tFarColor
)來(lái)計(jì)算混合比例。對(duì)于低于0.25的情況,使用tFarMap = 1.0f - (tEnvMap / 0.25f)
值來(lái)確定底部貼圖的貢獻(xiàn)比例;對(duì)于大于0.75的情況,則用tFarMap = (1.0f - tEnvMap) / 0.25f
值來(lái)確定頂部貼圖的貢獻(xiàn)比例。
通過(guò)這些步驟,可以計(jì)算出從每個(gè)貼圖中獲取的顏色值,并根據(jù)混合比例將它們?nèi)诤?。最?#xff0c;主要的挑戰(zhàn)是如何正確地從這些環(huán)境貼圖中進(jìn)行采樣,特別是如何結(jié)合法線和表面描述值來(lái)決定應(yīng)該采樣哪個(gè)貼圖。需要傳遞的參數(shù)包括表面描述值、法線的XYZ值和要采樣的環(huán)境貼圖。
創(chuàng)建屏幕空間 UV
在進(jìn)行環(huán)境貼圖采樣時(shí),還需要知道屏幕上的位置,以便計(jì)算出正確的采樣區(qū)域。這就需要一個(gè)屏幕空間中的位置值,也就是像素在屏幕上的位置,通常稱(chēng)為PixelP
。為了進(jìn)行后續(xù)計(jì)算,需要將這個(gè)位置值歸一化。
歸一化的方法是通過(guò)屏幕的寬度和高度最大值來(lái)進(jìn)行。具體做法是將像素的X坐標(biāo)除以寬度最大值,將Y坐標(biāo)除以高度最大值,從而得到一個(gè)歸一化后的屏幕空間坐標(biāo)ScreenSpaceUV
。為了提高效率,可以提前計(jì)算出這些最大值的倒數(shù),然后直接用它們來(lái)計(jì)算歸一化的坐標(biāo)。
通過(guò)這種方法,能夠準(zhǔn)確地將屏幕空間的坐標(biāo)轉(zhuǎn)換為歸一化的坐標(biāo)值,進(jìn)而為后續(xù)的環(huán)境貼圖采樣和其他圖形操作提供必要的信息。
將這個(gè)屏幕空間 UV 傳遞給 SampleEnvironmentMap
通過(guò)計(jì)算得到屏幕空間的歸一化坐標(biāo)ScreenSpaceUV
后,可以將其用于采樣函數(shù)中,了解當(dāng)前的采樣方向、表面描述、光澤度等信息,并據(jù)此從環(huán)境貼圖中進(jìn)行采樣。這個(gè)過(guò)程可以通過(guò)一個(gè)函數(shù),比如SampleEnvironmentMap
來(lái)實(shí)現(xiàn),利用歸一化的屏幕坐標(biāo)、法線信息以及表面描述進(jìn)行采樣。
此時(shí),采樣函數(shù)能夠根據(jù)提供的參數(shù)和坐標(biāo)位置,從對(duì)應(yīng)的環(huán)境貼圖中獲取所需的信息,從而完成環(huán)境映射的計(jì)算。
檢查是否有法線貼圖
如果存在法線貼圖,則無(wú)需執(zhí)行上述步驟。將來(lái)可以為著色器編寫(xiě)兩個(gè)版本,分別評(píng)估不同的選項(xiàng)。假設(shè)已經(jīng)有了這些步驟,接下來(lái)可以編寫(xiě)SampleEnvironmentMap
函數(shù),最終得到光照顏色。為了簡(jiǎn)化處理,當(dāng)前可以通過(guò)這些步驟來(lái)驗(yàn)證和調(diào)試。
預(yù)計(jì)算 Texel 的光照
為了簡(jiǎn)化處理,可以使用Hadamard積,將每個(gè)像素值與光照貢獻(xiàn)進(jìn)行相乘。基本思路是,假設(shè)像素的初始顏色代表最大亮度,然后根據(jù)從環(huán)境中接收到的光線來(lái)調(diào)節(jié)它。簡(jiǎn)單來(lái)說(shuō),像素值告訴我們反射了多少光,而通過(guò)將它與接收到的光顏色相乘,可以計(jì)算出實(shí)際反射的光量。這是為了確定最終的光照效果。
我們還沒(méi)有討論過(guò)如何計(jì)算光照方程
目前并沒(méi)有詳細(xì)討論如何計(jì)算光照方程,部分原因是我們并沒(méi)有完全實(shí)現(xiàn)真實(shí)的光照計(jì)算。因此,如何處理接收到的光照信息并不完全明確,畢竟我們采用了一種近似的方式來(lái)計(jì)算輸入的光照,而并非追求高度的準(zhǔn)確性。實(shí)際上,我們可能更傾向于使用一種“hackish”方法,調(diào)整參數(shù)直到結(jié)果看起來(lái)合適,因?yàn)閷?duì)于完全人造的光照值,進(jìn)行準(zhǔn)確計(jì)算并沒(méi)有太大意義??傮w來(lái)說(shuō),目前的設(shè)計(jì)包含了光照計(jì)算的基本框架。
獲取 SampleEnvironmentMap 并暫時(shí)返回法線
目前的實(shí)現(xiàn)方法是簡(jiǎn)化為直接返回法線作為光照顏色,即不進(jìn)行實(shí)際的光照計(jì)算。目的是為了快速測(cè)試和驗(yàn)證系統(tǒng)是否能夠正常工作,而不是進(jìn)行真實(shí)的光照計(jì)算。在這個(gè)過(guò)程中,使用了一個(gè)“鏡面反射”值(可能被稱(chēng)為粗糙度),其中0表示完全反射,1表示非常粗糙。此外,代碼中還存在一些調(diào)整,確保計(jì)算中能夠使用XYZ而不僅僅是RGB,并且避免修改透明度(Alpha值),因?yàn)楫?dāng)前的設(shè)計(jì)并不涉及反射亮度的變化。
管道處理
在復(fù)雜的渲染過(guò)程中,通常最困難的部分并不是編寫(xiě)計(jì)算公式,而是“管道”部分,即確保所有數(shù)據(jù)能夠正確地通過(guò)系統(tǒng)并在合適的位置。涉及到的工作包括確保不同的緩沖區(qū)能夠正確配對(duì)和管理,尤其是在GPU渲染中,還需要處理數(shù)據(jù)在GPU與主機(jī)之間的傳輸,這會(huì)帶來(lái)大量的管理和記錄工作。當(dāng)前,渲染沒(méi)有使用GPU,因此系統(tǒng)的管理相對(duì)簡(jiǎn)單,但隨著復(fù)雜度的增加,問(wèn)題也會(huì)逐漸顯現(xiàn)。
在具體實(shí)現(xiàn)中,傳遞環(huán)境貼圖時(shí),需要擴(kuò)展相關(guān)的結(jié)構(gòu)以包含所有必要的信息。為了調(diào)試,代碼返回的是渲染坐標(biāo)系,確保能正確傳遞信息并避免不必要的設(shè)置。最終,刪除了不必要的代碼部分,保持了清晰簡(jiǎn)潔的結(jié)構(gòu)。
在游戲中查看并生成一個(gè)可以玩的法線貼圖
為了生成法線貼圖,首先需要一個(gè)可以生成可預(yù)測(cè)法線的算法。由于目前沒(méi)有現(xiàn)成的法線貼圖,因此決定使用一個(gè)簡(jiǎn)單的算法直接為樹(shù)生成法線貼圖。這個(gè)算法類(lèi)似于創(chuàng)建一個(gè)空白位圖,但目標(biāo)是生成一個(gè)球形法線貼圖。
步驟如下:
- 定義一個(gè)函數(shù),可能命名為
MakeSphereNormalMap
,該函數(shù)將生成一個(gè)球形法線貼圖。 - 函數(shù)的輸入是一個(gè)加載的位圖指針,并且該函數(shù)會(huì)在位圖中寫(xiě)入生成的法線。
- 這個(gè)法線貼圖的生成將基于球形的法線分布,用來(lái)模擬一個(gè)合成的環(huán)境,以便可以在沒(méi)有真實(shí)法線貼圖的情況下進(jìn)行測(cè)試和調(diào)整。
最終,這將為后續(xù)的渲染提供一個(gè)簡(jiǎn)單的法線貼圖,雖然這種方法不是真實(shí)的法線數(shù)據(jù),但它足夠用來(lái)進(jìn)行初步的可視化和調(diào)試。
黑板:我們將要?jiǎng)?chuàng)建的法線貼圖
為了創(chuàng)建法線貼圖,計(jì)劃生成一個(gè)簡(jiǎn)單的球形法線圖。這個(gè)法線圖將模擬一個(gè)球形的表面,其中法線會(huì)根據(jù)位置指向不同的方向。
具體做法如下:
- 法線圖會(huì)模擬一個(gè)圓頂形狀,法線從頂部指向觀眾的方向。
- 在圖的中心,法線會(huì)指向上方,而在靠近邊緣的地方,法線會(huì)稍微偏離中心。
- 這樣,法線圖看起來(lái)就像是從上方看一個(gè)圓頂,法線會(huì)根據(jù)其在圖中的位置而變化,模擬一個(gè)簡(jiǎn)單的球形表面。
這種法線圖的生成方法雖然非常簡(jiǎn)單,但足夠用來(lái)進(jìn)行初步的渲染測(cè)試和調(diào)試。
實(shí)現(xiàn) MakeSphereNormalMap
為了生成法線貼圖,將對(duì)每個(gè)像素進(jìn)行處理,并根據(jù)其在位圖中的位置計(jì)算法線。
- 遍歷X和Y坐標(biāo),逐個(gè)像素生成法線。
- 通過(guò)計(jì)算每個(gè)像素的位置,使用X和Y的歸一化值(范圍從0到1)來(lái)定位每個(gè)像素的UV坐標(biāo)。
- 將這些歸一化值用于計(jì)算法線。在計(jì)算法線時(shí),X和Y值將從0到1映射到-1到1的范圍,這樣可以使法線指向不同的方向。例如,X值的變化會(huì)影響法線的水平方向。
- 然而,單純這樣計(jì)算得到的只是平面上的法線值,并不足以得到一個(gè)完整的球形法線貼圖。因?yàn)樵谄矫嫔戏ň€的Z值未被考慮,因此需要在計(jì)算中考慮Z軸的變化。
整體思路是通過(guò)遍歷位圖中的每個(gè)像素,利用該像素的位置生成一個(gè)法線,但這個(gè)方法目前存在問(wèn)題,需要進(jìn)一步補(bǔ)充Z軸的計(jì)算。
黑板:再想一想這個(gè)問(wèn)題
為了生成球形法線貼圖,首先需要考慮如何獲取球面上的三個(gè)法線分量。原本期望通過(guò)簡(jiǎn)單的計(jì)算來(lái)生成法線,但實(shí)際上需要更細(xì)致地思考球面的具體結(jié)構(gòu)。
一種簡(jiǎn)單的方式是直接計(jì)算Z軸的值,可以通過(guò)X和Y的值來(lái)推導(dǎo)Z軸的法線方向。具體來(lái)說(shuō),離球心越近的區(qū)域,其Z值就會(huì)相應(yīng)改變,這樣可以有效地決定法線的Z分量。通過(guò)這種方式,可以較為直接地計(jì)算出法線。
暫時(shí)加入 Normal.z 計(jì)算,并完成 MakeSphereNormalMap
為了生成法線貼圖,首先需要計(jì)算每個(gè)像素的法線。對(duì)于每個(gè)法線,需要將其從負(fù)1到1的范圍轉(zhuǎn)換為0到255的范圍,這樣可以適應(yīng)顏色編碼。在生成法線時(shí),X和Y分量需要進(jìn)行處理,將其范圍從-1到1調(diào)整為0到255。而Z分量因?yàn)槭冀K指向上方,可以直接處理為一個(gè)較小的值。
對(duì)于每個(gè)像素,生成的法線需要存儲(chǔ)在位圖中,且顏色值需要與法線值相對(duì)應(yīng)。粗糙度值也會(huì)被轉(zhuǎn)換為255范圍內(nèi)的值,最后再根據(jù)需求對(duì)法線進(jìn)行正確的解包和存儲(chǔ)。
這整個(gè)過(guò)程涉及到將計(jì)算得出的法線值與表面特性(如粗糙度)一起編碼到位圖中,并最終寫(xiě)入。
在 game_math.h 中添加 Normalize
在游戲的數(shù)學(xué)庫(kù)中,發(fā)現(xiàn)并沒(méi)有實(shí)現(xiàn)標(biāo)準(zhǔn)化(normalize)函數(shù)。標(biāo)準(zhǔn)化操作非常簡(jiǎn)單,基本上就是將一個(gè)向量除以其長(zhǎng)度。這是一個(gè)常見(jiàn)的操作,盡管之前在代碼中做過(guò)多次類(lèi)似的處理,但沒(méi)有單獨(dú)實(shí)現(xiàn)一個(gè)標(biāo)準(zhǔn)化函數(shù)。標(biāo)準(zhǔn)化操作的具體實(shí)現(xiàn)是:將向量的每個(gè)分量除以向量的長(zhǎng)度。由于時(shí)間緊迫,決定繼續(xù)進(jìn)行這個(gè)實(shí)現(xiàn),并使用簡(jiǎn)單的方式來(lái)完成該操作。
編譯并在游戲中查看效果
目前,編譯工作正在進(jìn)行中,大部分內(nèi)容已經(jīng)準(zhǔn)備好,但還沒(méi)有完全完成。因此,需要在接下來(lái)的時(shí)間里繼續(xù)處理法線貼圖的相關(guān)工作。雖然已經(jīng)設(shè)定了生成球體法線貼圖的目標(biāo),但實(shí)際實(shí)現(xiàn)還沒(méi)有完全正確,下一步需要在代碼中進(jìn)一步調(diào)整和完善,特別是在法線貼圖生成方面。
你的 2D art已經(jīng)有一些陰影了。這是否會(huì)在與這個(gè)動(dòng)態(tài)光照結(jié)合時(shí)代表環(huán)境光?
在2D游戲中,直接通過(guò)代碼實(shí)現(xiàn)所有光照是不現(xiàn)實(shí)的,因?yàn)樾枰墓庹站忍?#xff0c;2D游戲無(wú)法提供足夠的細(xì)節(jié)來(lái)有效實(shí)現(xiàn)這一點(diǎn)。因此,藝術(shù)資源會(huì)盡量看起來(lái)像是有一定的環(huán)境光照,呈現(xiàn)出一種基礎(chǔ)的光照效果,然后再在此基礎(chǔ)上添加動(dòng)態(tài)光照。與通常的光照計(jì)算方式不同,這種方法更像是減法過(guò)程,而不是加法過(guò)程。這種做法是為了在技術(shù)限制下,盡量表現(xiàn)出合適的光照效果。
你認(rèn)為在只使用 CPU(而非 GPU)時(shí),這樣的引擎能增加多少能力,直到因性能限制而受阻?
討論中提到,使用CPU版本進(jìn)行教育目的時(shí),性能問(wèn)題并不重要,因?yàn)椴粫?huì)將該版本作為玩家使用的最終版本。如果需要在沒(méi)有GPU的環(huán)境中發(fā)布,可能需要進(jìn)行大量?jī)?yōu)化才能使動(dòng)態(tài)光照等功能運(yùn)行,但這并不是主要關(guān)注點(diǎn)。實(shí)際上,在較低的分辨率和幀率下運(yùn)行,性能問(wèn)題也不嚴(yán)重,因?yàn)樵摪姹緝H用于展示如何實(shí)現(xiàn)這些功能。如果要在合理分辨率和幀率下運(yùn)行,動(dòng)態(tài)光照會(huì)很消耗性能,因此可能需要選擇更簡(jiǎn)單的光照方案,但考慮到這是教育用途,性能優(yōu)化并不是重點(diǎn)。
你不需要將法線做 SRBGToLinear1 處理,或者使用法線而非 Texel 來(lái)進(jìn)行插值嗎?
法線不應(yīng)該使用sRGB到線性空間的轉(zhuǎn)換,因?yàn)榉ň€不是感知亮度值,而是以線性方式編碼的。因此,在解碼時(shí),應(yīng)該避免將法線轉(zhuǎn)為0到1的范圍,直到在插值(lerp)操作之后再進(jìn)行。這樣可以確保插值操作在線性空間中進(jìn)行,從而節(jié)省計(jì)算量。在插值之后,可以統(tǒng)一將其轉(zhuǎn)換為適當(dāng)?shù)姆秶?#xff0c;從而避免不必要的計(jì)算。
Normal.z = Sqrt(1.0f - Normal.x * Normal.x - Normal.y * Normal.y);
某個(gè)數(shù)學(xué)公式表示法線的Z分量為 sqrt(1 - normal.x^2 - normal.y^2)
,并提出這種做法是否能夠生成正確的球形。假設(shè)該公式是正確的,可以通過(guò)計(jì)算 X 和 Y 的平方和來(lái)推算出 Z 分量,從而保持法線的長(zhǎng)度為1,并最終得到一個(gè)球形的法線。雖然有些不確定,但暫時(shí)假設(shè)它能正常工作,并繼續(xù)進(jìn)行后續(xù)工作。
問(wèn)題是,如果超出球體范圍,它將取負(fù)數(shù)的平方根
討論中提到,當(dāng)數(shù)值超出球體范圍時(shí),可能會(huì)出現(xiàn)負(fù)數(shù)的平方根。對(duì)此,可以通過(guò)限制范圍來(lái)避免,比如使用clamp
函數(shù)或者取最小值,確保數(shù)值不會(huì)超過(guò)1。這是一個(gè)簡(jiǎn)單的測(cè)試,因此不需要處理超出范圍的情況。最后,討論結(jié)束時(shí)提到,雖然可以嘗試運(yùn)行正常貼圖來(lái)查看效果,但決定將這部分留到下次進(jìn)行。
我們的提問(wèn)環(huán)節(jié)到此結(jié)束
已完成了大部分基礎(chǔ)工作,接下來(lái)將繼續(xù)進(jìn)行正常貼圖的調(diào)試,嘗試修復(fù)代碼中的小錯(cuò)誤,并檢查正常貼圖的效果。接下來(lái)還會(huì)嘗試將其與環(huán)境貼圖結(jié)合,查看其實(shí)際效果和可用性。