找回密码
 立即注册
查看: 397|回复: 0

Unity Scriptable Render Pipeline(一)

[复制链接]
发表于 2022-3-29 11:57 | 显示全部楼层 |阅读模式
一、前言

Unity SRP是一组底层API接口,允许C#脚本对渲染管进行调配,其作作用与CommandBuffer类似。但与内置管线相比,暴露了更多的底层接口,使得其可定制化程度更高。官方基于SRP,针对不同性能需求实现了通用渲染管线 (URP)、高清渲染管线 (HDRP) 两种渲染管线,在后续章节中会进行介绍。
本章节会介绍SRP的特性与基础使用,并完成一个简单的自定义管线。Unity使用2020.3.11f,Core RP Library Package使用10.5.1。
1.1 可编程管线

为什么要推出可编程管线?

  • 内置管线性能差:为了兼容多平台,有很多冗余处理,难以充分利用硬件性能
  • 内置管线可扩展性差:虽然内置管线有CommonBuff可以对管线进行微调,但可处理的内容较窄,而且代码维护、阅读不太方便。
可以对哪些内容进行定制?
除了Canvas - Screen Space的UI,其它渲染内容与方式都可以自行控制。具体包括

  • 摄像机渲染顺序、渲染方式、渲染对象
  • 场景对象的剔除、排序、渲染
  • 灯光处理
  • 阴影处理
  • ……
1.2 SRP Batcher

SRP针对相同Shader变体的渲染做了优化处理:SRP Batch。CPU向GPU提交数据后,GPU需要设置渲染状态后进行渲染。SRP Batch针对相同的Shader变体,将这部分渲染数据提交后,只进行一次状态设置,减少了渲染状态的变化。(与Unity的其它Batcher不同,SRP并不会减少DC)


二、创建一个管线

2.1 Package

创建一个内置管线工程,SRP的使用需要Core RP Library Package。


2.2 逻辑入口与资源创建

Unity每帧调用RenderPipeline.Render方法进行画面绘制,这也是处理渲染逻辑的地方。
ScriptableRenderContext是Unity低级图形接口,可以调度渲染命令。存在两种渲染调度方式:ScriptableRenderContext.ExecuteCommandBuffer和ScriptableRenderContext静态方法。在设置完命令后,通过Submit 将命令提交到GPU缓冲区。Unity中的CommandBuffer在之前章节有过介绍
using UnityEngine;
using UnityEngine.Rendering;

public class FirstPipeline : RenderPipeline
{
    public FirstPipeline() { }

    protected override void Render(ScriptableRenderContext context, Camera[] cameras)
    {
        // 创建并调度命令以清除当前渲染目标
        var cmd = new CommandBuffer();
        cmd.ClearRenderTarget(true, true, Color.red);
        context.ExecuteCommandBuffer(cmd);
        cmd.Release();

        // 指示可编程渲染上下文告诉图形 API 执行调度的命令
        context.Submit();
    }
}
RenderPipeline只定义了处理逻辑,但逻辑需要应用到实例资源才能生效。RenderPipelineAsset用于Editor中创建RenderPipeline对应的资源。
using UnityEngine;
using UnityEngine.Rendering;

[CreateAssetMenu(menuName = "Rendering/FirstPipeline")]
public class FirstPipelineAsset : RenderPipelineAsset
{
    protected override RenderPipeline CreatePipeline()
    {
        return new FirstPipeline();
    }
}
2.3 创建管线资源




创建管线资源

创建完管线资源后,可在Editor -> Project Settings -> Graphics中配置,启用自定义管线。由于此时我们只将屏幕设置为了红色,并未处理Camera,因此场景对象不会渲染(Canvas为Screen Space - Overlay不受影响)。


三、管线逻辑

在了解了管线的创建逻辑后,我们来详细处理帧处理方式,在此之前我们需要明确管线需要完成的工作。渲染的流程分为CPU准备数据、GPU设置状态、渲染,SRP处于CPU的数据处理与提交阶段。

  • 场景加速优化:场景对象管理、Camera剔除
  • 渲染顺序:渲染队列(Camera;不透明物体;半透明物体)、排序
  • 渲染内容:灯光、阴影、场景对象、UI
3.1 RenderPipeline



