倉庫:https://gitee.com/mrxiao_com/2d_game_2
開始進行一點回顧
今天的目標是繼續實現正常貼圖的操作,盡管目前我們還沒有足夠的光照信息來使其完全有用。昨日完成了正常貼圖相關的基礎工作,接下來將集中精力實現正常貼圖的基本操作,并準備好在下周進一步處理如何生成可用的光照字段,供正常貼圖使用。
首先,計劃實現一個模擬球形正常貼圖生成器,然后將其與環境貼圖進行采樣,最終驗證生成的正常貼圖是否合理。盡管之前沒有正確連接這些部分,但目標是確保正常貼圖能正確顯示并為接下來的光照處理做好準備。
討論 MakeSphereNormalMap 和 GPU 與 CPU 渲染器
首先,實現正常貼圖時,需要傳入一個位圖和粗糙度值。粗糙度值作為材質參數,被打包到透明度通道中。正如之前所討論的那樣,并不一定需要在正常貼圖中存儲所有三個法線值,因為在GPU中,通常只會存儲兩個值。因此,可能在CPU端也只存儲兩個值。
主要目標是使用CPU渲染的方式幫助理解整個過程,并展示如何優化,而不是為了實現游戲中高效的性能。畢竟,CPU并非為圖形渲染設計,盡管隨著AVX-512等技術的發展,CPU的圖形處理能力有所提升,但從總體上看,CPU在圖形渲染方面無法與GPU相匹敵。因此,CPU渲染的質量和效率不能與GPU相比較,主要還是為了教學和理解過程。
為 MakeSphereNormalMap 創建 TreeNormal
首先,正常貼圖生成器已經準備好了。接下來,在加載時,將生成一個正常貼圖供使用。最初使用的是樹的位圖來進行測試。雖然現在并沒有真正的樹的正常貼圖,但假設我們有一個樹的正常貼圖,并將粗糙度設為1,即完全平滑和反射的材質,這樣有助于調試。
接下來,需要為此分配空間。通過調用 MakeEmptyBitmap
來分配空間。需要傳入內存區域、寬度和高度,寬度和高度與樹的位圖的寬高一致。由于接下來會直接寫入數據,因此不需要將其清零。
這些貼圖會被存儲在臨時數組(transient array)中,這與地面緩沖區類似。樹的正常貼圖將被存放在相同的內存區域中,這樣可以確保它們與其他臨時狀態共享內存。
接下來,將生成一個球形正常貼圖,并將其存儲在該內存區域。然后,可以將樹的正常貼圖傳遞到適當的函數中,確保能夠從中進行采樣,下一步需要返回到相關的例程,處理和應用該正常貼圖。
讓游戲運行,查看 NormalMap 的解包并改變其打包方式
在對法線貼圖進行采樣時,首先進行了解包操作。法線的值是從0到255以及0到128的范圍。這種打包方式存在一定問題,尤其是法線的Z值應該是128加127的范圍,而不是直接使用0到128的范圍,因為法線的Z值應該始終是正值。為了一致性,可以采用一種更合理的打包方式,即將法線的Z值加1,再除以0.5并進行縮放處理,這樣就可以確保所有值都在相同的坐標空間內。
目前的做法是只解包了法線,并進行線性插值,但仍未對解包后的值進行適當的縮放處理,因此無法得知這些值的具體含義。為了正確使用法線,需要將其調整到正確的范圍,考慮到正負值的變化,并進一步處理這些值。
黑板:考慮問題的 hacky 特性
在處理3D問題時,尤其是在一個本質上是2D的世界中,常常會遇到一些問題,這些問題通常來源于需要將3D的概念壓縮到2D環境下進行處理。具體來說,法線(Normals)計算是這種問題的一個例子,尤其是在2D渲染中,如何處理法線方向和深度(Z軸)的問題。
在繪制物體時,例如繪制一個球體,可以觀察到法線指向的方向。例如,球體上的法線可能會指向我們,或者指向其他方向(比如上、下、左右等),而且這些法線會隨著物體的遠離而變得越來越小。需要注意的一點是,球體的法線永遠不會指向屏幕后面,也就是說,我們不可能有指向屏幕后方的法線。
具體來說,在這種2D環境中,法線的意義變得難以直觀理解。可以通過把法線的方向與世界坐標系的軸進行比較來幫助理解:
- 法線的
X
軸對應于世界的X
軸。 - 法線的
Y
軸更像是世界的Z
軸,用于描述地面和天空之間的過渡。 - 法線的
Z
軸則描述了物體的“外向性”,表示物體的指向。
這種映射方式的背后邏輯是:在2D中,物體的法線方向無法指向物體的背面,所有可見的法線方向都指向物體的前面。因此,在2D渲染環境中,法線的 X、Y、Z 坐標實際上映射到了一個類似的坐標系統中。在這個系統中,法線的 X
和 Y
軸代表著物體的空間位置,而 Z
軸則決定了物體的相對外向性,即指向屏幕的方向。
通過這種方式,即使在2D世界中處理3D法線的問題,我們也可以通過這種轉換方法將其映射到合適的方向和位置,從而在圖形渲染中正確地表示法線信息。
黑板:地面的法線
在處理地面和物體的法線時,地面法線的情況與其他物體有所不同。地面通常是平坦的,但如果地面有一些起伏(例如不平整的地形),那么地面的法線就不再是單一方向的,而是可能指向半球的各個方向。因此,地面法線的處理相對復雜,它不像其他物體那樣總是指向一個固定方向。
對于這種情況,可以考慮兩種類型的法線貼圖:
- 正面法線貼圖:這類貼圖的法線主要指向物體的前面,通常用于表示像樹等物體的表面。法線的方向是確定的,主要指向物體的外部。
- 上方向法線貼圖:這類貼圖的法線是真正的三維法線,代表物體表面的實際方向。在地面這種場景中,法線方向可能指向不同的方向,以適應地面的起伏和變化。
因此,對于這兩種法線貼圖,可能需要不同的紋理采樣路徑。一種用于處理類似樹木等物體的法線,另一種則用于處理地面等平坦或起伏的物體。根據繪制的物體類型,可以切換使用不同的法線貼圖采樣路徑,具體取決于物體是地面類型還是其他類型的物體。
雖然大多數情況下法線指向物體的外部,但也可以考慮一些特殊情況。例如,在某些游戲場景中,如俯視角的地牢,墻壁的法線可能會指向不同的方向。墻壁的法線不僅指向外部,還可能指向不同的方向。這表明法線不僅僅限于指向物體的外面,某些特殊情況下,物體(如墻壁、地牢等)的法線也可能指向其他方向。因此,在處理這些場景時,可能需要更多的法線信息,尤其是對于像精靈(sprite)等物體,它們的法線有時會指向不同的方向。
綜上所述,法線的方向處理不僅僅依賴于物體表面是否指向一個固定方向,還要根據具體場景的需要來調整法線的處理方式。在不同的紋理類型和物體類型之間,可能需要采用不同的法線貼圖和采樣路徑。
處理前向位圖,首先引入 UnscaleAndBiasNormal
目前,我們只處理正面朝向的法線貼圖,地面部分會稍后處理,可能會使用不同的路徑來處理。假設我們已經將法線貼圖正確地加載進來,接下來需要做的就是處理法線值的縮放。
首先,我們需要從法線貼圖中提取法線,并對這些法線進行縮放。法線的每個分量(X、Y、Z)都被壓縮到了0到255的范圍內,因此首先需要將這些值從0到255的范圍轉換到0到1的范圍。這是通過簡單的除以255來實現的。
接下來,我們需要將0到1的范圍映射到-1到1的范圍。這意味著,我們需要對轉換后的法線分量進行擴展,使其從-1開始,然后通過將值乘以2來覆蓋整個-1到1的范圍。這樣,法線的每個分量就能正確地反映其在三維空間中的方向。
至于法線的W分量,它表示粗糙度,通常已經是一個從0到1的值,因此無需進行任何額外的處理,只需要像處理顏色值一樣進行轉換即可。
最終,經過這些處理后,我們就能夠獲得一個合理的法線值,它已正確縮放并適用于渲染計算。
在游戲中查看,并回顧代碼
在進行法線處理時,當前的操作是將法線的每個分量從[0, 255]的范圍轉換到[0, 1],然后再從-1開始,向正負兩個方向擴展到[-1, 1]范圍。對于法線的處理似乎是正確的,但結果顯示的法線效果不如預期。雖然一半的結果看起來正常,但另一半的法線卻顯得有些奇怪,給人一種不太對勁的感覺。
接下來需要完成的部分是對法線進行插值(lerp),然后將其重新調整到正常范圍。雖然法線從[0, 255]編碼的方式可能會引起一些后續的影響,但這個步驟可能依然是可行的。理論上,先進行插值,再將法線解壓并調整回正常的范圍,應該是可行的。
在此之后,還有一個關于環境采樣的操作,預計是要通過法線來作為顏色進行顯示。然而,這一部分可能并沒有按預期的效果展示。
僅繪制法線并將 Texel.a 強制為 1.0f
首先,打算直接繪制法線,將其作為紋素(Texel)顯示。為了避免顏色閃爍,將紋素的透明度(Texel.a)強制設置為1.0,以確保沒有不必要的視覺效果。然而,顯示的結果卻是完全黑色,說明出現了某種問題。
由于最近在代碼處理上有所不確定,決定先停下來驗證所有的邏輯,確保每一步都按照預期執行。尤其是對于昨天編寫的代碼,雖然今天繼續處理,但仍然缺乏完全的信心,因此需要仔細檢查一下各個環節的工作情況。
步進 NormalMap 代碼
為了驗證從法線貼圖中獲取的數據,打算直接查看樣本值。發現解包后,法線的各個分量(A、B、C、D)都為零,這顯然不合理。正常情況下,法線貼圖的值不應該為零,除非某些特殊的區域,比如邊緣區域。感到這種情況不太可能出現,因此決定進一步調查這個問題。計劃直接查看法線貼圖,以了解為什么會出現這種異常情況。
領悟時刻:我們仍然在從紋理中采樣
發現了第一個問題:昨天的代碼復制粘貼時沒有修改法線貼圖,仍然從錯誤的紋理中進行采樣。這顯然沒有任何幫助,導致了當前的問題。
繼續步進代碼
發現了法線貼圖中的問題,法線 B 和 D 顯示為零,其他法線的值也看起來完全錯誤。例如,1, 1, 0
不是一個有效的法線。于是決定查看法線貼圖的實際內容,查看內存中的值,并比較這些值是否合理。同時,通過查看生成球體位圖的函數,進一步驗證問題的來源。
領悟時刻:通常寫一個像素時,你想移動到下一個像素
對昨天的代碼產生了懷疑,指出在一個像素上寫入多次是沒有意義的。通常,在寫入一個像素后,應該移動到下一個像素。
快速檢查數學
檢查了數學公式,發現之前的計算存在問題。通過正確生成位圖 UV,將 X 除以寬度來獲得正確的值,并使用該值生成適當的 X 和 Y 坐標。
黑板:Nz 應該通過畢達哥拉斯定理解決
通過使用勾股定理來計算球體的 Z 坐標。已知 X 和 Y 坐標,以及球體的半徑為 1,可以使用公式 x 2 + y 2 + z 2 = 1 x^2 + y^2 + z^2 = 1 x2+y2+z2=1 來計算 Z 坐標。通過將 X 和 Y 的平方移到方程的右邊,并對其進行平方根運算,得到 Z 坐標的值: Z = 1 ? x 2 ? y 2 Z = \sqrt{1 - x^2 - y^2} Z=1?x2?y2?。
計算 Nz
通過使用勾股定理計算 Z 坐標,公式為 Z = 1 ? x 2 ? y 2 Z = \sqrt{1 - x^2 - y^2} Z=1?x2?y2?,可以得到球體上的 Z 坐標。
設置法線并查看像素是如何打包的
在處理法線時,首先將其范圍從 -1 到 1 轉換到 0 到 1,然后乘以 255 來進行打包,最后將粗糙度值放入 ALF 位置。完成打包后,進行截斷和位移操作。這樣就能將法線打包到適當的顏色通道中。需要確保解包操作能正確恢復原始順序,以保證法線數據的正確性。
步進代碼
正在檢查計算出來的法線,期望能夠得到實際可用的法線。嘗試查看法線值,但發現值仍然是非常不合理的,如 2.5 或 250。對于這些值感到困惑,認為它們沒有意義。
黑板:處理球體外的區域
面臨的另一個問題是,有時計算結果可能會偏離球面。因為填充整個位圖時,可能會得到一些超出球面范圍的值。為了解決這個問題,可以考慮將這些值處理為合理的法線方向。例如,可以將這些超出范圍的值設定為指向上方的法線(例如,(0, 0, 1)),或者可以選擇將它們設置為球面上最近的點所對應的法線方向。
引入 RootTerm
為了處理球面之外的情況,首先計算根式項,確保在計算時根項不會小于零。如果根項大于等于零,則可以正常計算平方根。如果根項變為負數,則意味著將進入復數范圍,這顯然是不合適的。因此,在這種情況下,可以將其替換為更有用的值。為了簡化處理,當前可以假設不在球面上的點直接指向上方((0, 0, 1)),但是未來可能會考慮采用更復雜的方案來處理這些情況。
檢查法線值
通過處理越界的情況,得到了更合理的結果,值為(0, 0, 1),這正是預期的結果。當進行線性插值時,應該能夠得到合理的結果。接著,通過對法線進行去偏移和縮放,期望得到的結果是(0, 0, 1),實際上也得到了相似的結果。不過,有些地方可能需要進一步檢查,尤其是確保128能夠精確映射到預期的值。
在游戲中查看,發現一些錯誤的負值
遇到的問題是沒有任何合理的值被填充進來,結果總是指向正上方,這顯得很奇怪。原本應該通過計算根式值(1.0減去x和y的平方和)來得到一些結果,但實際情況是,這個計算似乎沒有正確執行。通過檢查,發現即使期望的值為(0, 0),也沒有得到合適的計算結果,可能存在某些環節的問題,導致無法得到正確的值。
查看正確的 NormalMap
現在代碼看起來已經回到一個更合理的空間,雖然有些部分可能還需要進一步調整,但至少不再完全荒謬了,算是取得了一些進展。現在的結果符合預期,雖然還存在一些小問題,但總體上已經接近正確的方向。這是逐步前進的過程。
重新計算 Texel.rgb 使其在有效顏色范圍內
為了繪制效果,可以在渲染過程中調整值的范圍,將其從負一到一的范圍轉換為有效的顏色范圍。通過將值乘以0.5并加上0.5,可以將其調整到有效的顏色范圍內。這可以幫助觀察正常的映射效果,比如隨著橫向移動,紅色的數量增加,縱向移動時綠色增加等。然而,可能存在某些細節上的問題,比如lerp計算不太正確,導致某些地方的效果出現異常。因此,需要更仔細地檢查其中的實現,確保所有的計算都正確。如果關閉正常映射后,效果回歸正常,可能是與正常映射相關的某些計算出現了偏差。
關閉 NormalMap,并考慮使用比樹位圖更明確定義的東西
為了更好地調試問題,考慮用一個非常明確的形狀而不是當前的樹形進行測試。這可以幫助更清晰地顯示出是否存在問題,因為樹形可能不如預期那樣干凈,導致問題不容易察覺。使用一個簡單且明確的形狀進行測試,可能會提供更直觀的反饋,幫助發現并解決潛在的問題。
暫時將 TreeNormal 當作紋理圖繪制
為了調試法線貼圖,考慮將樹的法線直接繪制出來,并將其當作一個紋理來觀察。這樣可以更清楚地了解為什么采樣結果看起來如此奇怪。通過觀察,發現圖像存在sRGB顏色空間的異常,尤其是黃色區域,可能是由于128作為零值導致的。此外,雖然邊緣看起來沒有明顯的畸形,但采樣結果仍然顯得有些不對勁。因此,下一步需要進一步檢查法線貼圖的采樣過程,確保沒有發生任何異常,導致圖像出現帶狀現象。
引入 BilinearSample 函數
為了避免出錯,可以考慮將代碼封裝成一個函數,使其更加簡潔且不易出錯。例如,可以定義一個內聯函數來執行雙線性采樣,該函數接收加載的位圖(即紋理)以及采樣的 x 和 y 坐標,完成采樣后返回一個結構體,其中包含四個采樣結果。
接下來,將所有相關的法線貼圖采樣操作替換為此封裝好的雙線性采樣函數,使得代碼更加簡潔和易于管理。通過這種方式,可以更輕松地進行法線貼圖的處理和調整,同時保持代碼的整潔性。
區分一下幾個單詞 Linear 線性 Nearest 是臨近
在插值方法中,“臨近” 和 “雙線性” 通常是用來描述不同的插值算法。以下是這些詞語的英文翻譯以及簡要說明:
-
臨近插值 - Nearest Neighbor Interpolation
這種方法選擇離目標點最近的像素值,簡單快速,但效果較粗糙。 -
雙線性插值 - Bilinear Interpolation
該方法使用目標點周圍四個像素點的加權平均來計算目標點的值。相比于臨近插值,雙線性插值生成的結果更加平滑。 -
單線性插值 - Linear Interpolation
這種插值方法通常用于一維數據中,通過兩個相鄰數據點的線性插值來估算目標點的值。在二維空間中,可以將其視為沿著兩個軸進行線性插值,常用于圖像的一維變化。
在游戲中查看,并切換到法線圖調節紋理的路徑
目前的狀態看起來相對合理,雖然仍然有些不對勁,可能存在輕微的剪切問題,需要進一步檢查。總體上,當切換回正常路徑時,可以看到正常映射的顏色與樹的紋理相互作用,表明兩者的采樣已經相對正確。現在可以開始進一步進行操作,繼續朝著目標推進。
用 NormalMap 照亮紋理
當前的問題是如何處理環境映射。需要傳入環境映射以進行采樣,并決定如何調節這些映射。可以考慮將它們直接相加,這可能是一個合理的選擇,但也需要確保不會發生溢出,因此需要對結果進行限制,確保所有值都在0到1之間。此外,還存在一個問題,即光照顏色是預乘 alpha 的,因此從光照顏色中獲取的值需要乘以紋理的 alpha 值,因為紋理的 alpha 值是我們預期的值,這也是需要額外處理的部分。
在游戲中查看
當前的情況是,雖然已經使用法線貼圖進行照明,但法線貼圖本身并沒有包含任何光照值,實際上只使用了法線貼圖中的值來進行照明,這樣的效果非常有限。因此,下一步的目標是從某個來源獲取光照值,以便真正實現照明效果。
處理 environment_map 的內容
在處理法線貼圖時,最初的代碼中使用了錯誤的法線軸(Z軸),實際上應該使用法線的Y軸來確定方向,因為Y軸值決定了位置的高低。法線的Y值范圍從-1到1,因此,之前的測試方法不太適用,應該調整為更合理的范圍測試。根據法線Y值的大小,決定使用哪個地圖:如果Y值小于-0.5,則使用底部地圖,如果大于0.5,則使用頂部地圖。
為了混合頂部和底部地圖的光照值,需要根據法線Y值計算權重。如果法線Y值小于-0.5(即最小值),則混合底部地圖的光照,如果法線Y值接近0.5,則混合頂部地圖的光照。計算時,首先將法線Y值從-1范圍內的數值映射到0到0.5的區間,再進行適當的處理以得到正確的結果。
接下來,在選擇了頂部或底部地圖后,需要進行采樣,獲取相應的光照值。對于采樣過程,傳遞法線的X、Y、Z值有些不清楚是否完全符合要求,因此需要進一步思考這些值在采樣中的具體意義,并決定是否需要調整。
黑板:從環境映射中選擇
在處理這個問題時,首先需要理解的是,當前場景中的點和法線信息可以視為類似于重心坐標。通過屏幕空間中的UV坐標,可以確定環境貼圖中對應的位置。UV坐標告訴我們在環境貼圖中的相對位置,例如,某個點可能對應UV值(0.2, 0.8)。此外,法線會指示觀察方向,幫助確定視角和環境貼圖之間的交點。
接下來,需要計算的是如何根據法線與環境貼圖的關系來確定需要采樣的貼圖位置。假設法線與Z軸的偏離角度不超過45度(即0.5),這樣可以簡化計算過程。這個角度范圍可以視作一個錐形區域,向上和向下各有一個錐形視野。我們需要確定每個法線值對應的環境貼圖上的像素。
在面對水平區域(即正中央平面)時,計算變得更加復雜,因為我們沒有明確的標準來處理這一部分。為此,需要更細致地思考如何處理這個區域,尤其是在定義環境類型時,可能會有不同的處理方式。總的來說,目標是根據法線的方向,從環境貼圖中選出合適的像素進行采樣。
使中間采樣為空,并讓 SampleEnvironmentMap 生成我們可以使用的值
在處理環境貼圖的采樣時,首先需要解決的一個問題是如何去除粗糙度的影響。通過去除粗糙度的計算,可以簡化處理過程,將其暫時從方程中移除。這樣,粗糙度值不會直接影響我們選擇的 LOD(細節層次)地圖。接下來,需要將粗糙度量化到一個合適的桶中,以決定選擇哪個 LOD 索引。
通過將粗糙度值乘以數組的計數(即細節層次的數量),我們可以得到一個在范圍內的整數,這個整數表示粗糙度的層次。然后,這個索引值將用于選擇對應的 LOD 圖像。接下來,使用雙線性插值采樣從相應的位圖中獲取顏色值。
在完成粗糙度的計算和位圖選擇后,進一步的處理是進行雙線性插值的計算。通過獲得的紋理坐標 (FX, FY),可以進行 sRGB 雙線性插值,得到需要的顏色樣本。
當完成這些步驟后,接下來需要處理的是如何在環境貼圖中進行交點計算。通過法線的方向與環境貼圖中的對應關系,可以計算出正確的采樣位置,獲取合適的顏色樣本。然而,這一部分的交點數學計算尚未完全完成,預期在下次工作時繼續進行。
為了避免錯誤和不一致,程序會檢查環境貼圖是否有效,確保不會因為無效的貼圖而導致程序崩潰。在確認有效的環境貼圖后,程序將從指定的 LOD 中獲取相應的顏色值,并繼續后續的處理。
展望接下來的內容
目前,已經準備好繼續進行的步驟包括生成環境貼圖和處理交點數學計算。交點計算是為了確定應該在哪里進行采樣,這一步驟非常關鍵,需要計算出正確的采樣位置。完成這些后,還需要確保地面和天空等元素能夠參與到采樣中,這意味著需要進一步處理與這些元素相關的內容。
回顧今天的清理工作
今天主要是進行了一些清理工作,修正了昨天輸入的錯誤。還進行了代碼的簡化,將一些代碼抽取成函數,這樣可以避免因拼寫錯誤而帶來的麻煩。在開發的初期,抽象出可以正常工作的函數是非常有幫助的,雖然在優化階段可能需要將這些函數展開,但在確保功能正確的過程中,這種做法可以減少錯誤的發生。
當你插值兩個法線時,你可能得不到一個標準化的向量。你對這個問題有什么看法?請簡要評論一下。
在討論關于法線的插值時,提到了一些可能會導致非標準化向量的問題。首先,插值(lerp)時可能會得到一個不正常的法線,這就是為什么需要特別注意插值過程。雖然現在的插值方法可能產生不標準化的法線,但在做交集計算時,可能可以通過在除法步驟中進行標準化來解決這個問題,這樣可以避免手動標準化向量。不過,如果在實際計算中需要一個真正標準化的法線,就會遇到問題。
目前,代碼中的法線選擇處理不完全正確,因為插值時使用了法線的Y分量,導致插值權重不準確。為了確保正確性,雖然可以選擇在計算過程中始終對輸入的法線進行標準化,這樣可以確保正確性,尤其是在硬件上,特別是桌面PC上,通常會有快速的逆平方根運算,避免存儲Z分量,而是通過計算X和Y來得到Z值。因此,可以在著色器中直接計算Z分量,而不需要在CPU端進行這一操作,這也是一個高效的做法。
問:我們如何在計算每個精靈的像素值時進行光源采樣?會有一個所有光源的列表嗎?還是這會是某種參數?
在計算每個像素的亮度值時,光源的采樣方法是通過環境貼圖(environment maps)來實現的。所有的光源將會被寫入到環境貼圖中,然后通過從貼圖中提取來進行采樣。因此,不需要維護一個光源列表。只需要一個紋理,就能夠處理任意數量的光源。無論光源的數量有多少,這種方法的開銷是固定的,不會因為光源數量的增加而增加額外的成本。這是一種簡化處理的方式,可以高效地處理多個光源。
會為藝術制作法線圖嗎?
目前計劃是將法線貼圖(normal maps)包含在藝術素材集中,但具體如何生成這些貼圖還不確定。可能會使用離線處理的方式生成法線貼圖,而不是由藝術家直接制作。因此,雖然法線貼圖會作為藝術集的一部分,但它們的創建過程尚未確定。
你會講解更高級的 BRDF 嗎?還是只會講像 Phong 這樣的簡單光照方程?
目前的照明模型尚未采用BRDF(雙向反射分布函數),因為在當前的簡化照明模型中,使用BRDF并不會帶來顯著的效果。BRDF通常需要復雜的照明環境才能看到其效果,而目前的模型較為簡單,尚未考慮視角角度的影響。雖然可以通過簡單的線性組合來進行照明計算,但目前的做法并不包括視角衰減或光照反彈等計算。
盡管如此,如果未來需要,可以考慮在現有模型中加入視角依賴的光照衰減計算,但目前這些功能不在優先考慮之內。此外,由于整體模型仍然是為適應2D方案進行簡化的,加入更復雜的計算可能會造成效率上的浪費,因此目前并不計劃進一步增加這些計算。
如果我不想讓我的游戲看起來“很普通”?有沒有其他類型的圖可以讓我游戲更獨特?
可以使用一種叫做“異常法線貼圖”(Abnormal Maps)來讓游戲的外觀更獨特。異常法線貼圖的概念來源于將兩個不同的紋理(A和B)進行操作,通常通過加法、乘法或卷積等方式進行組合。這種方法會破壞原本法線貼圖中的計算,從而產生全新的效果。
例如,可以將兩個法線貼圖進行乘法操作,生成一個全新的異常法線貼圖。通過這種方式,雖然會失去原有的法線計算精度,但可以得到一個獨特且不同的視覺效果,從而使游戲的畫面顯得更加與眾不同。不過,這樣的效果往往會顯得不太自然,因此使用時要小心平衡。
問:有些游戲會使用“凹凸貼圖”作為法線圖的補充或替代。它們之間有什么區別?各自的優缺點是什么?
現代游戲中,通常不再使用凹凸貼圖(Bump Map)替代法線貼圖(Normal Map)進行光照計算。凹凸貼圖的作用通常是為了壓縮法線貼圖的大小,或者通過凹凸貼圖中的值來近似法線,從而減少法線貼圖的存儲需求。
另外,凹凸貼圖還可以用來實現其他效果,例如位移或遮蔽效果,而不僅僅是光照計算。這些效果可能會用來增強視覺表現,但相比法線貼圖,凹凸貼圖在真實光照的模擬上并不如法線貼圖準確和高效。因此,凹凸貼圖在現代游戲中更多地用于非光照效果的實現。
黑板:凹凸貼圖與法線貼圖
凹凸貼圖(Bump Map)和法線貼圖(Normal Map)之間有著明顯的區別。凹凸貼圖主要存儲的是表面點的高度值,即每個像素的高度信息,而法線貼圖則存儲每個表面點的法向量,即表面在該點的方向。法線貼圖提供了更精確的信息,因為它直接給出了表面的法線方向,能夠更好地用于光照計算。
凹凸貼圖通過高度差來計算法線,通常無法準確表達表面細節,特別是硬邊等特征,因為它只存儲高度變化信息,不能明確表示表面的方向。相比之下,法線貼圖能夠直接表達表面法線,可以捕捉到更多的細節和更精確的光照效果。
法線貼圖的存儲效率比凹凸貼圖高,因為它直接給出法線的方向,不需要通過計算周圍像素的差異來推測。而凹凸貼圖通常用于需要節省存儲空間的情況,或者在某些情況下,凹凸貼圖可以用來生成其他效果,比如遮蔽效應。
在技術上,法線貼圖實際上可以被視為凹凸貼圖的導數,或者更準確地說,是凹凸貼圖的梯度,即凹凸貼圖的高度變化方向。總之,法線貼圖比凹凸貼圖更為強大和直觀,可以更好地表達復雜的表面形態和光照效果。
問:這個效果最終會像凹凸貼圖的效果嗎?
法線貼圖(Normal Map)實際上是凹凸貼圖(Bump Map)的更強大版本。雖然凹凸貼圖可以產生類似的效果,但它的表現遠不如法線貼圖精確。簡單來說,凹凸貼圖可以看作是法線貼圖的簡化版。
我們能否通過在繪制位圖時使用幀緩沖區作為環境映射來實現反射?
通過將幀緩沖區作為環境貼圖來實現反射是目前的計劃。這樣,物體的反射效果,比如地面上的反射,就可以通過這種方式實現。雖然還不確定最終效果如何,但這是目前的嘗試方向。
如果你把表面看作一個隱式函數 f(x,y,z) = 0,那么它就是該函數的梯度。它與表面切線的叉積相同。
在討論時,假設表面是一個隱式函數,實際上是在談論該函數的梯度。與顯式的高度圖(如 bump map)不同,隱式函數會在每個位置都有一個值,然后尋找等高線。這時的確會涉及到梯度。然而,bump map 并非隱式函數,它是顯式的,因此不能直接通過梯度來處理。為了從 bump map 中得到法線,必須通過計算切線方向,并將它們的叉積作為法線。具體來說,在任何點上,我們有兩個切線方向,分別是 U 和 V 方向,然后將它們的叉積得到法線。雖然這類似于梯度,但由于 bump map 是顯式的,不能直接像梯度那樣操作,因此需要其他方法來處理。
我們已經結束了所有問題
總結來說,討論結束后,計劃繼續進行接下來的步驟。主要目標是下周開始構建照明采樣緩沖區,并使用正確傳遞的法線貼圖來進行采樣。盡管這仍然是一個實驗性的過程,因為具體的實現方法還不明確,但會繼續努力并盡力實現所需的效果。雖然精確的光照模擬非常復雜,通常需要進行全光照傳輸計算,但由于時間限制,通常只能進行近似的光照計算,且這種近似方法在3D環境中也屬于常見的做法。接下來幾周將繼續調整這些近似值,直到找到一個能夠作為渲染設置基礎的方案。