戈森莉 发表于 2025-9-26 19:06:04

【渲染流水线】[应用阶段]-[渲染命令队列]以UnityURP为例

应用阶段最后是CPU向GPU提交需要渲染的数据。通常数据会被复制到显存中,然后设置渲染参数,最后调用渲染接口。PC中是这样的,但是移动设备一般没有单独的显存。使用内存为GPU服务。他们使用同一内存地址。除非要读/写这段内存内容才会复制出一份调整CPU和GPU之间协作。
【从UnityURP开始探索游戏渲染】专栏-直达
渲染状态:


[*]一连串开关或方法,以及方法的地址指向(阶段中的各种可配置的阶段等都是在这里)。SetPassCall。例如是否开启混合、用哪个纹理、哪个顶点着色器、是否背面剔除等,在Unity中则是ShaderLab语法规则中规定的各种标签。
渲染指令:


[*]调用具体渲染的对象。drawcall是一个渲染指令,这个指令仅指向一连串图元(点线面 网格拆分后的状态),并不会包含任何其他材质信息。每个状态前都伴随着一连串渲染状态设置,所以渲染命令队列中,渲染状态和渲染指令是交替出现。
渲染命令队列:


[*]其中包含渲染状态、渲染指令的缓冲区。CPU向缓冲区放入指令,GPU执行指令。CPU发送渲染状态后,CPU需要控制总线将数据从内存搬运到显存,搬运过程耗费大量时间。drawcall多了后会导致大量内存搬运,运行速度下降。
打包数据

模型信息

模型数据主要从网格资源(Mesh)中获取,包含以下核心属性:

[*]‌顶点坐标‌描述模型局部空间的顶点位置坐标(x, y, z)。
[*]‌法线信息‌定义顶点朝向,用于光照计算和表面平滑度。
[*]‌UV信息‌二维纹理映射坐标(u, v),范围,控制贴图在模型表面的分布。
[*]‌切线向量‌与法线配合构建切线空间,用于法线贴图等高级渲染效果。
[*]‌顶点颜色‌存储逐顶点颜色值,可用于特殊着色效果。
[*]‌索引列表‌定义顶点连接顺序,优化绘制效率(减少重复顶点)


[*]‌数据来源‌:

[*]由建模工具(如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_P‌MVP矩阵 ‌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))



灯光、材质参数


[*]灯光参数‌

[*]‌光源属性‌:位置、颜色、强度、衰减等,源自场景中的Light组件。
[*]‌阴影参数‌:阴影强度、分辨率,通过URP光源设置(如UniversalAdditionalLightData)配置。

[*]‌材质数据‌

[*]‌Shader与材质属性‌:漫反射颜色、高光强度等,由Material实例定义。
[*]‌纹理贴图‌:通过材质绑定(如_MainTex),从纹理资源加载。

‌数据传递流程‌:

应用阶段通过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.csAddRenderPasses, Execute‌指令录制‌CommandBuffer.csClear, DrawMesh, Blit‌材质状态管理‌Material.csSetPass, SetShaderPassEnabled‌数据提交‌ScriptableRenderContext.csSubmit, 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解析‌ShaderCompilerCompileShaderShaderLab → ShaderData‌状态录制‌CommandBufferSetRenderStateCPU → GPU指令队列‌Pass执行‌DrawObjectsPassExecute通过DrawingSettings传递状态‌动态修改‌MaterialPropertyBlockSetFloat/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开始探索游戏渲染】专栏-直达

(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 【渲染流水线】[应用阶段]-[渲染命令队列]以UnityURP为例