Custom SRP - LOD and Reflections

1 LOD Groups

場景中對象越多,場景就越豐富,但是過多的對象,也會增加 CPU 和 GPU 的負擔.同時如果對象最終渲染在屏幕上后覆蓋的像素太少,就會產生模糊不清的像素點/噪點.如果能夠不渲染這些過小的對象,就能解決噪點問題,同時釋放 CPU GPU,去處理更重要的對象.

裁剪掉這些對象,可能會導致對象突然消失/出現的問題,因此,可以基于對象在屏幕上的大小,定義一系列子對象,根據對象到攝像機的距離,選擇一個子對象進行渲染

這些邏輯,都可以有 LOD Group 組件來實現.

1.1 LOD Group Component

組件創建后,默認有4個LOD級別, LOD 0,1,2 和culled(裁剪掉,不渲染).

組件上的百分數,代表對象在屏幕上,渲染的高度與屏幕高度的比值(一般都是這樣),叫做對象的屏占比.如上圖,它表示如果屏占比大于60%則用 LOD 0 渲染,以此類推,知道屏占比小于10% 時就會被裁剪掉

在 Quality Settings 中,可以配置 LOD Bias,默認是 2, 會將對象屏占比乘以2.在上面的配置中,意味著屏占比大于30%時渲染 LOD 0.

對于一個 LOD 對象,我們一般會創建一個對象,添加 LOD Group 組件,然后為其創建子對象,這些子對象被LOD驅動,渲染當前 LOD 級別配置的 Renderer.

選擇一個 LOD 級別,點擊 + 號,創建一個 Renderer 項, 然后就可以將 LOD 子對象拖動到 Renderers 列表中,表示該LOD會渲染它.

一個對象可以配置到多個 LOD 中.比如有A,B,C三個子對象,LOD0時,ABC都渲染,LOD1時渲染AB,LOD2時渲染A.這樣隨著距離增加,按照重要程度(ABC),三個子對象會一次消失.

1.2 LOD Transition

LOD之間的切換可能會過于突兀, 在LOD Group 的 Fade Mode 選項中,選擇 cross fade,則切換時舊的 LOD 會有淡出過程,新的也會有淡入.但是目前該選項不會帶來任何效果,因為這需要我們的 shader 的支持.

切換時兩個LOD 的 Renderer 都會渲染, shader 中需要對他們進行混合.

Unity 用 LOD_FADE_CROSSFADE 來定義支持混合的 shader 變體.需要在 CustomLit 和 ShadowCaster 兩個 pass 中定義 shader keyword:

#pragma multi_compile _ LOD_FADE_CROSSFADE

Fade 控制參數由 per draw buffer 中的 unity_LODFade 提供.其 x 時淡出參數.該參數在淡出和淡入時的取值是不同的:

  • 淡出時,1 ~ 0
  • 淡入時,-1 ~ 0 (一定是負的,但是是不是 0 ~ -1 ?)

后面實現 fade 效果時,會針對性處理.

1.3 Dithering fade 效果

我們通過 clip 來實現淡入淡出效果.

定義 ClipLOD 方法,并在像素著色器開始時調用

// 執行 lod fade 裁剪
void ClipLOD(float2 positionCS, float fade)
{
#if defined(LOD_FADE_CROSSFADE)//// 在垂直方向上劃分條紋的效果//float dither = (positionCS.y % 32)/32;// unity dither 生成函數float dither = InterleavedGradientNoise(positionCS.xy, 0);// 淡入時,fade 時負的,因此需要 + ditherclip(fade + (fade < 0 ? dither : -dither));
#endif
}float4 LitPassFragment(Varyings input) : SV_TARGET
{UNITY_SETUP_INSTANCE_ID(input);ClipLOD(input.positionCS, unity_LODFade.x);...
}

可以看到我們自定義的 dither 的 fade 效果

lightmap 過度時,我們用到了 unity 提供的 dither 生成函數?InterleavedGradientNoise,這里可以換成同樣的函數看效果

