可编程的渲染管线(SRP)

作者:追风剑情 发布于:2022-5-19 15:01 分类:Shader

[官方文档] 可编程渲染管线简介
[知乎] https://zhuanlan.zhihu.com/p/430789702
[知乎] https://zhuanlan.zhihu.com/p/378828898

本篇文章中的代码均转自 https://zhuanlan.zhihu.com/p/430789702https://zhuanlan.zhihu.com/p/378828898

可编程的渲染管线(Scriptable Render Pipeline, SRP),SRP 是一个轻量级的 API 层,允许使用 C# 脚本来调度和配置渲染命令。SRP 将这些命令传递给 Unity 的低级图形架构,后者随后将指令发送给图形 API。


自定义渲染管线所需步骤:
1、创建自定义渲染管线资源(RenderPipelineAsset),用于存储有关 SRP 的配置数据。
2、创建自定义渲染管线实例(RenderPipeline)。
3、自定义 Shader LightMode。
4、设置渲染管线。
[Edit]->Project Settings->Graphics->Scriptable Render Pipeline Settings

创建自定义渲染管线资源

using UnityEngine;
using UnityEngine.Rendering;
/// <summary>
/// 自定义渲染管线资源
/// 右击Asset面板,Create/Rendering/Custom Render Pipeline 创建管线资源
/// </summary>
[CreateAssetMenu(menuName = "Rendering/Custom Render Pipeline")]
public class CustomRenderPipelineAsset : RenderPipelineAsset
{
    protected override RenderPipeline CreatePipeline()
    {
        return new CustomRenderPipeline();
    }
}  

创建自定义渲染管线

using UnityEngine;
using UnityEngine.Rendering;
/// <summary>
/// 自定义渲染管线
/// </summary>
public class CustomRenderPipeline : RenderPipeline
{
    CameraRenderer renderer = new CameraRenderer();

    /// <summary>
    /// Unity会在每一帧调用此方法
    /// </summary>
    protected override void Render(ScriptableRenderContext context, Camera[] cameras)
    {
        foreach (var cam in cameras)
        {
            renderer.Render(context, cam);
        }
    }
}  

摄像机渲染器类

CameraRenderer.cs 处理 Game 窗口的渲染

using UnityEngine;
using UnityEngine.Rendering;
/// <summary>
/// 摄像机渲染器(处理Game窗口渲染)
/// </summary>
public partial class CameraRenderer
{
    public ScriptableRenderContext context;
    public Camera camera;
    const string bufferName = "Render Camera";
    CommandBuffer commandBuffer = new CommandBuffer { name = bufferName };

    public void Render(ScriptableRenderContext context, Camera cam)
    {
        this.context = context;
        this.camera = cam;
        PrepareBuffer();
        //当Canvas的Render Mode设置为Screen Space - Camera,绘制UGUI
        PrepareForSceneWindow();
        //物体可见性判断
        if (!Cull())
            return;
        Setup();
        DrawVisibleGeometry();
        DrawUnSupportShader();
        DrawGizmos();
        Submit();
    }

    //记录更加详细的Cull信息
    CullingResults cullRes;
    // 判断像机的裁剪参数是否能正确获取到
    bool Cull()
    {
        ScriptableCullingParameters p;
        if (camera.TryGetCullingParameters(out p))
        {
            cullRes = context.Cull(ref p);
            return true;
        }
        return false;
    }

    // 应用摄像机属性
    void Setup()
    {
        //必须在ClearRenderTarget前调用SetupCameraProperties,否则Scene窗口将显示黑屏
        context.SetupCameraProperties(camera);
        // 当前相机的ClearFlags
        CameraClearFlags flags = camera.clearFlags;
        //ClearRenderTarget(是否清除深度缓冲数据, 是否清除颜色缓冲数据, 背景颜色)
        //现在的传参方式是为了兼容多像机的情况
        //第三个参数要注意当前使用的是线性空间(linear)还是伽马空间(gamma)
        commandBuffer.ClearRenderTarget(flags <= CameraClearFlags.Depth, flags == CameraClearFlags.Color, flags == CameraClearFlags.Color ? camera.backgroundColor.linear : Color.clear);
        //使用Profiler Samples,方便在Profiler和Frame Debug中查看性能数据。
        commandBuffer.BeginSample(SampleName);
        ExecuteCommand();
    }

    // 执行命令
    void ExecuteCommand()
    {
        context.ExecuteCommandBuffer(commandBuffer);
        commandBuffer.Clear();
    }

    //自动找到LightMode为CustomLightModeTag的Pass进行渲染
    //Pass{ Tags { "LightMode" = "CustomLightModeTag"} }
    //static ShaderTagId unlitShaderTagId = new ShaderTagId("CustomLightModeTag");

    //SRPDefaultUnlit 支持显示UGUI
    static ShaderTagId unlitShaderTagId = new ShaderTagId("SRPDefaultUnlit");