除了处理渲染逻辑Render方法外,RenderPipelineManager还为管线处理生命周期提供了静态接口(需要手动在Render中调用)。
// 外部组件
void Start()
{
    RenderPipelineManager.beginCameraRendering += BeginCameraRendering;
    RenderPipelineManager.beginFrameRendering += BeginFrameRendering;
    RenderPipelineManager.endCameraRendering += EndCameraRendering;
    RenderPipelineManager.endFrameRendering += EndFrameRendering;
}

private void OnDestroy()
{
    RenderPipelineManager.beginCameraRendering -= BeginCameraRendering;
    RenderPipelineManager.beginFrameRendering -= BeginFrameRendering;
    RenderPipelineManager.endCameraRendering -= EndCameraRendering;
    RenderPipelineManager.endFrameRendering -= EndFrameRendering;
}

// 管线调用生命周期方法
protected override void Render(ScriptableRenderContext context, Camera[] cameras)
{
    RenderPipeline.BeginFrameRendering(context, cameras);
    // 创建并调度命令以清除当前渲染目标
    var cmd = new CommandBuffer(){ name = "claer"};
    cmd.ClearRenderTarget(true, true, Color.red);
    context.ExecuteCommandBuffer(cmd);
    cmd.Release();

    // 指示可编程渲染上下文告诉图形 API 执行调度的命令
    context.Submit();
    RenderPipeline.EndFrameRendering(context, cameras);
}

3.2 创建SRP Shader

创建分别创建不透明Shader与半透明Shader,并生成相应材质,用于后续测试使用。


3.3 场景对象渲染

视锥体剔除
此处的剔除是Unity的内置方法,尚不清楚具体的处理方式,也没有接口能够修改剔除逻辑。若需要对剔除算法优化,只能手动管理场景对象。
// 摄像机剔除
camera.TryGetCullingParameters(out var cullingParameters);
var cullingResults = context.Cull(ref cullingParameters);
更新着色器Camera数据
context.SetupCameraProperties(camera);
渲染场景对象
// 不透明对象
RenderCamera(context, camera, "First1", SortingCriteria.CommonOpaque, RenderQueueRange.opaque);

// 半透明对象
RenderCamera(context, camera, "First2", SortingCriteria.CommonTransparent, RenderQueueRange.transparent);


private void RenderCamera(ScriptableRenderContext context, Camera camera, string TagId, SortingCriteria criteria, RenderQueueRange queue)
{
    RenderPipeline.BeginCameraRendering(context, camera);

    // Camera剔除
    camera.TryGetCullingParameters(out var cullingParameters);
    var cullingResults = context.Cull(ref cullingParameters);

    // 基于当前摄像机,更新内置着色器变量的值
    context.SetupCameraProperties(camera);

    // 渲染命令:队列、排序、光照Tag
    ShaderTagId shaderTagId = new ShaderTagId(TagId);
    var sortingSettings = new SortingSettings(camera) { criteria = criteria };
    DrawingSettings drawingSettings = new DrawingSettings(shaderTagId, sortingSettings);
    FilteringSettings filteringSettings = new FilteringSettings(queue);
    context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings);

    // 绘制天空盒
    if (camera.clearFlags == CameraClearFlags.Skybox && RenderSettings.skybox != null && queue != RenderQueueRange.transparent)
    {
        context.DrawSkybox(camera);
    }

    context.Submit();

    RenderPipeline.EndCameraRendering(context, camera);
}
创建两个Cube,一个使用不透明材质,另一个使用半透明材质。此时场景渲染存在4个批次:清空渲染目标;渲染一个不透明Cube;渲染天空盒;渲染半透明Cube。


3.4 Sence视图中显示UI

若Canvas类型为Screen Space,此时UI并不在我们的管线逻辑中,却仍然可以正常显示。但Sence视图下无法显示UI,因为Sence视图是以场景空间绘制UI的。此时需要我们将Game视图中的几何体提交到Sence绘制,并且UI的Shader也替换为自定义Shader,参考默认的UI-Shader逻辑处理。



Sence视图Canvas处理



参考


  • Scriptable Render Pipeline fundamentals
  • Creating a custom render pipeline
  • Scriptable Render Pipeline Batcher
  • SamUncle:关于静态批处理/动态批处理/GPU Instancing /SRP Batcher的详细剖析
  • SRP Core | Core RP Library | 10.5.1
  • Catlike Coding - Custom Pipeline
  • Catlike Coding - Draw Calls

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-9-22 15:49 , Processed in 0.065366 second(s), 23 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表