时间:2025-08-11 11:08
人气:
作者:admin
应用阶段最后是CPU向GPU提交需要渲染的数据。通常数据会被复制到显存中,然后设置渲染参数,最后调用渲染接口。PC中是这样的,但是移动设备一般没有单独的显存。使用内存为GPU服务。他们使用同一内存地址。除非要读/写这段内存内容才会复制出一份调整CPU和GPU之间协作。
【从UnityURP开始探索游戏渲染】专栏-直达
模型数据主要从网格资源(Mesh)中获取,包含以下核心属性:
由建模工具(如Blender/Maya)导出时生成,随模型文件(.fbx/.obj)导入Unity。
程序化网格通过Mesh类API动态设置(如mesh.vertices, mesh.uv)
变换矩阵
矩阵数据由CPU计算并传递给GPU:
Transform组件(位置/旋转/缩放)计算得出。
Camera组件的位姿(位置/朝向/上方向)生成。
Field of View (FOV):视角范围Near/Far Clipping Planes:近远裁剪平面Aspect Ratio:屏幕宽高比。MVP = P × V × M| 矩阵类型 | 计算者 | 存储位置(GPU端) | 访问方式(Shader) |
|---|---|---|---|
| 模型矩阵 (M) | Transform组件 (CPU计算) | unity_ObjectToWorld |
UNITY_MATRIX_M |
| 视图矩阵 (V) | 摄像机组件 (CPU计算) | unity_MatrixV |
UNITY_MATRIX_V |
| 投影矩阵 (P) | 摄像机投影参数 (CPU计算) | unity_MatrixP |
UNITY_MATRIX_P |
| MVP矩阵 | Shader运行时组合 | 无独立存储 | mul(UNITY_MATRIX_VP, mul(UNITY_MATRIX_M, pos)) |
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组件。UniversalAdditionalLightData)配置。Material实例定义。_MainTex),从纹理资源加载。应用阶段通过SetPassCall设置渲染状态(Shader/材质),并通过DrawCall提交图元列表
ScriptableRenderer类
UniversalRenderer.cs中,负责管理URP的默认渲染流程。EnqueuePass方法将渲染Pass(如DrawObjectsPass)加入队列。CommandBuffer类
CommandBufferPool.Get获取实例,录制渲染指令。csharp
cmd.SetRenderTarget()// 绑定渲染目标
cmd.SetGlobalTexture()// 设置全局纹理
cmd.SetViewProjectionMatrices()// 设置VP矩阵
Material与Shader
Material.SetPass方法设置,触发底层SetPassCall。ShaderData类管理着色器变体(Variant)的切换。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
| 功能 | 脚本文件 | 核心方法 |
|---|---|---|
| 渲染流程控制 | 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渲染管线的应用阶段,渲染命令队列包含一系列图形命令,主要用于调度和执⾏渲染操作,例如清除缓冲区、绘制几何体、设置材质和着色器参数、处理光源阴影,以及执行后处理效果。
这些命令通过ScriptableRenderContext接口进行管理,该接口作为C#代码与Unity底层图形引擎的桥梁,确保命令按序列化顺序提交GPU处理。队列内容包括:
DrawMesh或DrawProcedural)。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列表管理。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队列
}
}
ShaderCompiler解析ShaderLab代码,将Cull、ZTest、Blend等指令转换为底层渲染状态标识符。ShaderData结构中,包含渲染状态变体(Variant)和材质属性。CommandBuffer录制指令(如cmd.SetRenderTarget、cmd.SetGlobalDepthBias),通过ScriptableRenderContext.Submit提交到GPU。RenderStateBlock.cullMode,通过DrawingSettings传递给DrawRenderers调用。DepthState结构(含ZWrite、ZTest)配置,最终写入GPU深度缓冲区。BlendState管理(含BlendOp、SrcFactor等参数),绑定到渲染管线状态。UniversalRenderer在AddRenderPasses阶段收集所有Pass的渲染状态,合并到RenderStateBlock。MaterialPropertyBlock动态覆盖材质属性(如运行时修改_ZWrite)。| 功能 | 脚本/类 | 核心方法 | 数据流向 |
|---|---|---|---|
| Shader解析 | ShaderCompiler |
CompileShader |
ShaderLab → ShaderData |
| 状态录制 | CommandBuffer |
SetRenderState |
CPU → GPU指令队列 |
| Pass执行 | DrawObjectsPass |
Execute |
通过DrawingSettings传递状态 |
| 动态修改 | MaterialPropertyBlock |
SetFloat/SetInt |
运行时覆盖Shader参数 |
hlsl
// ShaderLab中声明深度测试
SubShader {
Pass {
ZWrite On
ZTest LEqual
}
}
运行时读取:通过Material.GetInt("_ZWrite")获取状态。
动态修改:
csharp
var block = new MaterialPropertyBlock();
block.SetInt("_ZWrite", 0);// 禁用深度写入
renderer.SetPropertyBlock(block);
【从UnityURP开始探索游戏渲染】专栏-直达
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,????)