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

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

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

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

准备项目

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

步骤如下所示:

  1. Create an SRP-compatible Unity shader.
  2. 创建一个或多个要渲染的游戏对象
  3. 创建自定义 SRP 的基本结构
  4. Optional: If you plan to extend your simple custom SRP to add more complex functionality, install the SRP Core package. The SRP Core package includes the SRP Core shader library (which you can use to make your Unity shaders SRP Batcher compatible), and utility functions for common operations. For more information, see the SRP Core package documentation.

Creating an SRP-compatible Unity shader

In the Scriptable Render Pipeline, you use the LightMode Pass tag in a Unity shader to determine how to draw geometry. For more information on assigning Pass tags to Unity shaders, see ShaderLab: assigning tags to a Pass.

This task shows you how to create a very simple unlit Unity shader with a LightMode Pass tag value of ExampleLightModeTag.

  1. Create a new Unity shader asset in your project. For instructions on creating a Unity shader asset, see Unity shader assets.
  2. In your Project view, double click the Unity shader asset to open the Unity shader source code in a text editor.
  3. Replace the existing shader code with the following:
// This is a simple unlit Unity shader that is compatible with a custom Scriptable Render Pipeline.
// It applies a hardcoded color, and demonstrates the use of the LightMode Pass tag.
// It is not compatible with SRP Batcher.

Shader "Examples/SimpleUnlitColor"
{
    SubShader
    {
        Pass
        {
            // The value of the LightMode Pass tag must match the ShaderTagId in ScriptableRenderContext.DrawRenderers
            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
        }
    }
}

创建要渲染的游戏对象

In order to test that your render loop works, you must create something to render. This task shows you how to put GameObjects in your scene that use the SRP-compatible Unity shader that you created in the previous task.

  1. Create a new material asset in your Unity project. For instructions on creating a material asset, see Materials.
  2. Assign the Unity shader to the material asset. For instructions on assigning a Unity shader to a material asset, see Materials.
  3. Create a cube in your scene. For instructions on creating a cube in your scene, see Primitive objects.
  4. Assign the material to it. For instructions on assigning a material to a GameObject, see Materials.

创建自定义 SRP 的基本结构

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

  1. 按照创建渲染管线实例和渲染管线资源中的说明,创建一个继承自 RenderPipeline 的类和一个兼容渲染管线资源。
  2. 按照设置激活的渲染管线资源中的说明,设置激活的渲染管线资源。Unity 会立即开始使用自定义 SRP 进行渲染,这意味着 Scene 视图和 Game 视图会为空白,直到向自定义 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. 创建和配置 DrawingSettings 结构,它描述要绘制的几何体以及如何进行绘制。
  4. Optional: By default, Unity sets the render state based on the Unity shader. If you want to override the render state for some or all of the geometry that you are about to draw, you can use a RenderStateBlock struct to do this.
  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();
        }
    }
}
在自定义渲染管线中创建渲染管线资源和渲染管线实例
摄像机