Version: 2023.1
在自定义渲染管线中创建渲染管线资源和渲染管线实例
摄像机

在自定义渲染管线中创建简单渲染循环

渲染循环是指在单一帧中发生的所有渲染操作的术语。本页面包含有关在基于 Unity 可编程渲染管线的自定义渲染管线中创建简单渲染循环的信息。

本页面上的代码示例演示使用可编程渲染管线的基本原则。可以使用此信息构建自己的自定义可编程渲染管线,或了解 Unity 的预构建可编程渲染管线如何工作。

准备项目

开始为渲染循环编写代码之前,必须准备好项目。

步骤如下所示:

  1. 创建与 SRP 兼容的着色器
  2. 创建一个或多个要渲染的游戏对象
  3. 创建自定义 SRP 的基本结构
  4. 可选:如果计划扩展简单自定义 SRP 以添加更复杂的功能,请安装 SRP Core 包。SRP Core 包中包含 SRP Core 着色器库(可以用于使着色器与 SRP Batcher 兼容)以及用于常见操作的实用程序函数。有关更多信息,请参阅 SRP Core 包文档

创建与 SRP 兼容的着色器

在可编程渲染管线中,使用 LightMode 通道标签确定如何绘制几何体。有关通道标签的更多信息,请参阅 ShaderLab:向通道分配标签

此任务演示如何创建非常简单的无光照 Shader 对象,其 LightMode 通道标签值为 ExampleLightModeTag

  1. 在项目中创建一个新着色器资源。有关创建着色器资源的说明,请参阅着色器资源
  2. 在 Project 视图中,双击着色器资源以在文本编辑器中打开着色器源代码。
  3. 将现有代码替换为以下内容:
// 这定义一个与自定义可编程渲染管线兼容的简单无光照 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 兼容着色器的游戏对象。

  1. 在 Unity 项目中创建一个新材质资源。有关说明,请参阅材质
  2. 将着色器资源分配给材质资源。有关说明,请参阅材质
  3. 在场景中创建一个立方体。有关说明,请参阅原始对象
  4. 将材质分配给它。有关说明,请参阅材质

创建自定义 SRP 的基本结构

准备的最后阶段是创建自定义 SRP 所需的基本源文件,并告知 Unity 开始使用自定义 SRP 进行渲染。

  1. 按照创建渲染管线实例和渲染管线资源中的说明,创建一个继承自 RenderPipeline 的类和一个兼容渲染管线资源。
  2. Set the active Render Pipeline Asset, following the instructions in How to get, set, and configure the active render pipeline. Unity will begin rendering using the custom SRP immediately, which means that your Scene view and Game view will be blank until you add code to your custom SRP.

创建渲染循环

在简单渲染循环中,基本操作有:

  • 清除渲染目标,这意味着移除在最后一帧期间绘制的几何体。
  • 剔除,这意味着过滤掉对摄像机不可见的几何体。
  • 绘制,这意味着向 GPU 告知要绘制的几何体以及如何进行绘制。

清除渲染目标

清除意味着移除在最后一帧期间绘制的内容。渲染目标通常是屏幕;但是,也可以渲染到纹理以创建“画中画”效果。这些示例演示如何渲染到屏幕,这是 Unity 的默认行为。

要清除可编程渲染管线中的渲染目标,请执行以下操作:

  1. 使用 Clear 命令配置 CommandBuffer
  2. CommandBuffer 添加到 ScriptableRenderContext 上的命令队列;为此,请调用 ScriptableRenderContext.ExecuteCommandBuffer
  3. 指示图形 API 执行 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();
    }
}

剔除

剔除是过滤掉对摄像机不可见的几何体的过程。

要在可编程渲染管线中进行剔除,请执行以下操作:

  1. 使用有关摄像机的数据填充 ScriptableCullingParameters 结构;为此,请调用 Camera.TryGetCullingParameters
  2. 可选:手动更新 ScriptableCullingParameters 结构的值。
  3. 调用 ScriptableRenderContext.Cull,并将结果存储在一个 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 中进行绘制,请执行以下操作:

  1. 如上所述执行剔除操作,并将结果存储在 CullingResults 结构中。
  2. 创建和配置 FilteringSettings 结构,它描述如何过滤剔除结果。
  3. Create and configure a DrawingSettings struct, which describes which geometry to draw and how to draw it.
  4. 可选:默认情况下,Unity 基于 Shader 对象设置渲染状态。如果要覆盖即将绘制的部分或所有几何体的渲染状态,可以使用 RenderStateBlock 结构执行此操作。
  5. 调用 ScriptableRenderContext.DrawRenderers,并将创建的结构作为参数进行传递。Unity 根据设置绘制过滤后的几何体集。

此示例代码基于上面的示例进行构建,演示如何清除渲染目标,执行剔除操作,然后绘制生成的几何体:

/* 
这是自定义可编程渲染管线的简化示例。
它演示基本渲染循环的工作方式。
它演示最清晰的工作流程,而不是最高效的运行时性能。
*/

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();
        }
    }
}
在自定义渲染管线中创建渲染管线资源和渲染管线实例
摄像机