1.4 Animated cross-fading

開啟后, cross fade 會在一定時間內完成,默認時 0.5 秒.這個值可以在代碼中,修改?LODGroup.crossFadeAnimationDuration?的值來改變.注意這個值是靜態變量,會影響所有 LOD Group

2 Reflections

目前我們的材質還不支持高光反射,導致我們的 metallic 材質是黑色的.

首先向 baked scene 中,創建幾個球,配置其材質為 metallic > 0.8,可以發現是黑色的

2.1 Indirect BRDF

首先增加 IndirectBRDF 函數

// BRDF.hlsl
float3 IndirectBRDF(Surface surface, BRDF brdf, float3 diffuse, float3 specular)
{float3 reflection = brdf.specular * specular;// 粗糙表面會散射光線,因此除以粗糙度的平方+1reflection /= (brdf.roughness * brdf.roughness + 1.0);float3 diff = brdf.diffuse * diffuse;return diff + reflection;
}

粗糙度除了降低反射強度,還會讓反射變得模糊.我們可以通過采樣 cubemap 的低級別 mipmap 來實現這樣的效果.Unity 在 Core RP Library 中的 ImageBasedLighting.hlsl 中定義了基于 perceptual roughness(感知粗糙度)來獲取mipmap level,因此需要在 BRDF 結構體中定義該數據

// BRDF.hlsl
struct BRDF
{float3 specular;float3 diffuse;float roughness;float perceptualRoughness;
};
...BRDF GetBRDF(Surface surface, bool premultiplyAlpha)
{BRDF brdf;float oneMinusReflectivity = OneMinusReflectivity(surface.metallic);oneMinusReflectivity = 1.0 - surface.metallic;brdf.diffuse = surface.color * oneMinusReflectivity;if (premultiplyAlpha)brdf.diffuse *= surface.alpha;brdf.specular = lerp(MIN_REFLECTIVITY, surface.color, surface.metallic);brdf.perceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(surface.smoothness);brdf.roughness = PerceptualRoughnessToRoughness(brdf.perceptualRoughness);return brdf;
}

unity 通過 unity_SpecCube0 提供環境貼圖,在GI.hlsl 中聲明相應的貼圖和采樣器,然后定義采樣函數.在GI結構體中增加 specular 用來傳遞反射

// GI.hlsl#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/EntityLighting.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/ImageBasedLighting.hlsl"
...
TEXTURECUBE(unity_SpecCube0);
SAMPLER(samplerunity_SpecCube0);
...
struct GI
{float3 diffuse;ShadowMask shadowMask;float3 specular;
};
...
// 采樣環境貼圖
float3 SampleEnvironment(Surface surfaceWS, BRDF brdf)
{// 采樣環境貼圖需要一個方向,由表面法線和表面的觀察方向計算反射方向.float3 uvw = reflect(-surfaceWS.viewDirection, surfaceWS.normal);// 基于“感知粗糙度”,計算采樣時的 mipmap levelfloat mip = PerceptualRoughnessToMipmapLevel(brdf.perceptualRoughness);float4 environment = SAMPLE_TEXTURECUBE_LOD(unity_SpecCube0, samplerunity_SpecCube0, uvw, 0.0);return environment.rgb;
}
..
GI GetGI (float2 lightMapUV, Surface surfaceWS) 
{GI gi;gi.diffuse = SampleLightMap(lightMapUV) + SampleLightProbe(surfaceWS);// 采樣環境圖gi.specular = SampleEnvironment(surfaceWS);...return gi;
}

修改Lighting 中的 GetLighting 函數,調用 IndirectBRDF

float3 GetLighting(Surface surfaceWS, BRDF brdf, GI gi)
{ShadowData shadowData = GetShadowData(surfaceWS);shadowData.shadowMask = gi.shadowMask;// 臨時返回以查看數據//return gi.shadowMask.shadows.rgb;//float3 color = gi.diffuse * brdf.diffuse;float3 color = IndirectBRDF(surfaceWS, brdf, gi.diffuse, 1.0);for(int i = 0; i < GetDirectionalLightCount(); ++i){Light light = GetDirectionalLight(i, surfaceWS, shadowData);color += GetLighting(surfaceWS, brdf, light);}return color;
}