    // 绘制所有摄像机可见的几何图形
    void DrawVisibleGeometry()
    {
        var sortingSettings = new SortingSettings(camera)
        {
            // 不透明对象的典型排序(从前到后)
            criteria = SortingCriteria.CommonOpaque
        };
        // 绘图设置
        var drawSettings = new DrawingSettings(unlitShaderTagId, sortingSettings);
        // 过滤设置,先绘制不透明物体
        var filteringSettings = new FilteringSettings(RenderQueueRange.opaque);
        context.DrawRenderers(cullRes, ref drawSettings, ref filteringSettings);
        //绘制天空盒
        context.DrawSkybox(camera);
        // 最后绘制透明物体,排序规则改为透明排序
        sortingSettings.criteria = SortingCriteria.CommonTransparent;
        drawSettings.sortingSettings = sortingSettings;
        // 绘制透明队列
        filteringSettings.renderQueueRange = RenderQueueRange.transparent;
        context.DrawRenderers(
           cullRes, ref drawSettings, ref filteringSettings
        );
    }

    // 提交绘制数据
    void Submit()
    {
        commandBuffer.EndSample(SampleName);
        ExecuteCommand();
        context.Submit();
    }
}  

CameraRenderer.Editor.cs 处理 Scene 窗口的渲染

using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Profiling;
/// <summary>
/// 摄像机渲染器(处理Scene窗口渲染)
/// </summary>
partial class CameraRenderer
{
    //分部方法签名
    partial void PrepareBuffer();
    partial void PrepareForSceneWindow();
    partial void DrawGizmos();
    partial void DrawUnSupportShader();

#if UNITY_EDITOR
	string SampleName { get; set; }	
	partial void PrepareBuffer()
    {
        Profiler.BeginSample("Editor Only");
        commandBuffer.name = SampleName = camera.name;
        Profiler.EndSample();
    }
#else
    //非编辑器条件下不需要进行可视化区分,固定SampleName就好
	const string SampleName = bufferName;
#endif

    // 只针对编辑器
#if UNITY_EDITOR
    partial void PrepareForSceneWindow()
    {
        // 当Canvas的Render Mode设置为Screen Space - Camera
        // 当相机类型为SceneView类型,显式通过调用ScriptableRenderContext将UI添加到World Geometry中。
        // 使用相机作为参数
        if (camera.cameraType == CameraType.SceneView)
        {
            ScriptableRenderContext.EmitWorldGeometryForSceneView(camera);
        }
    }

    partial void DrawGizmos()
    {
        if (Handles.ShouldRenderGizmos())
        {
            //当选中某个物体时,在Scene窗口中绘制它的线框。
            context.DrawGizmos(camera, GizmoSubset.PreImageEffects);
            context.DrawGizmos(camera, GizmoSubset.PostImageEffects);
        }
    }

    //{Tag LightMode="这个值"}
    //ShaderTagId(LightMode)
    static ShaderTagId[] legacyShaderTagIds = {
        new ShaderTagId("Always"),
        new ShaderTagId("ForwardBase"),
        new ShaderTagId("PrepassBase"),
        new ShaderTagId("Vertex"),
        new ShaderTagId("VertexLMRGBM"),
        new ShaderTagId("VertexLM")
    };
    static Material _errorMat;

    partial void DrawUnSupportShader()
    {
        if (!_errorMat)
        {
            //让物体显示为纯粉红色
            _errorMat = new Material(Shader.Find("Hidden/InternalErrorShader"));
        }
        var drawingSettings = new DrawingSettings(
            legacyShaderTagIds[0], new SortingSettings(camera))
        {
            overrideMaterial = _errorMat
        };
        var filterSettings = FilteringSettings.defaultValue;
        for (int i = 1; i < legacyShaderTagIds.Length; i++)
        {
            drawingSettings.SetShaderPassName(i, legacyShaderTagIds[i]);
        }
        context.DrawRenderers(cullRes, ref drawingSettings, ref filterSettings);
    }
#endif
}  

自定义Shader

Shader "Custom/UnlitColor"
{
    SubShader
    {
        Pass
        {
            //创建ShaderTagId对象时要使用LightMode的值
            Tags { "LightMode" = "SRPDefaultUnlit"}

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

        float4x4 unity_MatrixVP;
            float4x4 unity_ObjectToWorld;

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };

            v2f vert(appdata v)
            {
                v2f o;
                float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
                o.vertex = mul(unity_MatrixVP, worldPos);
                return o;
            }

            float4 frag(v2f i) : SV_TARGET
            {
                return float4(0.5,1,0.5,1);
            }
            ENDHLSL
        }
    }
}  

设置渲染管线

[Edit]->Project Settings->Graphics->Scriptable Render Pipeline Settings

11111.png

工程截图

22222.png

33333.png

44444.png

标签: Shader

Powered by emlog  蜀ICP备18021003号-1   sitemap

川公网安备 51019002001593号