渲染循环是指在单一帧中发生的所有渲染操作的术语。本页面包含有关在基于 Unity 可编程渲染管线的自定义渲染管线中创建简单渲染循环的信息。
本页面上的代码示例演示使用可编程渲染管线的基本原则。可以使用此信息构建自己的自定义可编程渲染管线,或了解 Unity 的预构建可编程渲染管线如何工作。
开始为渲染循环编写代码之前,必须准备好项目。
步骤如下所示:
在可编程渲染管线中,使用 LightMode
通道标签确定如何绘制几何体。有关通道标签的更多信息,请参阅 ShaderLab:向通道分配标签。
此任务演示如何创建非常简单的无光照 Shader 对象,其 LightMode 通道标签值为 ExampleLightModeTag
。
// 这定义一个与自定义可编程渲染管线兼容的简单无光照 Shader 对象。
// 它应用硬编码颜色,并演示 LightMode 通道标签的使用。
// 它不与 SRP Batcher 兼容。
Shader "Examples/SimpleUnlitColor"
{
SubShader
{
Pass
{
// LightMode 通道标签的值必须与 ScriptableRenderContext.DrawRenderers 中的 ShaderTagId 匹配
Tags { "LightMode" = "ExampleLightModeTag"}
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
float4x4 unity_MatrixVP;
float4x4 unity_ObjectToWorld;
struct Attributes
{
float4 positionOS : POSITION;
};
struct Varyings
{
float4 positionCS : SV_POSITION;
};
Varyings vert (Attributes IN)
{
Varyings OUT;
float4 worldPos = mul(unity_ObjectToWorld, IN.positionOS);
OUT.positionCS = mul(unity_MatrixVP, worldPos);
return OUT;
}
float4 frag (Varyings IN) : SV_TARGET
{
return float4(0.5,1,0.5,1);
}
ENDHLSL
}
}
}
要测试渲染循环是否可正常工作,必须创建要渲染的内容。此任务演示如何在场景中放置使用在上一个任务中创建的 SRP 兼容着色器的游戏对象。
准备的最后阶段是创建自定义 SRP 所需的基本源文件,并告知 Unity 开始使用自定义 SRP 进行渲染。
RenderPipeline
的类和一个兼容渲染管线资源。在简单渲染循环中,基本操作有:
清除意味着移除在最后一帧期间绘制的内容。渲染目标通常是屏幕;但是,也可以渲染到纹理以创建“画中画”效果。这些示例演示如何渲染到屏幕,这是 Unity 的默认行为。
要清除可编程渲染管线中的渲染目标,请执行以下操作:
Clear
命令配置 CommandBuffer
。CommandBuffer
添加到 ScriptableRenderContext
上的命令队列;为此,请调用 ScriptableRenderContext.ExecuteCommandBuffer。ScriptableRenderContext
上的命令队列;为此,请调用 ScriptableRenderContext.Submit。与所有可编程渲染管线操作一样,使用 RenderPipeline.Render 方法作为此代码的入口点。此示例代码演示如何执行此操作:
/*
这是自定义可编程渲染管线的简化示例。
它演示基本渲染循环的工作方式。
它演示最清晰的工作流程,而不是最高效的运行时性能。
*/
using UnityEngine;
using UnityEngine.Rendering;
public class ExampleRenderPipeline : RenderPipeline {
public ExampleRenderPipeline() {
}
protected override void Render (ScriptableRenderContext context, Camera[] cameras) {
// 创建并调度命令以清除当前渲染目标
var cmd = new CommandBuffer();
cmd.ClearRenderTarget(true, true, Color.black);
context.ExecuteCommandBuffer(cmd);
cmd.Release();
// 指示图形 API 执行所有调度的命令
context.Submit();
}
}
剔除是过滤掉对摄像机不可见的几何体的过程。
要在可编程渲染管线中进行剔除,请执行以下操作:
ScriptableCullingParameters
结构的值。CullingResults
结构中。此示例代码扩展了上面的示例,演示如何清除渲染目标,然后执行剔除操作:
/*
这是自定义可编程渲染管线的简化示例。
它演示基本渲染循环的工作方式。
它演示最清晰的工作流程,而不是最高效的运行时性能。
*/
using UnityEngine;
using UnityEngine.Rendering;
public class ExampleRenderPipeline : RenderPipeline {
public ExampleRenderPipeline() {
}
protected override void Render (ScriptableRenderContext context, Camera[] cameras) {
// 创建并调度命令以清除当前渲染目标
var cmd = new CommandBuffer();
cmd.ClearRenderTarget(true, true, Color.black);
context.ExecuteCommandBuffer(cmd);
cmd.Release();
// 遍历所有摄像机
foreach (Camera camera in cameras)
{
// 从当前摄像机获取剔除参数
camera.TryGetCullingParameters(out var cullingParameters);
// 使用剔除参数执行剔除操作,并存储结果
var cullingResults = context.Cull(ref cullingParameters);
}
// 指示图形 API 执行所有调度的命令
context.Submit();
}
}
绘制是指示图形 API 使用给定设置绘制一组给定几何体的过程。
要在 SRP 中进行绘制,请执行以下操作:
CullingResults
结构中。此示例代码基于上面的示例进行构建,演示如何清除渲染目标,执行剔除操作,然后绘制生成的几何体:
/*
这是自定义可编程渲染管线的简化示例。
它演示基本渲染循环的工作方式。
它演示最清晰的工作流程,而不是最高效的运行时性能。
*/
using UnityEngine;
using UnityEngine.Rendering;
public class ExampleRenderPipeline : RenderPipeline {
public ExampleRenderPipeline() {
}
protected override void Render (ScriptableRenderContext context, Camera[] cameras) {
// 创建并调度命令以清除当前渲染目标
var cmd = new CommandBuffer();
cmd.ClearRenderTarget(true, true, Color.black);
context.ExecuteCommandBuffer(cmd);
cmd.Release();
// 遍历所有摄像机
foreach (Camera camera in cameras)
{
// 从当前摄像机获取剔除参数
camera.TryGetCullingParameters(out var cullingParameters);
// 使用剔除参数执行剔除操作,并存储结果
var cullingResults = context.Cull(ref cullingParameters);
// 基于当前摄像机,更新内置着色器变量的值
context.SetupCameraProperties(camera);
// 基于 LightMode 通道标签值,向 Unity 告知要绘制的几何体
ShaderTagId shaderTagId = new ShaderTagId("ExampleLightModeTag");
// 基于当前摄像机,向 Unity 告知如何对几何体进行排序
var sortingSettings = new SortingSettings(camera);
// 创建描述要绘制的几何体以及绘制方式的 DrawingSettings 结构
DrawingSettings drawingSettings = new DrawingSettings(shaderTagId, sortingSettings);
// 告知 Unity 如何过滤剔除结果,以进一步指定要绘制的几何体
// 使用 FilteringSettings.defaultValue 可指定不进行过滤
FilteringSettings filteringSettings = FilteringSettings.defaultValue;
// 基于定义的设置,调度命令绘制几何体
context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings);
// 在需要时调度命令绘制天空盒
if (camera.clearFlags == CameraClearFlags.Skybox && RenderSettings.skybox != null)
{
context.DrawSkybox(camera);
}
// 指示图形 API 执行所有调度的命令
context.Submit();
}
}
}