如下圖,反射了天空盒

2.2 Fresnel Reflection

當視線掠過表面(觀察方向與表面法線接近90度)時,表面更像鏡子,會反射更多,這種現象就是 菲涅爾反射.模擬這種現象非常復雜,我們參考 URP 中采用的 Schlick approximation(石里克近似) 法.

我們將菲涅爾反射定義為白色,而粗糙表面會降低菲涅爾效應,因此我們基于粗糙度計算菲涅爾顏色(本質上灰度).然后基于觀察方向和表面法線計算菲涅爾強度,并在菲涅爾顏色和反射之間插值.

// BRDF.hlslstruct BRDF
{float3 specular;          // 高光反射float3 diffuse;          // 漫反射float roughness;          // 粗糙度float perceptualRoughness; // 感知粗糙度float fresnel; // 菲涅爾顏色,因為是灰度,rgb都相等,因此只需要一個浮點數
};...BRDF GetBRDF(Surface surface, bool premultiplyAlpha)
{BRDF brdf;float oneMinusReflectivity = OneMinusReflectivity(surface.metallic);oneMinusReflectivity = 1.0 - surface.metallic;brdf.diffuse = surface.color * oneMinusReflectivity;if (premultiplyAlpha)brdf.diffuse *= surface.alpha;brdf.specular = lerp(MIN_REFLECTIVITY, surface.color, surface.metallic);brdf.perceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(surface.smoothness);brdf.roughness = PerceptualRoughnessToRoughness(brdf.perceptualRoughness);// 計算菲涅爾灰度顏色brdf.fresnel = saturate(surface.smoothness + 1.0 - oneMinusReflectivity);return brdf;
}float3 IndirectBRDF(Surface surface, BRDF brdf, float3 diffuse, float3 specular)
{// 計算菲涅爾強度float fresnelStrength = Pow4(1.0 - saturate(dot(surface.normal, surface.viewDirection)));// 反射是根據菲尼爾強度,在高光反射和菲涅爾反射之間插值float3 reflection = specular + lerp(brdf.specular, brdf.fresnel, fresnelStrength);// 粗糙表面會散射光線,因此除以粗糙度的平方+1reflection /= (brdf.roughness * brdf.roughness + 1.0);float3 diff = brdf.diffuse * diffuse;return diff + reflection;
}

2.3 Fresnel Slider

菲涅爾反射主要出現在幾何體的邊緣.當環境圖與物體背后的顏色匹配時,效果很好.但是如果顏色不匹配,就會顯得怪異.

降低光滑度可以降低菲涅爾反射,但會讓整個表面變暗.同時,在某些情況下,近似的菲涅爾反射可能不合適,如水下.因此,需要加一個參數來控制菲涅爾強度.

