圖形渲染方面說,生死狙擊2項目已經度過了3年開發期。開始進入高畫質提升階段,所以再一次優化植被的算法。主要是unity terrain的草不支持normal貼圖,smoothness貼圖,specular參數,厚度貼圖(做透光性效果)和AO貼圖等,也不支持陰影。所以這次提升主要是實現這些,但大部分都是pbr常規算法復用就不寫了。不同於引擎standard內算法的地方是2處,透光性效果和特殊草陰影(陰影后面會單獨寫一篇)。因為並不滿足於 一個簡單的 網絡流傳很久的 dot(normal,-lightDir)來實現效果。所以準備較為全面細緻的推導這個算法。友情提示:雖然是圖形學分享但本文帶有凡爾賽文學創作元素請小心品嘗。
理論基礎關於 透射與次表面散射的關係很多人可能含糊不清。先看一張常見的美術想要的SSS效果。相信很多TA在與美術的需求交流中,會分不清他們描述的SSS到底是什麼,是次表面散射?是透射效果(透光而不透明)?其實他們又不是小孩子當然是都要啊 只是沒空去細分而已。那麼如何區分他們呢?目前為止我並未查到言之鑿鑿的清晰分界定義。只能自己根據一些獨立資料嘗試自己的自圓其說,其中很多資源也彼此矛盾,所以不能確保選取組合是完全正確的。先看3個概念。我不翻譯縮寫的全稱和定義,而是用直白的話描述。
BRDF 描述入射表面孤立的反射的情況,這也是unity提供的最常用最基礎模型。不研究折射,物體內部光線傳播等。Standard shader基於此。
BSSRDF 和BRDF 類似但不再孤立的考慮入射表面本身,還會考慮從附近位置反射的情況。但渲染過程是相反的,所以要理解成渲染的表面計算了附近表面入射光 也從我當前渲染表面反射的情況。
BTDF R代表反射所以都不考慮深入的進入物體內部並穿透的情況,所以需要T來描述。在12模型中認為,光線只要沒反射就進入內部被吸收。普通的透明玻璃處理 也是按BRDF計算計算的 只是最終渲染結束後像素混合的時候用了百分比顏色疊加,並非渲染過程的光線考慮。
或許你會看到很多權威文章。配合下面第二張圖。告訴你 BSDF=BRDF+BTDF。那麼 我們關注的次表面 BSSRDF呢?被拋棄了嗎?其實BSSRDF屬於BRDF,只是更通用的解。BRDF就是BSSRDF散射距離為0的特殊情況。因為更大維度上 光線只分反射與折射。
結論
次表面散射 描述的是陰影過渡處,並不生硬。而不是遠離過渡處看到全部區域,常見的耳朵與手掌中間就不是次表面散射的效果了。屬於透射部分的光,發生在任何觀察角度。
透光性 描述的是薄物體透射,描述大量非邊緣區域,只發生在光源在背後的情況。而植被的「SSS」渲染 個人感覺應該重點透光性 畢竟區域大效果明顯 性能高。
光源在背面光源在正面算法分析我當時拿起一片樹葉在公司樓下站了1小時,關注了2點。
透到背面的光線 是各方向均勻的 還是類似高光以入射光角度為主的傳播?決定算法是否與dot(normal,viewDir)有關
入射光線與表面法線夾角 對於穿透量是什麼一個影響。
對於1 我不太確定需要測量,對於2 我是大體有概念的,垂直照射肯定是透光最多的 。因為垂直通過的距離更短,更重要的一個方面還是 菲涅爾效應。但這也是在學習他之前就可以發現的。坦克斜面裝甲防止高速子彈的穿透,甚至小學之前丟旋轉高速的石子 打水漂。都能發現越接近物體表面越難穿透,這些知識具有普遍性。但具體的關係如何,我還不知道。我會選一種自己喜歡的方式去探索他。一般不是直接查表,而是假模假樣的做點實物測試。我學所有技術的動力與樂趣 都是更好的與真實物理世界對話,而非與他人 或人類知識本身交互。
正式測量首先準備好測量4件套,角度圖紙,大樹葉,光照度測量儀,手電筒。由於家境貧寒 我沒找到手電筒,用的是 公司今年發的陽光普照獎IPhone12 上的手電筒功能。將就著測吧。話到如此了,順便拉下小夥伴加入我們吧 TA 圖形 程序各崗位都需要,因為項目開發的不錯拿到了騰訊n個億的投資,會開多個高品質項目。坐標杭州。
然後是想辦法把樹葉固定在 紙張量角器的圓心位置。同樣因為家境貧寒,我不可能找到膠帶等現代工業產物的。只好拿出一本自己看透而不再需要的嬰幼兒時期的讀物來固定。如下即可
測試結果 需要關燈 和選擇毛絨環境減少環境反射所以不能拍了 直接寫下測試結果
1 樹葉的透射光線是均勻的與觀察角度無關,也沒有在入射光線上保持更大通光量權重。
2入射角度與樹葉法線夾角分別為(90/平行樹葉 ,60,30,0/垂直)時,樹葉背後光照度分別為0lux 4lux 12lux 15lux,光本身強度為600lux。
有這四個數據,作為老碼農隨便寫4,5行代碼實現擬合工具.我這裡沒有根據泰勒的展開去湊各階導數,也沒根據傅立葉展開去拼n個sin與cos,雖然都可行。但因為我很早就知道是類似菲涅爾現象,而菲涅爾可以可以用 pow(cos(x*f),p)k,來做,k縮放和f頻率是我單位不一致等的考慮。於是很快就得到了 公式。這裡的F=56.9 可能你覺得很奇怪,其實我是用角度做參數,轉成弧度後56.9*3.14159/180=0.993 拋開誤差應該就是1 和菲涅爾公式的單位周期一致。
工程實現與效果在測量之前我就寫了版經驗公式的效果 先看下
先看 素材的默認BRDF照明(左邊) 和 直接dot(normal,-lightdir)效果 (右邊 2種不同強度)
這種沒考慮陰影的透光效果是很差的,所以前面所有理論 都沒考慮陰影所以真實落地其實需要考慮的更多。陰影的實現forward渲染簡單很多 gi.light.color可直接拿到到陰影結果的光照。deferred需要改 buildin那個Internal-DeferredShading.shader ,還需要往gbuffer加matID,我們是後者麻煩些,但考慮大部分項目都是手遊項目都是forward。所以就只說forward實現了。加上陰影后 這樣了,好看不少。
但是會發現,透光性的物體不僅僅會透背面的光,也會透四周的環境光,所以他不會像實體那麼黑,也就是美術說的通透感,於是又加上陰影減弱。效果就更好了。有點漂亮有沒有哈
但是 只考慮平行光是不夠的,現實中樹葉背後(相對觀察者)各方向的光 都可以造成透光效果。所以正確的做法應該是實現基於IBL的透光計算。但是這個性能就不太好了,所以退一步,用lightprobe的 低頻漫反射描述各方向光強度+平行光實現。以下2張。
只計算平行光透光性的 正面無透光效果用lightprobe 考慮各方向的光+平行光計算 正面也有效果用上測量公式
測量結果中主要是獲得power的係數 為1.77
以下為算法源碼,(gi.light.color+0.8)/1.8 是利用環繞照明減弱陰影。關於環繞照明多說一句是TA非常常用的小技巧和非常常用的數學思維。格式為 (A+x)/(1+x) .比如 x=0.2情況下當A為0和1時,分別為0.2和1,既不溢出1 又最低值防止0的黑色出現。又比max(0)好的保持了整體過渡而非裁剪。這種思想常常是很多人本身就具備的,記得我最初學圖形接觸 HalfLambert模擬早期GI時 見到floathLambert = difLight * 0.5 + 0.5; 我提成一個思考,為什麼必須是0.5呢,可否是個x 係數 ,變成A*x+(1-x)呢。進一步推導下 令x=1/(1+y) 得到 A/(1+y)+(1-1/(1+y))=A/(1+y)+y/(1+y)=(A+y)/(1+y) ,是不是很熟悉?就是一開始那個 (A+x)/(1+x) 環繞照明。所以說很多數學思路是本身具備不需要全部通過學習的,見到只是重逢自然比重新認識更快理解。但當時能理解這些關係的人不多 我這種提法還被 嘲諷為HaHaLambert(脆弱的小心肝差點放棄圖形哈哈)
//穿透疊加色 後面解釋
float3 addColor =half3(0.15, 0.15, 0.0);
#if 0
//經驗公式
c.rgb += (gi.light.color+0.8)/1.8 *(max(dot(s.Normal, -gi.light.dir),0)) * (s.Albedo + addColor) *pow(dot(s.Normal, viewDir) * 0.5 + 0.5, 1.2);
c.rgb += ShadeSHPerPixel(-s.Normal, 0, 0) * (s.Albedo + addColor) * (saturate( Luminance(gi.light.color) + 0.8) / 1.8;
#else
//測量公式
c.rgb += (gi.light.color+ 0.8) / 1.8 * pow(max(dot(s.Normal, -gi.light.dir), 0), 1.77) *(s.Albedo + addColor);
c.rgb += ShadeSHPerPixel(-s.Normal, 0, 0) * (s.Albedo + addColor) * (saturate(Luminance(gi.light.color)) + 0.8) / 1.8;
#endif
return c;
這裡解釋下 ,穿透疊加色 。就是透光後的顏色應該是什麼顏色呢?獨立設置一個顏色 當然最好,但為了省圖,一般配置個顏色。但是這樣純色又不符合真實,真實情況背面一般比正面淺。所以一般做個通用寫法就是 s.Albedo + addColor 。addColor 一般為0.1到0.2.但是白色會發灰,所以這裡指定half3(0.15,0.15,0.0);其中綠色 是因為背面一般更嫩綠。紅色有而藍色沒有,是因為我們知道光線穿越非真空區域時,波長最長的紅色容易保持原來路線容易繞過障礙。藍色波長短最容易被散射。比如早晚陽光穿大氣層藍色就被散射形成藍色的天空,紅色到達眼睛看起來比較紅。但這裡是我 自圓其說的 主要是這樣調了好看。
最終效果對比從上至下分別是 沒有透射,經驗公式透射,測量數據透射
可以看到 葉面與視線平行時才有細微差別,ps裡對齊切換會明顯些。說明經驗公式真的很有經驗哈哈。因為提前用大幅度的0.5做了half保護。
聲明:發布此文是出於傳遞更多知識以供交流學習之目的。若有來源標註錯誤或侵犯了您的合法權益,請作者持權屬證明與我們聯繫,我們將及時更正、刪除,謝謝。
作者:jackie
原文:https://zhuanlan.zhihu.com/p/348259514
More:【微信公眾號】 u3dnotes