[程式] 超新手 shader language 教學文 (八)

看板GameDesign (遊戲設計)作者 (meowyih)時間2年前 (2022/06/20 12:50), 2年前編輯推噓4(400)
留言4則, 4人參與, 2年前最新討論串1/1
這篇是 Ray Marching 相關系列的最後一篇, 接著看緣份吧,因為越寫越不『超』新手了。 今天我為這篇文章寫的 shader 在這: https://youtu.be/P4wCIKMwYlE
沒錯,有顏色了,這次的主題就是光, 與如何讓物件疊加的方法。 (畫面裏面有『四個』球) 其實要我寫『光』的教學文,我感覺超心虛的。 我微積分學的不好,很多東西我只會抄, 一堆相關公式文章wiki根本看不懂在說三隻小貓的。 別說我自己寫了, 我還希望有哪個大師寫給我看勒。Orz 所以... 這篇真的是新手文囉。 [基本知識] 這篇『不會』說到像是用 Game Engine 或是 Blender, 先做幾個多邊形,然後擺個光源調個強度, 再改一改材質就做會出來的效果。 這篇是新手文,不是大學電腦繪圖課。:> 這篇會講的是體積光 (Volumetric Light)。 體積光是甚麼?體積光也有人叫他 God Light,長這樣: https://i.ebayimg.com/images/g/mggAAOSwShpZc31P/s-l500.jpg
早期的電腦繪圖,光影效果只出現在實際存在的鋼體上。 像是實心的球表面有光暗反射散射等等的效果, 影子也只會出現在實體的地面上, 那些也就是我上面說的大學電腦繪圖課會教的東西。 但體積光,就像上面那個天使要降臨前的光束, 是在空無一物的空氣中出現的。 這種東西就像讓光有體積,或是把光當作物體一樣看待, 所以就叫體積光。 我們之前二個 shader, 不都有假定世界任何一個點都有密度嗎? 只要有密度,就能被光照到, 而且密度不是 1,光就能透過去, 並照到下一個點 (ray marching)。 這概念其實就是體積光。 再換個說法,現實中看的到體積光,是因為空氣中有粉塵, 粉塵多寡就代表了該點為中心的某個範圍的密度, 所以是很符合物理現實的。 除了體積光,還需要知道一些光的基本常識: 1. 反射 (Reflection) 就國中物理的入射光反射光那些東西, 這篇 shader 沒用到,再用 GPU 都要燒起來了。 2 吸收 (Absortion) 光射到一個物體,某些波長會被吸收變成熱量之類的, 沒被吸收然後反射的部分就是顏色。 3. 透射 (Transmittance) 光可以穿過透明物體,不過會有耗損 (被反射或被吸收了) 所以,Reflection + Absortion + Transmittance = 1.0 透射就是等等程式裡的密度, 程式中的光的顏色則是代表了吸收的概念。 大家國中物理一定都學得很好, 所以我們開始看程式吧!(笑) [程式] 程式在這: https://www.shadertoy.com/view/fdycD3 基本邏輯都是繼承前二篇的,所以看不懂可以先翻前面的看。 [世界構成] func 跟之前長的一樣 void world( vec3 pos, out distance, out density ) 這次世界有四個球,ball_1 ~ ball_4。 雖然前一個範例也有 sky/river 二個物體, 但二者沒有任何交集,顏色只是加起來而已。 這是世界的四個球是有重疊的, 這種情況,密度和最短距離要用正確的方法算。 最短距離: 先分別算 pos 對四顆球的最短距離, 然後在四個數字中取最小的。 min( min( min( dis1, dis2 ), dis3 ), dis4 ); 密度: 二種算法, 一種是四個球的密度取最大值。 一種是把四個值加起來。 沒有好壞,是疊加處的感覺不同。 我的感覺是透明度越低的,越適合最大值,反之是疊加值。 二種在程式中都有寫,可以試試。 [用 ray marching 算顏色] raymarch() 現在不是回傳該方向的總密度, 而是直接回傳該方向的總顏色了。 vec4 raymarch( in vec3 ro, in vec3 rd ) 要怎麼算某個方向的總顏色呢? 總顏色跟總密度一樣, 是 raymarch 的路徑上所有的採樣點的顏色的總和, 密度高的點顏色佔的比例高,密度低的比例低, 比例的高底是放在 color.a 的透明度裡面。 計算方法跟總密度的算法幾乎一樣。 color.a *= 0.4; // 跟範例 (六) 把密度調低在加的意思一樣 color.rgb *= color.a; // 照透明度調淡顏色 color_sum += color * (1.0 - color_sum.a); 那要怎麼算出各別的採樣點的顏色呢? 假設採樣點位置是 pos, 首先,我們有個光源位置: LIGHT_POS = vec3( -2.1, -2.6, 1.2 ), 接著我們可以算出採樣點到光源的方向向量。 然後從採樣點出發, 往光源方向 ray marching 前進某距離, 再算出 pos 到光源的該距離的總密度 t。 t 代表 Transmittance (透射率), 密度高的路線,光比較不容易射到 pos, 密度低的路線,光比較容易射到 pos, 若 t = 0,光線可以沒有任何耗損直接射到 pos。 也就是說, t 越高,光的影響越低, t 越低,光的影響越高。 假設光的顏色是 light_color, 我們就依照透射率比例,加光的顏色加在原本該點的顏色上, 如此就是下面 code 的意思: t = lightmarch( pos, LIGHT_POS ); vec3 light_color = vec3(1.0,0.6,0.3); vec3 ambient_color = vec3(0.91,0.98,1.05); color.rgb = color.rgb * (light_color*(1.0-t) + ambient_color); 那... ambient color 是甚麼? 如果有用 Blender 或任何的遊戲引擎做過 3D 遊戲都應該用過, 這是 "如果沒有任何光源時,物體的基本自體發光"。 你可以把 ambient_color = vec3(0.0), 然後就會發現光照不到的地方都變全黑了。 在做 3D 遊戲時, 有 ambient color 其實不太好, 因為不好控制光源效果。 比較好的做法是多放幾個光源, 像 direction light 之類的, 但這只是 shader 範例,所以別在意這個了。 [最佳化] 我們一直都沒討論最佳化的問題, 但當你在做光影時,最佳化的問題是避免不了的。 像是我這個範例,在瀏覽器的 WebGL 跑, 若你拿幾千塊的手機或是平板, fps 我猜就 10 上下吧。XD 那要怎麼辦呢? 首先,我們真的需要對畫面上所有的方向做 ray marching 嗎? 像前一個例子,做天空時,畫面下半部的點可以通通不做 ray march, 做河流時,畫面上半部也是一樣,馬上就可以減少一半的計算。 而以這個四球例子,一般要先算 ro/rd 是否有和任何物體相交, 有相交再做 ray marching。 甚麼? 怎麼知道一條 ro/rd 的光線有沒有和四顆球相交? wiki 公式和證明: https://bit.ly/39AgCMz 看不懂在寫三隻小貓?顆顆,寫成 code 是這樣: // i remember its from Duke in shadertoy, // but i can't find the origin post, sorry bool inter(v3 ro,v3 rd,v3 pos,f radius,v3 pt) { v3 pos2ro = (ro - pos); float b = dot(pos2ro, rd); float c = dot(pos2ro, pos2ro) - radius*radius; float d = b*b - c; pt = ro + rd*(-b - sqrt(d));//第一個交點 return d >= 0.0; } 不是我寫的,給你參考。 還有,光源的 ray marching 採樣 可是直接讓計算次數以倍數成長的, 到底採樣幾次,才會達到你預期的效果? 還是只採樣最遠的那個採樣點, 然後照比例算透射率就好? 然後,我們真的需要這麼逼真的表面嗎? 不用的話,減少 ray marching 採樣次數, 然後調整 noise 參數找到可接受的就好, 像前一個大河的例子, 從 40 次降到 20 次其實也還行。 最後,如果這不是遊戲的主要重點, 我們是不是該手動降低 fps, 把資源留給遊戲前景呢? 作法只是不用每次都在 main 裏叫 ray marching, 加個 timer 減少畫面的更新。 [結語] 連寫了三篇 ray marching,算是把基本的都講完了。 靠這三篇的基礎,也可以在遊戲中做出很炫的東西了。 美麗的夜空、磅礡的大雨、 深海的景象、宇宙、爆炸、通通都做得出來。 但做得出來跟做的漂不漂亮是二回事就是了, 美感是很吃天份的 (我就沒有), 請多看看其他神人的作品吧。 -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 61.228.104.110 (臺灣) ※ 文章網址: https://www.ptt.cc/bbs/GameDesign/M.1655700635.A.0DF.html ※ 編輯: meowyih (61.228.104.110 臺灣), 06/20/2022 13:37:10

06/20 19:15, 2年前 , 1F
感謝分享,收藏ing
06/20 19:15, 1F

06/21 09:36, 2年前 , 2F
推推
06/21 09:36, 2F

06/21 12:46, 2年前 , 3F
06/21 12:46, 3F

06/21 22:59, 2年前 , 4F
推分享
06/21 22:59, 4F
文章代碼(AID): #1Yh_oR3V (GameDesign)
文章代碼(AID): #1Yh_oR3V (GameDesign)