// Lit.shader 中定義材質參數
_Smoothness("Smoothness", Range(0,1)) = 0.51
_Fresnel("Fresnel", Range(0,1)) = 1// LitInput.hlsl 中,定義 per material 變量,并定義查詢函數
UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
...
float _Smoothness;
float _Fresnel;
...
UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)...float GetFresnel (float2 baseUV)
{return UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Fresnel);
}// UnlitInput.hlsl 中,定義返回0的函數
float GetFresnel (float2 baseUV)
{return 0.0;
}// Surface.hlsl 中,為Surface結構體增加 fresnelStrength
struct Surface
{...float smoothness;float fresnelStrength;...
};// LitPass.hlsl 中,為 Surface.fresnelStrength 賦值
float4 LitPassFragment(Varyings input) : SV_TARGET
{...surface.smoothness = GetSmoothness(input.uv);surface.fresnelStrength = GetFresnel(input.uv);...
}// BRDF.hlsl 中,應用該強度
// 間接 BRDF 光
float3 IndirectBRDF(Surface surface, BRDF brdf, float3 giDiffuse, float3 giSpecular)
{//return gi.diffuse * brdf.diffuse;//float3 reflection = giSpecular * brdf.specular;// 計算菲涅爾強度 float fresnelStrength = surface.fresnelStrength * Pow4(1.0 - saturate(dot(surface.normal, surface.viewDirection)));// 反射是根據菲尼爾強度,在高光反射和菲涅爾反射之間插值,在與 giSpecular(來自環境圖) 相乘float3 reflection = giSpecular * lerp(brdf.specular, brdf.fresnel, fresnelStrength);// 粗糙表面會散射光線,因此除以粗糙度的平方+1reflection /= (brdf.roughness * brdf.roughness + 1.0);// 計算間接 diffusefloat3 diffuse = brdf.diffuse * giDiffuse;// 累加 diffuse 和 reflectionreturn diffuse + reflection;
}

2.4 Reflection Probes

除了反射天空,還可以創建當前場景.通過GameObject / Light / Reflection Probe創建 Reflection Probe,該組件會在其位置拍攝6方向并生成立方體貼圖.屬性 Box Size 用來確定影響范圍,配合 Importance 重要性,該范圍內的對象會使用該 Reflection Probe.

Cube map 可以離線生成,也可以實時生成.實時生成需要渲染6個畫面,因此消耗比較大,不建議.

通過 Anchor Override, Renderer 可以調整 Reflection Probe 選擇,即使對象本身超出了范圍,但是可以指定該屬性的位置在 Reflection Probe 范圍內,來優化選擇. 使用場景中的 Light Probe 會打斷合批.同時,DrawMeshInstanced 接口渲染時,不支持指定 Reflection Probe.

Renderer 的 Reflection Probe 選項中:

  • 默認是 Blend Probes, Unity會選擇兩個 Reflection Probe 并進行插值,該模式與 SRP Batcher 不兼容,因此我們不能用.
  • off 表示使用天空盒的 cube map
  • Simple 表示使用最近,最重要的 Reflection Probe

Cube map 可能是 HDR 或 LDR 的,我們需要正確解碼采樣結果.該解碼參數以 unity_SpecCube0_HDR 變量提供.

