應用階段最後是CPU向GPU提交需要渲染的數據。通常數據會被複制到顯存中,然後設置渲染參數,最後調用渲染接口。PC中是這樣的,但是移動設備一般沒有單獨的顯存。使用內存為GPU服務。他們使用同一內存地址。除非要讀/寫這段內存內容才會複製出一份調整CPU和GPU之間協作。
【從UnityURP開始探索遊戲渲染】專欄-直達
渲染狀態:
- 一連串開關或方法,以及方法的地址指向(階段中的各種可配置的階段等都是在這裏)。SetPassCall。例如是否開啓混合、用哪個紋理、哪個頂點着色器、是否背面剔除等,在Unity中則是ShaderLab語法規則中規定的各種標籤。
渲染指令:
- 調用具體渲染的對象。drawcall是一個渲染指令,這個指令僅指向一連串圖元(點線面 網格拆分後的狀態),並不會包含任何其他材質信息。每個狀態前都伴隨着一連串渲染狀態設置,所以渲染命令隊列中,渲染狀態和渲染指令是交替出現。
渲染命令隊列:
- 其中包含渲染狀態、渲染指令的緩衝區。CPU向緩衝區放入指令,GPU執行指令。CPU發送渲染狀態後,CPU需要控制總線將數據從內存搬運到顯存,搬運過程耗費大量時間。drawcall多了後會導致大量內存搬運,運行速度下降。
打包數據
模型信息
模型數據主要從網格資源(Mesh)中獲取,包含以下核心屬性:
- 頂點座標描述模型局部空間的頂點位置座標(x, y, z)。
- 法線信息定義頂點朝向,用於光照計算和表面平滑度。
- UV信息二維紋理映射座標(u, v),範圍[0,1],控制貼圖在模型表面的分佈。
- 切線向量與法線配合構建切線空間,用於法線貼圖等高級渲染效果。
- 頂點顏色存儲逐頂點顏色值,可用於特殊着色效果。
- 索引列表定義頂點連接順序,優化繪製效率(減少重複頂點)
-
數據來源:
- 由建模工具(如Blender/Maya)導出時生成,隨模型文件(.fbx/.obj)導入Unity。
- 程序化網格通過
Mesh類API動態設置(如mesh.vertices,mesh.uv) -
變換矩陣
矩陣數據由CPU計算並傳遞給GPU:
-
模型矩陣 Model Matrix(M) 模型局部座標→世界座標,由物體的
Transform組件(位置/旋轉/縮放)計算得出。- 計算順序:縮放 → 旋轉 → 平移(SRT)
-
視圖矩陣 View Matrix(V) 世界座標→攝像機座標,基於
Camera組件的位姿(位置/朝向/上方向)生成。- 計算原理:先逆平移(攝像機到原點),再逆旋轉(對齊座標軸)
-
投影矩陣 Projection Matrix(P) 攝像機座標→齊次裁剪座標,通過相機參數計算:
- 參數來源:FOV、近/遠裁剪面、寬高比
- 透視投影(近大遠小)或正交投影(等比例縮放)
Field of View (FOV):視角範圍Near/Far Clipping Planes:近遠裁剪平面Aspect Ratio:屏幕寬高比。
- MVP矩陣最終變換矩陣:
MVP = P × V × M -
計算主體與存儲位置
矩陣類型 計算者 存儲位置(GPU端) 訪問方式(Shader) 模型矩陣 (M) Transform組件 (CPU計算) unity_ObjectToWorldUNITY_MATRIX_M視圖矩陣 (V) 攝像機組件 (CPU計算) unity_MatrixVUNITY_MATRIX_V投影矩陣 (P) 攝像機投影參數 (CPU計算) unity_MatrixPUNITY_MATRIX_PMVP矩陣 Shader運行時組合 無獨立存儲 mul(UNITY_MATRIX_VP, mul(UNITY_MATRIX_M, pos)) -
計算時機
- CPU端:每幀渲染前更新(物體Transform或攝像機移動時)。
- GPU端:通過
UNITY_MATRIX_VP(視圖投影矩陣)與UNITY_MATRIX_M(模型矩陣)在頂點着色器動態組合
-
優化機制
- URP預計算
VP矩陣(視圖投影聯合矩陣),減少GPU計算量。 -
使用
UnityObjectToClipPos內置函數直接完成MVP變換:hlsl float4 clipPos = UnityObjectToClipPos(v.vertex); // 內部實現:mul(UNITY_MATRIX_VP, mul(UNITY_MATRIX_M, v.vertex))
- URP預計算
-
燈光、材質參數
-
燈光參數
- 光源屬性:位置、顏色、強度、衰減等,源自場景中的
Light組件。 - 陰影參數:陰影強度、分辨率,通過URP光源設置(如
UniversalAdditionalLightData)配置。
- 光源屬性:位置、顏色、強度、衰減等,源自場景中的
-
材質數據
- Shader與材質屬性:漫反射顏色、高光強度等,由
Material實例定義。 - 紋理貼圖:通過材質綁定(如
_MainTex),從紋理資源加載。
- Shader與材質屬性:漫反射顏色、高光強度等,由
數據傳遞流程:
應用階段通過SetPassCall設置渲染狀態(Shader/材質),並通過DrawCall提交圖元列表
Batch:
- 把數據加載到顯存,設置渲染狀態,CPU調用GPU渲染的過程稱之為一個Batch。
Unity URP渲染管線中的渲染狀態和渲染命令的實現
一、渲染狀態設置(SetPassCall)
-
ScriptableRenderer類
- 位於
UniversalRenderer.cs中,負責管理URP的默認渲染流程。 - 調用
EnqueuePass方法將渲染Pass(如DrawObjectsPass)加入隊列。
- 位於
-
CommandBuffer類
- 通過
CommandBufferPool.Get獲取實例,錄製渲染指令。 -
關鍵方法:
csharp cmd.SetRenderTarget()// 綁定渲染目標 cmd.SetGlobalTexture()// 設置全局紋理 cmd.SetViewProjectionMatrices()// 設置VP矩陣
- 通過
-
Material與Shader
- 材質狀態通過
Material.SetPass方法設置,觸發底層SetPassCall。 - URP通過
ShaderData類管理着色器變體(Variant)的切換。
- 材質狀態通過
二、圖元提交(DrawCall)
-
ScriptableRenderContext類
- 核心方法
Submit提交所有錄製的CommandBuffer到GPU。 -
調用鏈:
csharp context.ExecuteCommandBuffer(cmd);// 執行指令 context.DrawRenderers()// 觸發DrawCall
- 核心方法
-
DrawingSettings與FilteringSettings
-
在
DrawObjectsPass.Execute中配置:csharp var drawSettings = new DrawingSettings(...);// 指定Shader Pass和排序var filterSettings = new FilteringSettings(...);// 設置渲染隊列和層級 context.DrawRenderers(...);// 最終提交
-
-
Graphics.DrawMesh
- 直接提交網格數據的備選API,繞過URP流程但效率較低。
三、關鍵腳本位置
| 功能 | 腳本文件 | 核心方法 |
|---|---|---|
| 渲染流程控制 | UniversalRenderer.cs |
AddRenderPasses, Execute |
| 指令錄製 | CommandBuffer.cs |
Clear, DrawMesh, Blit |
| 材質狀態管理 | Material.cs |
SetPass, SetShaderPassEnabled |
| 數據提交 | ScriptableRenderContext.cs |
Submit, DrawRenderers |
四、執行流程示例
csharp
// 在ScriptableRenderPass中實現
public override void Execute(ScriptableRenderContext context, ref RenderingData data) {
CommandBuffer cmd = CommandBufferPool.Get("CustomPass");
cmd.SetRenderTarget(...);// SetPassCall
cmd.DrawMesh(...);// DrawCall
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
Unity URP渲染管線中的應用階段渲染命令隊列及渲染隊列實現
在Unity URP渲染管線的應用階段,渲染命令隊列包含一系列圖形命令,主要用於調度和執⾏渲染操作,例如清除緩衝區、繪製幾何體、設置材質和着色器參數、處理光源陰影,以及執行後處理效果。
這些命令通過ScriptableRenderContext接口進行管理,該接口作為C#代碼與Unity底層圖形引擎的橋樑,確保命令按序列化順序提交GPU處理。隊列內容包括:
- 緩衝區清除命令(如顏色緩衝和深度緩衝)。
- 幾何體繪製命令(調用
DrawMesh或DrawProcedural)。 - 狀態設置命令(如設置視口、混合模式)。
- 陰影貼圖生成命令(針對動態光源)。
- 後處理Pass(如抗鋸齒或景深應用)。這些命令在每幀的渲染循環中被動態生成和執行,以支持前向渲染策略和性能優化。
URP中的渲染隊列實現主要由ScriptableRenderPass類完成,它定義了Pass的執行順序和具體渲染邏輯。具體腳本流程如下:
渲染隊列管理:
- 渲染隊列(如
RenderQueueRange.opaque或RenderQueueRange.transparent)在ScriptableRenderPass的構造函數中指定,通過字段如renderPassEvent控制Pass的執行時機(例如在相機渲染前或後)。 -
例如,一個基本的Pass腳本會繼承自
ScriptableRenderPass,並在其Configure方法中設置隊列優先級:這裏,Execute方法包含具體命令隊列的實現,使用CommandBuffer來錄製命令(如cmd.ClearRenderTarget),並通過ScriptableRenderContext提交。csharp public class CustomRenderPass : ScriptableRenderPass { public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) { // 設置隊列範圍為不透明對象 renderPassEvent = RenderPassEvent.BeforeRenderingOpaques; } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { // 執行命令,如繪製或清除 CommandBuffer cmd = CommandBufferPool.Get(); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } }
腳本集成:
- 在
ScriptableRenderer(如UniversalRenderer)中,渲染隊列通過m_ActiveRenderPassQueue列表管理。 - Setup階段調用
AddRenderPasses方法收集所有關聯的ScriptableRenderPass實例(來自RendererFeature),並按事件順序排序;Execute階段遍歷列表執行每個Pass的Execute方法。 - 例如,
UniversalRenderPipeline.Render方法驅動整個流程:此腳本位於URP核心程序集(如UniversalRenderPipeline.cs),依賴於UniversalRenderPipelineAsset提供配置
csharp
protected override void Render(ScriptableRenderContext context, List<Camera> cameras)
{
// 排序相機並逐個處理foreach (var camera in cameras)
{
var renderer = cameraData.renderer;
renderer.Execute(context, ref renderingData);// 執行Pass隊列
}
}
在Unity URP中,ShaderLab配置的渲染狀態(如剔除、深度測試、混合模式等)的處理流程
一、狀態讀取與存儲機制
ShaderLab解析階段
- URP通過
ShaderCompiler解析ShaderLab代碼,將Cull、ZTest、Blend等指令轉換為底層渲染狀態標識符。 -
解析結果存儲在
ShaderData結構中,包含渲染狀態變體(Variant)和材質屬性。GPU狀態設置階段
- 運行時由
CommandBuffer錄製指令(如cmd.SetRenderTarget、cmd.SetGlobalDepthBias),通過ScriptableRenderContext.Submit提交到GPU。 -
關鍵存儲位置:
- 剔除模式:存於
RenderStateBlock.cullMode,通過DrawingSettings傳遞給DrawRenderers調用。 - 深度測試/寫入:通過
DepthState結構(含ZWrite、ZTest)配置,最終寫入GPU深度緩衝區。 - 混合模式:由
BlendState管理(含BlendOp、SrcFactor等參數),綁定到渲染管線狀態。
URP運行時管理
- 剔除模式:存於
UniversalRenderer在AddRenderPasses階段收集所有Pass的渲染狀態,合併到RenderStateBlock。- 通過
MaterialPropertyBlock動態覆蓋材質屬性(如運行時修改_ZWrite)。
二、關鍵腳本與調用鏈
| 功能 | 腳本/類 | 核心方法 | 數據流向 |
|---|---|---|---|
| Shader解析 | ShaderCompiler |
CompileShader |
ShaderLab → ShaderData |
| 狀態錄製 | CommandBuffer |
SetRenderState |
CPU → GPU指令隊列 |
| Pass執行 | DrawObjectsPass |
Execute |
通過DrawingSettings傳遞狀態 |
| 動態修改 | MaterialPropertyBlock |
SetFloat/SetInt |
運行時覆蓋Shader參數 |
三、使用示例(URP中動態修改深度測試)
hlsl
// ShaderLab中聲明深度測試
SubShader {
Pass {
ZWrite On
ZTest LEqual
}
}
- 運行時讀取:通過
Material.GetInt("_ZWrite")獲取狀態。 -
動態修改:
csharp var block = new MaterialPropertyBlock(); block.SetInt("_ZWrite", 0);// 禁用深度寫入 renderer.SetPropertyBlock(block);
【從UnityURP開始探索遊戲渲染】專欄-直達
(歡迎點贊留言探討,更多人加入進來能更加完善這個探索的過程,🙏)