現(xiàn)指南)
這問題隔三差五就會(huì)在群里看到一次Unity PBR的渲染方程是啥而且每次都會(huì)有人順手把PBR打成“策略路由”我一開始也會(huì)懵畢竟網(wǎng)絡(luò)那邊確實(shí)也有個(gè)PBR。先把概念拉直——Unity社區(qū)里說的PBR幾乎都是基于物理渲染Physically Based Rendering跟“PBR策略路由”完全兩碼事。你既然搜的是渲染方程說明你是想從底層搞明白Unity里的材質(zhì)為什么長這樣為什么金屬幾乎沒有漫反射為什么粗糙度調(diào)低后高光銳利得像刀為什么同一套材質(zhì)在完全不同光照環(huán)境下的一致性遠(yuǎn)好過BlinnPhong。這篇文章我打算從渲染方程本身拆到Unity Shader里的實(shí)際實(shí)現(xiàn)再把我在真實(shí)項(xiàng)目里調(diào)PBR材質(zhì)踩過的坑一并整理出來。適合人群很明確已經(jīng)會(huì)用Unity基礎(chǔ)材質(zhì)和Shader但看到Standard或Lit Shader里的GGXTerm、SmithJointGGXTerm、FresnelTerm這些名詞一頭霧水的同學(xué)或者做卡通渲染做膩了想回頭理解“物理正確”到底是怎么回事的同學(xué)。對美術(shù)同學(xué)也友好我不會(huì)把公式堆到讓你睡著每個(gè)符號(hào)都會(huì)翻譯成人話。1. 先理清一件事Unity里的PBR到底指什么1.1 PBR是策略路由還是物理渲染我?guī)缀醺铱隙ㄋ选癠nity PBR的渲染方程”的人里至少有一半最開始是被“PBR”這個(gè)詞帶偏的。PBR在計(jì)算機(jī)網(wǎng)絡(luò)領(lǐng)域確實(shí)是Policy-Based Routing的縮寫叫策略路由主要用來處理包轉(zhuǎn)發(fā)路徑選擇問題。你如果在搜索引擎里看到“PBR策略路由”“穩(wěn)定”“分流”這類詞基本是網(wǎng)絡(luò)方向的內(nèi)容和Unity沒有任何關(guān)系。但在Unity、UE、Blender、Substance這類工具的用戶群體里PBR就一個(gè)意思基于物理的渲染。它是一套材質(zhì)描述規(guī)范加一套光照模型核心讓材質(zhì)在不同環(huán)境下表現(xiàn)一致。你在Asset Store里看到“PBR Master”“PBR Textures”在Shader里看到SurfaceOutputStandard全都是這個(gè)意思。所以第一步就是別再把兩個(gè)PBR混在一起搜否則你會(huì)發(fā)現(xiàn)自己在Unity社區(qū)找了一晚上“路由策略怎么配”。1.2 PBR的真實(shí)訴求能量守恒與微表面理論P(yáng)BR不是一個(gè)具體Shader而是一套理論框架。傳統(tǒng)光照模型里高光強(qiáng)度經(jīng)常是美術(shù)或TA拍腦袋調(diào)出來的換個(gè)燈光角度材質(zhì)表現(xiàn)就可能崩掉同一種材質(zhì)在暗環(huán)境下像塑料在亮環(huán)境下像金屬缺乏一致性。PBR用兩個(gè)底層法則約束所有材質(zhì)讓結(jié)果盡量接近物理世界。第一個(gè)法則是能量守恒出射光的總能量不能超過入射光的總能量。這句話聽起來像廢話但實(shí)際很多自定義Shader就是栽在這里——漫反射和鏡面反射各算各的加起來超過1材質(zhì)看起來就“溢出”“過曝”。PBR通過BRDF的數(shù)學(xué)結(jié)構(gòu)天然保證這一點(diǎn)。第二個(gè)法則是微表面模型宏觀上平整的物體表面放大到微米級(jí)其實(shí)是無數(shù)朝向各異的小平面。光打到表面每個(gè)微平面都在做鏡面反射但它們朝向不同有的微平面會(huì)被相鄰微平面遮擋。最終我們看到的高光模糊程度就是微平面朝向分布和遮擋關(guān)系共同作用的結(jié)果。這就是為什么PBR要用D法線分布、G幾何遮蔽、F菲涅爾這三類函數(shù)來描述鏡面反射。2. 渲染方程本體所有PBR的源頭2.1 那個(gè)讓人頭疼的積分式到底在算什么渲染方程可以追溯到Kajiya在1986年提出的形式圖形學(xué)里叫Rendering Equation。PBR只是它的一個(gè)實(shí)時(shí)渲染近似實(shí)現(xiàn)。完整形式是Lo(p, ωo) Le(p, ωo) ∫Ω f_r(p, ωi, ωo) Li(p, ωi) (n · ωi) dωi逐項(xiàng)翻譯Lo(p, ωo)從表面點(diǎn)p朝觀察方向ωo出射的光線輻射率也就是最終進(jìn)眼睛的值。Le(p, ωo)p點(diǎn)自己發(fā)出的光比如自發(fā)光材質(zhì)。實(shí)時(shí)渲染里常見PBR材質(zhì)一般把這個(gè)置0。Li(p, ωi)從入射方向ωi打到p點(diǎn)的光。f_r(p, ωi, ωo)BRDF雙向反射分布函數(shù)描述“一束光從ωi進(jìn)來有多少比例從ωo出去”。n · ωi法線和入射方向的余弦用于把光投影到表面面積上?!姚?... dωi對法線半球上所有可能入射方向做積分。我第一次看這個(gè)式子也暈后來找到一個(gè)人話解釋它就是把四面八方可能照到這個(gè)點(diǎn)的光全累加起來乘上材質(zhì)反射比例得到你看到的結(jié)果。積分符號(hào)只是“把所有方向都算一遍并加總”的數(shù)學(xué)寫法?,F(xiàn)在實(shí)時(shí)渲染不可能真的對半球連續(xù)積分于是拆成兩部分——直接光和間接光分別處理。2.2 BRDF一體兩面漫反射加鏡面反射渲染方程里真正決定材質(zhì)長相的是BRDF也就是f_r函數(shù)。實(shí)時(shí)PBR里幾乎所有的方案都把BRDF拆成兩項(xiàng)f_r f_d漫反射項(xiàng) f_s鏡面反射項(xiàng)漫反射項(xiàng)對應(yīng)光線進(jìn)入材質(zhì)內(nèi)部經(jīng)過多次散射后重新從表面射出來的那部分。經(jīng)典Lambert模型認(rèn)為這一項(xiàng)與觀察方向無關(guān)只和光的入射角度有關(guān)公式是f_d diffuseColor / π這里除以π不是湊數(shù)而是為了保證球面積分時(shí)能量守恒——漫反射在半球上積分后的總能量正好是入射能量的比例除π后歸一。如果你在Unity代碼里看到UNITY_INV_PI那其實(shí)就是1 / π在漫反射項(xiàng)里出現(xiàn)。鏡面反射項(xiàng)才是PBR真正精彩的部分負(fù)責(zé)高光和反射由Cook-Torrance模型承擔(dān)。實(shí)時(shí)渲染中99%的PBR鏡面BRDF都長這樣f_s D * G * F / (4 * n·l * n·v)D是法線分布函數(shù)G是幾何遮蔽函數(shù)F是菲涅爾函數(shù)。每一項(xiàng)代表微表面模型的不同側(cè)面下一章逐項(xiàng)拆。3. Cook-Torrance鏡面反射Unity PBR的真正核心3.1 D項(xiàng)GGX法線分布函數(shù)為什么是默認(rèn)選擇D項(xiàng)描述微表面中法線恰好朝向半矢量h的比例。這里的h是指向入射光和視線中間方向的單位向量。D值越大說明越多微表面的朝向正好反射光線到眼睛高光就越亮越銳利。歷史上用過很多NDF最早常用Beckmann分布數(shù)學(xué)上接近高斯高光衰減比較“圓滑”。后來業(yè)界發(fā)現(xiàn)GGX分布——也就是Trowbridge-Reitz分布——對真實(shí)材質(zhì)的模擬更好尤其是粗糙金屬表面因此成為主流D_GGX(n, h, α) α2 / (π * ((n·h)2 * (α2 ? 1) 1)2)α是粗糙度參數(shù)。這里有個(gè)Unity新手必踩的坑材質(zhì)面板上你調(diào)的其實(shí)是Smoothness光滑度它和粗糙度是相反數(shù)關(guān)系roughness 1 - smoothness然后進(jìn)入GGX公式時(shí)很多實(shí)現(xiàn)還會(huì)再平方alpha roughness * roughness所以你在Unity里把光滑度從0.9調(diào)到0.99視覺上高光變化會(huì)非常劇烈因?yàn)镚GX里的α按平方關(guān)系縮小。老Unity Standard Shader里有GGXTerm函數(shù)URP里也保留類似實(shí)現(xiàn)inline half GGXTerm(half NdotH, half roughness) { half a2 roughness * roughness; half d (NdotH * a2 - NdotH) * NdotH 1.0h; return UNITY_INV_PI * a2 / (d * d 1e-7h); }GGX和Beckmann相比最大的特點(diǎn)是長尾高光中心衰減后還保留一層淡淡的拖尾余暉。粗糙金屬離高光中心較遠(yuǎn)的位置仍然有反射能量視覺上更接近現(xiàn)實(shí)。后來很多延伸模型——比如GGX各向異性、帶Clear Coat的多層GGX——主干還是它。3.2 G項(xiàng)Smith幾何遮蔽管的其實(shí)是“遮擋”G項(xiàng)解決的是微表面互相遮擋的問題。一個(gè)微平面上的反射光可能在到達(dá)眼睛前被另一個(gè)微平面擋住同理入射光也可能被相鄰微平面先擋住一部分。前者叫Masking后者叫Shadowing。這在粗糙表面和掠射角觀察時(shí)會(huì)特別明顯。如果漏掉G項(xiàng)粗糙金屬在邊緣會(huì)亮得反??雌饋硐癜l(fā)光。Smith近似把G拆成兩個(gè)獨(dú)立函數(shù)的乘積G(l, v) G1(l) * G1(v)每個(gè)方向單獨(dú)計(jì)算被遮擋的概率。配合GGX分布時(shí)常用Smith-Schlick-GGX形式k α / 2 G1(x) (n·x) / ((n·x) * (1 - k) k)Unity里你會(huì)看到兩個(gè)相關(guān)函數(shù)SmithBeckmannTerm和SmithJointGGXTerm。前者是老實(shí)現(xiàn)后者把陰影和遮蔽聯(lián)合計(jì)算性能和匹配度更好。URP/HDRP里主推SmithJointGGXTerm。實(shí)際調(diào)Shader時(shí)如果發(fā)現(xiàn)粗糙表面高光異常刺眼可以先懷疑G項(xiàng)沒接對或者接成了只乘一個(gè)方向的簡化版。3.3 F項(xiàng)Schlick近似讓菲涅爾變得便宜菲涅爾效應(yīng)在生活中隨處可見正面看水面是透明的斜著看水面變成一面鏡子。反射率會(huì)隨視線與法線的夾角變化。數(shù)學(xué)上精確的菲涅爾方程涉及折射率、吸收系數(shù)運(yùn)算量不小。實(shí)時(shí)PBR普遍用Schlick近似F(v,h) F0 (1 - F0) * (1 - v·h)^5F0是垂直入射視線和表面垂直時(shí)的反射率。對非金屬電介質(zhì)F0大約0.04就是這個(gè)原因?qū)е滤兴芰喜馁|(zhì)暗部都有一層微弱的灰色高光對金屬F0是彩色值直接取Albedo顏色作為反射主色所以金屬會(huì)反射出自身的顏色。Unity里的FresnelTerm就是按Schlick實(shí)現(xiàn)的inline half3 FresnelTerm(half3 F0, half cosA) { return F0 (1 - F0) * pow(1 - cosA, 5); }Schlick近似的好處是只用一個(gè)5次方就能得到視覺上很準(zhǔn)的結(jié)果。實(shí)際問題中它的表現(xiàn)也基本夠用——除非你是寫離線渲染器或者對掠射角要求極高否則不需要上完整菲涅爾公式。調(diào)材質(zhì)時(shí)建議經(jīng)常調(diào)整觀察角度因?yàn)檎娴姆颇鶢栕兓幻黠@掠射角才是判斷F項(xiàng)是否設(shè)置正確的關(guān)鍵。3.4 分母里的4(n·l)(n·v)能量守恒里最容易被漏掉的除數(shù)Cook-Torrance鏡面BRDF里那個(gè)分母經(jīng)常被人忽略但它非常重要f_s D * G * F / (4 * n·l * n·v)這個(gè)分母的作用是把微表面局部立體角轉(zhuǎn)換到宏觀表面立體角本質(zhì)是幾何與積分的對應(yīng)關(guān)系。沒有這個(gè)分母BRDF不符合能量守恒高光會(huì)明顯過亮。但由于n·l和n·v可能趨近0分母會(huì)帶來數(shù)值爆炸風(fēng)險(xiǎn)所以引擎代碼里幾乎都要加一個(gè)極小保護(hù)值float specularTerm D * G * F / max(4.0h * NdotL * NdotV, 0.001);我見過不少同學(xué)手寫PBR時(shí)忘記這個(gè)除數(shù)高光亮度翻倍還找不到原因。另一個(gè)容易漏的是順序BRDF里已經(jīng)有一個(gè)n·l而直接光照的radiance里通常也會(huì)乘一個(gè)n·l代碼里經(jīng)常把這兩個(gè)乘到一起再約掉你能看到實(shí)現(xiàn)里變成了只除一個(gè)4NdotV。這是合法的代數(shù)化簡不是bug。4. 從公式到ShaderUnity里PBR參數(shù)是怎么一步步進(jìn)方程4.1 URP Lit Shader里的PBS代碼長什么樣理論說了一堆也該看看Unity里實(shí)際代碼了。Built-in管線的Standard Shader在UnityStandardBRDF.cginc里實(shí)現(xiàn)BRDFURP管線則把相關(guān)代碼集中到了Packages/com.unity.render-pipelines.universal/Shaders/BRDF.hlsl。核心函數(shù)是InitializeBRDFData和DirectBRDF。InitializeBRDFData干的事是把材質(zhì)面板上的Albedo、Metallic、Smoothness這些美術(shù)參數(shù)轉(zhuǎn)成BRDF真正使用的diffuse、specular、roughnessinline void InitializeBRDFData( half3 albedo, half metallic, half specular, half smoothness, half alpha, out BRDFData outBRDFData) { half oneMinusReflectivity; half3 specularTint; // 高光工作流specular是美術(shù)給的彩色高光 // 金屬工作流specular由albedo和metallic混合得到 #if defined(_SPECULAR_SETUP) specularTint specular; oneMinusReflectivity 1.0h - Max3(specularTint.r, specularTint.g, specularTint.b); #else specularTint lerp(kDielectricSpec.rgb, albedo, metallic); oneMinusReflectivity 1.0h - metallic; #endif outBRDFData.diffuse albedo * oneMinusReflectivity; outBRDFData.specular specularTint; half perceptualRoughness 1.0h - smoothness; outBRDFData.roughness perceptualRoughness * perceptualRoughness; outBRDFData.roughness2 outBRDFData.roughness * outBRDFData.roughness; ... }這里的kDielectricSpec.rgb就是那個(gè)0.04的非金屬基準(zhǔn)反射率。金屬工作流里金屬度等于1時(shí)漫反射系數(shù)歸零specular完全取Albedo顏色金屬度等于0時(shí)diffuse取Albedospecular固定0.04灰。這樣就從代碼層面保證了金屬材質(zhì)沒有漫反射這個(gè)物理事實(shí)。DirectBRDF里就是實(shí)際光照計(jì)算大致邏輯half3 DirectBDRF(BRDFData brdfData, half3 normalWS, half3 lightDirectionWS, half3 viewDirectionWS, half NdotL, half NdotV) { half NdotH saturate(dot(normalWS, normalize(lightDirectionWS viewDirectionWS))); half VdotH saturate(dot(viewDirectionWS, normalize(lightDirectionWS viewDirectionWS))); half3 radiance lightColor * lightAttenuation * NdotL; half3 brdf brdfData.diffuse; brdf brdfData.specular * DirectBRDFSpecular(brdfData, normalWS, lightDirectionWS, viewDirectionWS, NdotL, NdotV, NdotH, VdotH); return brdf * radiance; }DirectBRDFSpecular內(nèi)部就是DGF除以保護(hù)后的分母D用GGXG用Smith聯(lián)合項(xiàng)F用Schlick。你把這套代碼和第三章方程逐項(xiàng)對照完全能一一對應(yīng)上。4.2 金屬度、光滑度、AO分別流向哪里美術(shù)同學(xué)最關(guān)心的通常是貼圖實(shí)際影響哪部分。我用一張表概括三個(gè)核心輸入貼圖/參數(shù)進(jìn)入方程的哪部分視覺影響Albedo非金屬進(jìn)漫反射diffuse金屬進(jìn)菲涅爾F0決定固有色和金屬反射主色Metallic控制diffuse與specular的混合權(quán)重0是電介質(zhì)1是金屬不能出現(xiàn)中間值Smoothness轉(zhuǎn)換后進(jìn)入GGX的α和Smith的k值控制高光銳利程度與反射模糊程度AO主要乘間接光diffuse可擴(kuò)展到specular讓接觸陰影更扎實(shí)理論不參與直接光這里我特別想提醒一個(gè)Unity上游經(jīng)驗(yàn)金屬度不要設(shè)置成0.5這種中間值。物理上材質(zhì)要么導(dǎo)電金屬要么不導(dǎo)電電介質(zhì)不存在半金屬半塑料。程序員測試時(shí)隨手把Metallic拖到0.5會(huì)得到一片既不漫反射也不鏡面反射的“死灰”區(qū)域這種現(xiàn)象本質(zhì)是能量不守恒不是PBR做錯(cuò)了。磨損舊金屬的正確表達(dá)應(yīng)該是用多張貼圖或上下兩層材質(zhì)混合而不是單個(gè)材質(zhì)上畫0.5過渡。另外金屬度和AO貼圖在導(dǎo)入時(shí)要注意Color Space。金屬度和Smoothness這類數(shù)據(jù)是線性變化如果被Unity當(dāng)成sRGB貼圖處理渲染結(jié)果會(huì)明顯偏灰或偏暗。老項(xiàng)目里經(jīng)常出現(xiàn)“我把材質(zhì)參數(shù)都調(diào)對了但畫面就是不對勁”的情況最后發(fā)現(xiàn)是貼圖導(dǎo)入設(shè)置里沒關(guān)sRGB。4.3 間接光與IBL光靠公式是渲染不出PBR的渲染方程的半球積分在實(shí)時(shí)渲染里沒法逐方向算。主流方案是IBL也就是基于圖像的光照。Unity里漫反射間接光和鏡面間接光是分開算的漫反射間接光把環(huán)境立方體貼圖卷積成輻照度圖IrradianceMap運(yùn)行時(shí)采樣一次再乘漫反射項(xiàng)。Unity里可以理解為Light Probe和環(huán)境光采樣的來源。鏡面間接光把環(huán)境立方體貼圖按不同粗糙度預(yù)過濾成不同Mip級(jí)別的PrefilteredEnvMap運(yùn)行時(shí)根據(jù)材質(zhì)粗糙度選擇對應(yīng)的Mip層采樣再用菲涅爾項(xiàng)加權(quán)。于是又能解釋一個(gè)常見問題為什么場景沒有反射探針時(shí)金屬材質(zhì)會(huì)發(fā)黑或者像涂了鍋底灰因?yàn)殓R面IBL沒有數(shù)據(jù)來源直接光只能提供一個(gè)太陽點(diǎn)光源級(jí)別的高光剩下的半球環(huán)境反射全是零。金屬材質(zhì)反射率本來就高沒了環(huán)境輸入就變成黑塊。解決辦法是在場景里布置反射探針或者至少讓Skybox作為默認(rèn)環(huán)境源存在。后處理管線里現(xiàn)在的SSR屏幕空間反射能在沒有探針的情況下補(bǔ)銳利反射HDRP做得尤其完整但本質(zhì)上還是對渲染方程中間接光部分的一種近似采樣。移動(dòng)端上SSR很難跑反射探針依然是最可靠的方案。5. PBR調(diào)參與性能優(yōu)化實(shí)操中的坑和思路5.1 材質(zhì)偏暗、過曝、發(fā)灰的排查順序在Unity里做PBR最容易遇到的怪異現(xiàn)象就是這三個(gè)偏暗、過曝、發(fā)灰。我按自己排查經(jīng)驗(yàn)給一個(gè)固定順序第一驗(yàn)證光照環(huán)境是否完整。PBR對環(huán)境的依賴遠(yuǎn)超舊光照模型。先給場景加一個(gè)反射探針把環(huán)境光強(qiáng)度拉到一個(gè)合理范圍再調(diào)整PBR材質(zhì)本身。很多時(shí)候材質(zhì)沒問題是環(huán)境沒喂夠。第二檢查貼圖導(dǎo)入設(shè)置。金屬度、粗糙度、AO這些線性數(shù)據(jù)千萬別按sRGB導(dǎo)入。Unity默認(rèn)UI看著挺正常實(shí)際渲染里可能已經(jīng)偏了一個(gè)量級(jí)。你可以在材質(zhì)面板上右鍵Shader著色查中間值顏色也可以直接用Frame Debugger看輸入貼圖。第三檢查高光是否過曝。BRDF的F項(xiàng)在掠射角會(huì)快速逼近1配合高動(dòng)態(tài)燈光很容易爆。Unity里燈光強(qiáng)度如果繼續(xù)沿用舊式幾千Lux的做法配合PBR高光會(huì)飛得很厲害。用ACES或Filmic色調(diào)映射能救回來但最好從源頭把燈光物理單位調(diào)合理。第四檢查能量守恒是否被破壞。比如金屬度用了中間過渡或自定義Shader漏掉除以π和分母4。把這些除數(shù)補(bǔ)上過曝基本能緩解60%。發(fā)灰往往發(fā)生在粗糙度平均值過高、鏡面反射缺乏環(huán)境細(xì)節(jié)的情況。一把低粗糙度材質(zhì)靠探針恢復(fù)反射后灰感會(huì)立刻消失。5.2 Built-in、URP、HDRP同一方程的不同落地方式很多人關(guān)心自己在Built-in里做的PBR材質(zhì)遷到URP會(huì)不會(huì)變樣。先給結(jié)論底層方程完全相同仍然是GGX加Smith加Schlick的Cook-Torrance框架。差異在配套功能Built-in Standard包含完整的PBS代碼在UnityStandardBRDF.cginc間接光采樣用了球諧和反射探針功能豐富但源碼歷史包袱重。URP LitShader更結(jié)構(gòu)化BRDF集中在BRDF.hlsl去掉了部分非物理擴(kuò)展項(xiàng)參數(shù)語義更清晰。如果你原來依賴高光調(diào)得夸張的舊Shader切到URP會(huì)發(fā)現(xiàn)高光表現(xiàn)明顯收斂——這不是引擎壞了是URP默認(rèn)把非物理的“魔法值”去掉了。HDRP Lit在同一個(gè)BRDF主干上擴(kuò)展了各向異性、清漆、次表面散射、SSS等高級(jí)功能。底層仍然是加過料的GGX體系。我自己的經(jīng)驗(yàn)是中小項(xiàng)目直接用URP跟著Unity 6走材質(zhì)遷移時(shí)不要指望自動(dòng)完美重點(diǎn)看金屬度、光滑度、AO這三張貼圖導(dǎo)入設(shè)置以及反射探針是否同步遷移。Surface Type和Render Face這些選項(xiàng)在URP里還會(huì)影響陰影和深度寫入需要逐材質(zhì)過一遍。還要提一個(gè)熱詞里不少人問到的“Unity 2018入門與實(shí)戰(zhàn)”相關(guān)點(diǎn)如果你是從老版本項(xiàng)目升上來Unity會(huì)自動(dòng)把Standard Shader材質(zhì)轉(zhuǎn)成URP Lit但轉(zhuǎn)換過程中Custom Shader可能變成洋紅色材質(zhì)需要手工重新指定。這個(gè)不用慌把Shader換成Lit重新調(diào)一遍參數(shù)就行。5.3 移動(dòng)端上PBR的性能賬移動(dòng)端跑PBR性能瓶頸往往不是BRDF那幾次乘除而是間接光采樣和后處理。BRDF本身的GGX和Smith計(jì)算在現(xiàn)代手機(jī)GPU上不算重真正貴的是反射探針立方體貼圖采樣尤其多個(gè)探針插值時(shí)。后處理鏈Bloom、SSR、TAA這些與PBR配套的效果開銷遠(yuǎn)大于材質(zhì)本身。高分辨率陰影貼圖與PBR高光的匹配粗糙度低時(shí)高光對陰影邊緣敏感陰影質(zhì)量差甚至?xí)苯悠茐牟馁|(zhì)質(zhì)感。優(yōu)化方向我給三條。第一移動(dòng)端盡量用單反射探針或干脆用環(huán)境貼圖模擬鏡面IBL不要場景里鋪一堆探針做實(shí)時(shí)插值。第二把PBR相關(guān)的高頻噪聲貼圖壓縮能走單通道的貼圖盡量不要存RGBA四通道帶寬省下來效果更明顯。第三如果手寫PBR可以考慮把預(yù)積分的DFG項(xiàng)存成LUT運(yùn)行時(shí)采樣省掉每像素多次pow運(yùn)算。URP內(nèi)部已經(jīng)有類似優(yōu)化自定義Shader時(shí)要記得接上。另外還有一個(gè)冷門但常見的問題粒子特效內(nèi)存泄露和PBR無關(guān)但只要粒子系統(tǒng)上用了帶鏡面反射的材質(zhì)在高反射環(huán)境下DrawCall和Overdraw會(huì)暴漲。做移動(dòng)端時(shí)粒子材質(zhì)盡量選Unlit必要的高光投射交給美術(shù)貼圖模擬比實(shí)時(shí)PBR劃算得多。我個(gè)人在實(shí)際項(xiàng)目里最后留下一個(gè)習(xí)慣無論腳本還是Shader都先把手寫的PBR核心單獨(dú)抽成一個(gè)工具材質(zhì)場景里放一個(gè)標(biāo)準(zhǔn)光源和反射探針用對照法調(diào)參。等美術(shù)把一套材質(zhì)調(diào)完再整批鋪到場景。這比一邊調(diào)材質(zhì)一邊被場景燈光干擾要高效得多。另外如果你是從二次元Shader或NPR卡通渲染轉(zhuǎn)過來的請記住PBR默認(rèn)假設(shè)的是物理光照環(huán)境——它不會(huì)像卡通渲染那樣“逆光也清晰”。你需要給它配套合理的環(huán)境光、反射源和色調(diào)映射才能得到那種真實(shí)但不發(fā)灰的畫面。這也是Unity PBR渲染方程之外真正需要時(shí)間積累的東西。