// UnityInput.hlsl 中
CBUFFER_START(UnityPerDraw)
...
float4 unity_ProbeVolumeMin;
float4 unity_SpecCube0_HDR; // 如何解碼 reflection cube map
CBUFFER_END// GI.hlsl 中
// 采樣環境貼圖
float3 SampleEnvironment(Surface surfaceWS, BRDF brdf)
{// 采樣環境貼圖需要一個方向,由表面法線和表面的觀察方向計算反射方向.float3 uvw = reflect(-surfaceWS.viewDirection, surfaceWS.normal);// 基于“感知粗糙度”,計算采樣時的 mipmap levelfloat mip = PerceptualRoughnessToMipmapLevel(brdf.perceptualRoughness);float4 environment = SAMPLE_TEXTURECUBE_LOD(unity_SpecCube0, samplerunity_SpecCube0, uvw, 0.0);// unity_SpecCube0_HDR 參數可以確定 cube map 是 HDR/LDR的,通過下面的方法正確解碼return DecodeHDREnvironment(environment, unity_SpecCube0_HDR);
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/921028.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/921028.shtml
英文地址,請注明出處:http://en.pswp.cn/news/921028.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Linux篇章】互聯網身份密碼:解密 Session 與 Cookie 的隱藏玩法和致命漏洞!

本篇摘要 本篇將承接上篇HTTP講解&#xff08; 戳我查看 &#xff09;遺留的關于Cookie與Session的介紹&#xff0c;在本篇&#xff0c;將會介紹Cookie的由來&#xff0c;作用&#xff0c;以及缺點等&#xff0c;進而引出Session&#xff0c;最后介紹一下它們的性質等&#xf…

Postman接口測試工具:高效管理測試用例與環境變量,支持斷言驗證及團隊協作同步

之前跟你們聊過能搭知識網絡的 Obsidian&#xff0c;今天換個偏向接口測試的方向 —— 給你們安利一個 Github 上的「Postman」&#xff0c;它是個接口測試工具&#xff0c;官網能直接下載&#xff08;Postman: The Worlds Leading API Platform | Sign Up for Free&#xff09…

可可圖片編輯 HarmonyOS 上架應用分享

可可圖片編輯 HarmonyOS 上架應用分享 介紹 可可圖片編輯 原名 圖片編輯大師&#xff0c;因為上架審核的時候 &#xff0c;提示與一些已有應用重名&#xff0c;為了避免沖突&#xff0c;需要改名字&#xff0c;所以苦心思考了一分鐘&#xff0c;就調整成 可可圖片編輯。 應用…

Notepad++近期版本避雷

近期Notepad若干版本存在投毒事件&#xff0c;雖然也歡迎大家使用替代軟件&#xff0c;但是Notepad作為一款開源軟件&#xff0c;如有需要也可以繼續白嫖使用&#xff0c;但是請務必避開若干埋雷版本&#xff01; 經檢查&#xff0c;部分版本在幫助菜單中加入了有關tw的部分個人…

【lucene核心】impacts的由來

在 Lucene 的 Impact 概念&#xff08;出現在 ImpactsEnum / Impact 對象里&#xff09;中&#xff1a;字段 含義 freq 當前 term 在該文檔中出現了多少次&#xff08;即詞頻 term frequency&#xff09;。 norm 當前 文檔在該字段中的長度因子&#xff08;即之前 norms 里保存…

基于Echarts+HTML5可視化數據大屏展示-惠民服務平臺

效果展示代碼結構&#xff1a;主要代碼實現 index.html布局 <!doctype html> <html><head><meta charset"utf-8"><title>雙數智慧公衛-傳染病督導平臺</title><meta http-equiv"refresh" content"60;urlhttps…

【Flink】DataStream API:執行環境、執行模式、觸發程序執行

目錄執行環境getExecutionEnvironmentcreateLocalEnvironmentcreateRemoteEnvironment執行模式流執行模式&#xff08;Streaming&#xff09;批執行模式&#xff08;Batch&#xff09;自動模式&#xff08;AutoMatic&#xff09;觸發程序執行DataStream API是Flink的核心層API&…

CentOS7.6

騰訊云服務器 騰訊云 產業智變云啟未來 - 騰訊 服務器在控制臺顯示 點擊進入面板&#xff0c;顯示所有信息 現在來安裝桌面的遠程控制軟件 寶塔SSH終端:一款同時支持SSH和SFTP客戶端的免費軟件! 點擊立即下載 在云服務器的實例列表復制公網ip 密碼就是服務器的密碼&#xff…

前端架構知識體系:常見圖片格式詳解與最佳實踐

前端開發必備&#xff1a; 在前端開發中&#xff0c;合理選擇圖片格式直接影響網頁加載性能、用戶體驗和帶寬成本。本文將系統梳理常見圖片格式&#xff0c;分析它們的優缺點、壓縮原理、兼容性和推薦使用場景&#xff0c;并提供前端優化實戰建議。1. JPEG / JPG 全稱&#xff…

ARM的編程模型

ARM的編程模型 ARM 的編程模型指的是從程序員&#xff08;特別是匯編程序員和編譯器設計者&#xff09;視角所看到的 ARM 處理器架構。它定義了程序員可以使用的資源、數據操作方式以及規則&#xff0c;主要包括&#xff1a;寄存器組、數據類型、內存訪問方式、執行狀態和異常處…

最大熵強化學習相比傳統強化學習,有什么缺點?

要理解最大熵強化學習&#xff08;MaxEnt RL&#xff09;相比傳統強化學習&#xff08;如DQN、PPO、DDPG等&#xff09;的缺點&#xff0c;首先需要明確兩者的核心差異&#xff1a;傳統RL的目標是“最大化累積獎勵”&#xff0c;而MaxEnt RL在該目標基礎上額外增加了“最大化策…

python生成器與協程深度剖析

目錄 生成器 傳統列表 vs 生成器對比 yield機制深度解析 生成器的高級用法 協程的演進:從yield到async/await 基于yield的協程 現代async/await語法 協程的錯誤處理和超時控制 異步生成器與異步迭代器 異步生成器 異步迭代器實現 實戰案例:異步爬蟲框架設計 生成器…

論文解讀:基于 77 GHz FMCW 毫米波雷達的艙內占位檢測

毫米波 (mm-Wave) 雷達是汽車應用&#xff08;例如高級駕駛輔助系統 (ADAS)&#xff09;的一種解決方案。本研究探索了商用毫米波雷達技術在車內應用領域的應用。本文提出了一種基于 77 GHz 毫米波雷達的車輛占用檢測器框架。本研究采用了德州儀器 (Texas Instruments) 的多輸入…

進程優先級(Process Priority)

&#x1f381;個人主頁&#xff1a;工藤新一 &#x1f50d;系列專欄&#xff1a;C面向對象&#xff08;類和對象篇&#xff09; &#x1f31f;心中的天空之城&#xff0c;終會照亮我前方的路 &#x1f389;歡迎大家點贊&#x1f44d;評論&#x1f4dd;收藏?文章 文章目錄進…

OpenCV的輪廓檢測

1. 輪廓檢測的基本概念輪廓是圖像中連續的、閉合的曲線段&#xff0c;代表物體的邊界&#xff08;如圓形的輪廓是一條閉合曲線&#xff09;。OpenCV 的輪廓檢測通過 cv2.findContours() 實現&#xff0c;可用于形狀識別、物體計數、圖像分割等場景。2. 核心函數與參數&#xff…

亞信安全亮相鴻蒙生態大會2025 攜手鴻蒙生態繪就萬物智聯新藍圖

8 月30 日&#xff0c;以 “新場景?新體驗” 為主題的鴻蒙生態大會 2025 在深圳福田會展中心隆重開幕。本次大會由全球智慧物聯網聯盟&#xff08;GIIC&#xff09;主辦、鴻蒙生態服務&#xff08;深圳&#xff09;有限公司承辦&#xff0c;旨在搭建全球鴻蒙生態伙伴的高層次交…

Linux內核進程管理子系統有什么第四十回 —— 進程主結構詳解(36)

接前一篇文章&#xff1a;Linux內核進程管理子系統有什么第三十九回 —— 進程主結構詳解&#xff08;35&#xff09; 本文內容參考&#xff1a; Linux內核進程管理專題報告_linux rseq-CSDN博客 《趣談Linux操作系統 核心原理篇&#xff1a;第三部分 進程管理》—— 劉超 《…

面試問題:進程和線程,編譯步驟,const,map和unordered_map,深入理解unordered_map

目錄 進程和線程的區別 const修飾指針(左邊內容&#xff0c;右邊指向) 1. const 修飾指針指向的內容&#xff08;指向常量&#xff09; 2. const 修飾指針本身&#xff08;常量指針&#xff09; 3. const 同時修飾指針本身和指向的內容&#xff08;指向常量的常量指針&…

利用棒棒糖圖探索Office (US)的IMDB評分

利用棒棒糖圖探索Office (US)的IMDB評分 import numpy as np import pandas as pd import matplotlib.colors as mc import matplotlib.image as image import matplotlib.pyplot as pltfrom matplotlib.cm import ScalarMappable from matplotlib.lines import Line2D from m…

Zephyr如何注冊設備實例

設備樹 → 編譯期生成 → 運行時訪問 流程圖&#xff1a;Zephyr dev->config 工作流程設備樹 (.dts) ───────────────────────────── anx745139 {compatible "analogix,anx7451";reg <0x39>;reset-gpios <&gpio1 5 …