Unity-Shader詳解-其二

前向渲染和延遲渲染

前向渲染和延遲渲染總的來說是我們的兩種主要的渲染方式。

我們在Unity的Project Settings中的Graphic界面能夠找到渲染隊列的設定:

我們也可以在Main Camera這里進行設置:

那這里我們首先介紹一下兩種渲染(Forward Renderring\Deferred Renderring)的基本概念:

渲染就像去餐館吃飯,每一桌就是一個需要渲染的對象,我們在吃飯前要先指指點點(渲染的設置),前向渲染就是比較樸素的餐館運行模式:針對每一桌客人,我們都執行:客人下單,上菜,執行完一桌之后我們才去咨詢下一桌,這樣的話問題:作為餐館的服務人員,我們來回跑了太多次(調用GPU進行渲染的指令),且這樣計算的總開銷時間較長(下一桌客人一定要等到上一桌客人的菜上完才能點菜);延遲渲染就是我們先總的收集所有客人的點餐情況,然后根據總的要求進行上菜,這樣最大的改善就是我們大大減少了咨詢客人點菜情況的時間,且減小了整體的計算時長。

兩種渲染方式在多光源場景下的性能差異尤為明顯:前向渲染的過程中我們要分別每個渲染對象單獨地計算完光照效果之后再計算下一個對象,但是對于延遲渲染來說,整個場景的所有物體只用計算一次光照,本身光照計算就幾乎是開銷最大的部分,這樣可以相當大一部分節省性能。

但是顯然延遲渲染并不是十全十美的,你能統計全餐館的下單情況的前提是你得有足夠大的一個記事本,在計算機里這個記事本叫做:G-BUFFER,我們會把各個對象的諸多信息放入這個緩沖區,然后后續再在緩沖區中統一計算光照。因此,從這個角度來說,我們可以認為延遲渲染是前向渲染的以空間換時間的一種方式。

我們現在展開來說:

通俗地說,針對場景里的多光源,前向渲染會給光源分為三個優先級:最亮的一檔就會采取逐像素渲染,而最不重要的一檔我們會直接使用球諧函數來生成一個結果避免復雜運算,中間的一檔(逐頂點渲染)則不會超過四個。其中每個光源并不是一定處以某個優先級,而是一個兩種優先級的混合疊加態。

上述說的光源的優先級和限制的逐像素渲染光源數都是可以修改的:

這是前向渲染處理多光源的方法,那么對于延遲渲染來說呢?

延遲渲染是不支持抗鋸齒的,這是一個非常致命的問題,同時還不支持半透明:

?

因此,根據不同的場景來選擇不同的渲染策略才是最重要的。?

陰影實現

還是那句話,現在并沒有完美的生成陰影的算法,大多數都是有些缺陷的。

我們直接上代碼來介紹吧:

Shader "Chapter3/chapter3_3_shadow"
{Properties{// 定義主顏色屬性,可在材質面板中調整_MainColor ("Main Color", Color) = (1, 1, 1, 1)}SubShader{// -------- 基礎Pass:處理主要光源的投影 --------Pass{// 標簽定義,用于指定此Pass的光照模式為"ForwardBase"Tags{"LightMode" = "ForwardBase"}CGPROGRAM// 頂點著色器和片段著色器入口#pragma vertex vert#pragma fragment frag#pragma multi_compile_fwdbase // 多重編譯,支持不同光照模型#include "UnityCG.cginc"      // 包含Unity常用工具函數和宏#include "Lighting.cginc"    // 包含Unity光照計算函數#include "AutoLight.cginc"   // 包含Unity自動化光照相關代碼// 定義頂點到片段的數據結構struct v2f{float4 pos : SV_POSITION;   // 裁剪空間的頂點位置float3 normal : TEXCOORD0; // 法線,用于光照計算float4 vertex : TEXCOORD1; // 模型空間的頂點位置SHADOW_COORDS(2)           // 使用Unity預定義宏存儲陰影坐標};fixed4 _MainColor; // 主顏色變量// 頂點著色器v2f vert (appdata_base v){v2f o;o.pos = UnityObjectToClipPos(v.vertex); // 轉換頂點到裁剪空間o.normal = v.normal;                   // 傳遞法線信息o.vertex = v.vertex;                   // 傳遞頂點位置TRANSFER_SHADOW(o)                     // 計算陰影坐標并存儲return o;}// 片段著色器fixed4 frag (v2f i) : SV_Target{// 計算法線方向float3 n = UnityObjectToWorldNormal(i.normal);n = normalize(n);// 計算世界空間中的光源方向float3 l = WorldSpaceLightDir(i.vertex);l = normalize(l);// 將頂點從模型空間轉換到世界空間float4 worldPos = mul(unity_ObjectToWorld, i.vertex);// Lambert光照模型:計算法線與光線夾角的點積fixed ndotl = saturate(dot(n, l));fixed4 color = _LightColor0 * _MainColor * ndotl;// 疊加4個點光源的光照color.rgb += Shade4PointLights(unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0, // 點光源位置unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,       // 點光源顏色unity_4LightAtten0, worldPos.rgb, n                     // 衰減和位置) * _MainColor;// 疊加環境光照color += unity_AmbientSky;// 使用Unity宏計算陰影衰減系數UNITY_LIGHT_ATTENUATION(shadowmask, i, worldPos.rgb)// 將陰影系數與顏色相乘,應用陰影效果color.rgb *= shadowmask;return color; // 返回最終顏色}ENDCG}// -------- 額外的Pass:處理其他逐像素燈光的投影 --------Pass{// 標簽定義,此Pass用于附加光源,模式為"ForwardAdd"Tags{"LightMode" = "ForwardAdd"}// 混合模式:相加混合Blend One OneCGPROGRAM#pragma vertex vert#pragma fragment frag#pragma multi_compile_fwdadd_fullshadows // 支持多重編譯,包含完整陰影#include "UnityCG.cginc"#include "Lighting.cginc"#include "AutoLight.cginc"// 定義頂點到片段的數據結構,與基礎Pass一致struct v2f{float4 pos : SV_POSITION;float3 normal : TEXCOORD0;float4 vertex : TEXCOORD1;SHADOW_COORDS(2)};fixed4 _MainColor;// 頂點著色器v2f vert (appdata_base v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.normal = v.normal;o.vertex = v.vertex;TRANSFER_SHADOW(o)return o;}// 片段著色器fixed4 frag (v2f i) : SV_Target{// 計算法線和光照方向float3 n = UnityObjectToWorldNormal(i.normal);n = normalize(n);float3 l = WorldSpaceLightDir(i.vertex);l = normalize(l);// 轉換頂點到世界空間float4 worldPos = mul(unity_ObjectToWorld, i.vertex);// Lambert光照計算fixed ndotl = saturate(dot(n, l));fixed4 color = _LightColor0 * _MainColor * ndotl;// 疊加點光源的光照color.rgb += Shade4PointLights(unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,unity_LightColor[0].rgb, unity_LightColor[1].rgb,unity_LightColor[2].rgb, unity_LightColor[3].rgb,unity_4LightAtten0, worldPos.rgb, n) * _MainColor;// 使用陰影宏計算陰影系數UNITY_LIGHT_ATTENUATION(shadowmask, i, worldPos.rgb)// 應用陰影到顏色color.rgb *= shadowmask;return color; // 返回最終顏色}ENDCG}}// 回退著色器,定義為DiffuseFallBack "Diffuse"
}

我想我需要首先介紹兩個Tags中的LightMode:ForwardBase和ForwardAdd。

在代碼中我們用:

            // 混合模式:相加混合Blend One One

的混合模式把這兩種前向渲染的結果進行結合就可以得到一個完整的生成陰影的方法。

現在我們來看具體代碼:

        // -------- 基礎Pass:處理主要光源的投影 --------Pass{// 標簽定義,用于指定此Pass的光照模式為"ForwardBase"Tags{"LightMode" = "ForwardBase"}CGPROGRAM// 頂點著色器和片段著色器入口#pragma vertex vert#pragma fragment frag#pragma multi_compile_fwdbase // 多重編譯,支持不同光照模型#include "UnityCG.cginc"      // 包含Unity常用工具函數和宏#include "Lighting.cginc"    // 包含Unity光照計算函數#include "AutoLight.cginc"   // 包含Unity自動化光照相關代碼// 定義頂點到片段的數據結構struct v2f{float4 pos : SV_POSITION;   // 裁剪空間的頂點位置float3 normal : TEXCOORD0; // 法線,用于光照計算float4 vertex : TEXCOORD1; // 模型空間的頂點位置SHADOW_COORDS(2)           // 使用Unity預定義宏存儲陰影坐標};fixed4 _MainColor; // 主顏色變量// 頂點著色器v2f vert (appdata_base v){v2f o;o.pos = UnityObjectToClipPos(v.vertex); // 轉換頂點到裁剪空間o.normal = v.normal;                   // 傳遞法線信息o.vertex = v.vertex;                   // 傳遞頂點位置TRANSFER_SHADOW(o)                     // 計算陰影坐標并存儲return o;}// 片段著色器fixed4 frag (v2f i) : SV_Target{// 計算法線方向float3 n = UnityObjectToWorldNormal(i.normal);n = normalize(n);// 計算世界空間中的光源方向float3 l = WorldSpaceLightDir(i.vertex);l = normalize(l);// 將頂點從模型空間轉換到世界空間float4 worldPos = mul(unity_ObjectToWorld, i.vertex);// Lambert光照模型:計算法線與光線夾角的點積fixed ndotl = saturate(dot(n, l));fixed4 color = _LightColor0 * _MainColor * ndotl;// 疊加4個點光源的光照color.rgb += Shade4PointLights(unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0, // 點光源位置unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,       // 點光源顏色unity_4LightAtten0, worldPos.rgb, n                     // 衰減和位置) * _MainColor;// 疊加環境光照color += unity_AmbientSky;// 使用Unity宏計算陰影衰減系數UNITY_LIGHT_ATTENUATION(shadowmask, i, worldPos.rgb)// 將陰影系數與顏色相乘,應用陰影效果color.rgb *= shadowmask;return color; // 返回最終顏色}ENDCG}

我們來說新東西:

#pragma multi_compile_fwdbase // 多重編譯,支持不同光照模型

這句指令就是允許我們的前向渲染根據場景中的光照動態地調整一些涉及著色器參數的關鍵字選擇。

ForwardBase中主要分成三個部分:v2f,vert和frag。

v2f中我們可以看到熟悉的頂點坐標(裁剪空間和模型空間),法線和一個陰影的預定義宏,其中:

                float4 vertex : TEXCOORD1; // 模型空間的頂點位置SHADOW_COORDS(2)           // 使用Unity預定義宏存儲陰影坐標

我們法線模型空間的頂點位置居然使用了TEXCOORD1來存儲,這不是紋理坐標的語義嗎?是的這確實是,但是其實也沒人規定你不可以用,只要合乎語法即可(但是有一種語義不可以用,是的就是我們之前提到過的系統值語義:這種語義存儲的內容是被規定好的特殊階段的特殊數據,如果不符合則整個渲染流程報錯),陰影的預定義宏則是提前為將來生成的陰影坐標分配好了存儲的坐標索引(2)。

TRANSFER_SHADOW(o)                     // 計算陰影坐標并存儲

這一步就是計算陰影坐標的方法,Unity的著色器語言為我們封裝成了一個函數。

                // 將頂點從模型空間轉換到世界空間float4 worldPos = mul(unity_ObjectToWorld, i.vertex);// Lambert光照模型:計算法線與光線夾角的點積fixed ndotl = saturate(dot(n, l));fixed4 color = _LightColor0 * _MainColor * ndotl;// 疊加4個點光源的光照color.rgb += Shade4PointLights(unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0, // 點光源位置unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,       // 點光源顏色unity_4LightAtten0, worldPos.rgb, n                     // 衰減和位置) * _MainColor;// 疊加環境光照color += unity_AmbientSky;// 使用Unity宏計算陰影衰減系數UNITY_LIGHT_ATTENUATION(shadowmask, i, worldPos.rgb)// 將陰影系數與顏色相乘,應用陰影效果color.rgb *= shadowmask;return color; // 返回最終顏色

這里為什么我們要采取蘭伯特模型而不是更精準的馮模型呢?

?我們根據蘭伯特模型的公式計算出基本的漫反射光照顏色值之后,再疊加四個點光源的效果,這里我們用了Unity內置的Shade4PointLights函數:

?綜上所述,現在我們的漫反射光照顏色值是蘭伯特光照模型和點光源效果的總和,我們再添加一個unity自帶的:

最后再乘以一個Unity自帶的陰影衰落因子就得到了ForwardBase輸出的結果。

然后是我們的ForwardAdd部分:
?

// -------- 額外的Pass:處理其他逐像素燈光的投影 --------
Pass
{// 標簽定義,此Pass用于附加光源,模式為"ForwardAdd"Tags{"LightMode" = "ForwardAdd"}// 混合模式:相加混合Blend One OneCGPROGRAM#pragma vertex vert#pragma fragment frag#pragma multi_compile_fwdadd_fullshadows // 支持多重編譯,包含完整陰影#include "UnityCG.cginc"#include "Lighting.cginc"#include "AutoLight.cginc"// 定義頂點到片段的數據結構,與基礎Pass一致struct v2f{float4 pos : SV_POSITION;float3 normal : TEXCOORD0;float4 vertex : TEXCOORD1;SHADOW_COORDS(2)};fixed4 _MainColor;// 頂點著色器v2f vert (appdata_base v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.normal = v.normal;o.vertex = v.vertex;TRANSFER_SHADOW(o)return o;}// 片段著色器fixed4 frag (v2f i) : SV_Target{// 計算法線和光照方向float3 n = UnityObjectToWorldNormal(i.normal);n = normalize(n);float3 l = WorldSpaceLightDir(i.vertex);l = normalize(l);// 轉換頂點到世界空間float4 worldPos = mul(unity_ObjectToWorld, i.vertex);// Lambert光照計算fixed ndotl = saturate(dot(n, l));fixed4 color = _LightColor0 * _MainColor * ndotl;// 疊加點光源的光照color.rgb += Shade4PointLights(unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,unity_LightColor[0].rgb, unity_LightColor[1].rgb,unity_LightColor[2].rgb, unity_LightColor[3].rgb,unity_4LightAtten0, worldPos.rgb, n) * _MainColor;// 使用陰影宏計算陰影系數UNITY_LIGHT_ATTENUATION(shadowmask, i, worldPos.rgb)// 應用陰影到顏色color.rgb *= shadowmask;return color; // 返回最終顏色}ENDCG
}

我們首先可以看到Blend One One,這是一種混合模式:

除此之外的ForwardAdd的代碼幾乎與ForwardBase的部分一模一樣,我們都只采用蘭伯特光照模型即可(其實具體的渲染模式和內部的光照模型并沒有直接掛鉤,不同的渲染模式主要是負責的職能不同而光照模型則是定義光照計算的方式不同)。

大體效果如圖:

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

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

相關文章

C++ 中 std::tuple 使用詳解

C 中 std::tuple 使用詳解 基本概念 std::tuple 是 C11 引入的模板類&#xff0c;用于打包任意數量、任意類型的值在一起。可看作是類型安全的變長結構體。 #include <tuple>std::tuple<int, std::string, double> t(42, "hello", 3.14);創建 tuple 的…

WebRTC基于網頁的視頻會議,手寫WebRTC流程(html)

WebRTC是web real-time communication網頁及時通信的縮寫&#xff0c;通過javascript就可以實現網頁會話&#xff0c;基于瀏覽器開發出來多媒體應用&#xff0c; 以下是手寫的WEBRTC調用本地攝像頭的html代碼&#xff0c;直接用瀏覽器打開&#xff0c;就可以使用 <!DOCTYPE…

MyBatis 官方子項目詳細說明及表格總結

MyBatis 官方子項目詳細說明及表格總結 1. 核心子項目說明 1.1 mybatis-3 GitHub 鏈接&#xff1a;https://github.com/mybatis/mybatis-3功能&#xff1a; MyBatis 核心框架的源碼&#xff0c;提供 SQL 映射、動態 SQL、緩存、事務管理等核心功能。主要功能&#xff1a; 支持…

【虛幻C++筆記】碰撞檢測

目錄 碰撞檢測參數詳情示例用法 碰撞檢測 顯示名稱中文名稱CSphere Trace By Channel按通道進行球體追蹤UKismetSystemLibrary::SphereTraceSingleSphere Trace By Profile按描述文件進行球體追蹤UKismetSystemLibrary::SphereTraceSingleByProfileSphere Trace For Objects針…

推論階梯——AI與思維模型【81】

一、定義 推論階梯思維模型是一種用于分析和理解人們如何從觀察到的事實,經過一系列的假設、推理和判斷,最終得出結論的思維過程的理論框架。它將這個過程比喻為一個階梯,每一步都建立在前一步的基礎上,逐漸形成一個完整的推論。這個模型幫助我們意識到在思考和決策過程中…

小剛說C語言刷題——1109加密四位數

1.題目描述 某軍事單位用 4位整數來傳遞信息&#xff0c;傳遞之前要求先對這個 4 位數進行加密。加密的方式是每一位都先加上 5然后對 10取余數&#xff0c;再將得到的新數顛倒過來。 例如&#xff1a;原數是 1379 &#xff0c;那么每位加 55對 10 取余數的結果為 6824 &…

云服務器和獨立服務器的區別在哪

在當今數字化的時代&#xff0c;服務器成為了支撐各種業務和應用的重要基石。而在服務器的領域中&#xff0c;云服務器和獨立服務器是兩個備受關注的選項。那么&#xff0c;它們到底有何區別呢&#xff1f; 首先&#xff0c;讓我們來聊聊成本。云服務器通常采用按需付費的模式…

【前端】【業務場景】【面試】在前端開發中,如何優化 SVG(可縮放矢量圖形)的性能,特別是在處理復雜圖形和動畫時

SVG?性能優化&#xff1a;循序漸進 4?步法 目標&#xff1a;先減負 → 再復用 → 后加速 → 最后按場景微調 ①?精簡—把包袱先丟掉 刪除無用元素 隱藏/被遮擋的 <path>、未引用的 <defs> 里漸變、濾鏡。 合并路徑 同填充色或描邊的路徑 ? SVGO / SVGOMG「Mer…

MySQL長事務的隱患:深入剖析與解決方案

MySQL長事務的隱患&#xff1a;深入剖析與解決方案 一、什么是長事務&#xff1f; 在數據庫系統中&#xff0c;長事務(Long Transaction)通常指執行時間超過預期或系統設定閾值的事務。對于MySQL而言&#xff0c;雖然沒有嚴格的時間定義&#xff0c;但一般認為執行時間超過數…

華為AR1200 telnet設置

華為路由配置TELNET登 &#x1f4fa; 啟動TELNET服務 在華為路由器上啟動TELNET服務&#xff0c;執行以下命令&#xff1a; telnet server enable &#x1f511; 配置AAA認證 進入AAA認證配置&#xff0c;創建一個路由器登錄帳號admin123&#xff0c;并設置密碼為huawei123&…

【Token系列】01 | Token不是詞:GPT如何切分語言的最小單元

文章目錄 01 | Token不是詞&#xff1a;GPT如何切分語言的最小單元&#xff1f;一、什么是 Token&#xff1f;二、Token 是怎么來的&#xff1f;——BPE算法原理BPE核心步驟&#xff1a; 三、為什么不直接用詞或字符&#xff1f;四、Token切分的實際影響五、中文Token的特殊性六…

如何快速高效學習Python?

如何快速高效學習Python&#xff1f; How to Fastly and Effectively Learn Python Programming? By JacksonML 1. Python年輕嗎&#xff1f; Python自1991年誕生到現在&#xff0c;已經經歷了三十四年或者更長時間了。畢竟&#xff0c;Python之父 – 吉多范羅蘇姆先生(Gu…

NAT穿透

NAT是 Net Address Traslation的縮寫&#xff0c;即網絡地址轉換 NAT部署在網絡出口的位置。位于內網和公網之間&#xff0c;是連接內挖個主機和公網的橋梁&#xff0c;雙向流量都必須經過NAT&#xff0c;裝有NAT軟件的路由器叫NAT路由器&#xff0c;NAT路由器擁有公網Ip NAT解…

搜索引擎的高級語法

文章目錄 精確搜索&#xff1a;雙引號站內搜索&#xff1a;site通配符搜索&#xff1a;*減號縮小范圍&#xff1a;-文檔搜索&#xff1a;filetypeURL搜索&#xff1a; inurl標題搜索&#xff1a;intitle正文搜索&#xff1a;intext參考鏈接 精確搜索&#xff1a;雙引號 “ ” …

RAG vs 微調:大模型知識更新的最優解之爭

一、技術本質&#xff1a;知識注入的兩條路徑 在大模型應用落地的實踐中&#xff0c;RAG&#xff08;檢索增強生成&#xff09;與微調&#xff08;Fine-tuning&#xff09;已成為知識更新的兩大核心技術路徑。二者的本質差異在于是否對模型參數進行修改&#xff1a; 維度RAG微…

解釋器體系結構風格-筆記

解釋器&#xff08;Interpreter&#xff09;是一種軟件設計模式或體系結構風格&#xff0c;主要用于為語言&#xff08;或表達式&#xff09;定義其語法、語義&#xff0c;并通過解釋器來解析和執行語言中的表達式。解釋器體系結構風格廣泛應用于編程語言、腳本語言、規則引擎、…

瀏覽器f12可以搜索接口的入參 和返回內容

瀏覽器f12可以搜索接口的入參 和返回內容

vue3+element-push 實現input框粘貼圖片或文本,圖片上傳。

vue3element-push 實現input框粘貼圖片或文本&#xff0c;圖片上傳。 <el-inputstyle"height: 100px; width: 100%"paste.capture.prevent"pasting"v-model"textMsg"placeholder"請輸入"/>// 展示上傳的列表--可不要<divsty…

高效使用DeepSeek對“情境+ 對象 +問題“型課題進行開題!

目錄 思路"情境 對象 問題"型 課題選題的類型有哪些呢&#xff1f;這要從課題題目的構成說起。通過對歷年來國家社會科學基金立項項目進行分析&#xff0c;小編發現&#xff0c;課題選題類型非常豐富&#xff0c;但一般是圍繞限定詞、研究對象和研究問題進行不同的組…

cursor改Goland操作習慣

步驟1&#xff1a;設置主題 步驟2&#xff1a;安裝最新go插件 步驟3&#xff1a;安裝最新go版本 需要使用最新版本go1.24.1,設置玩環境變量&#xff0c;需要關閉cursor進程再打開 步驟4&#xff1a;安裝go相關工具 Command Shift P安裝完成后需要把go版本設置回自己項目合…