当前位置:   article > 正文

Unity SRP自定义渲染管线学习1.2:初步绘制_unity自定义渲染管线

unity自定义渲染管线

绘制物体

绘制物体,包括不透明的物体,透明物体,再加上之前的天空盒
Camera

void DrawVisibleGeometry()
    {
        //我们需要将不透明物体和透明物体分开绘制
        //如果我们直接先绘制所有的物体,然后再绘制天空盒,我们就会看到对于透明的物体,如果其对于相机视线后面没有不透明的物体的话,就会被天空球给遮挡住
        //我们的透明shader没有写入深度缓冲,绘制天空盒时,在这个片元上如果没有不透明的物体写入过深度的话,天空盒的绘制就会直接覆盖掉
        //所以我们的绘制顺序是先不透明物体,再绘制天空盒,最后绘制透明物体
        //先进行不透明物体的绘制
        var sortingSettings = new SortingSettings(camera) //相机的透明排序取决于使用的是正交还是基于距离的排序
        {
            // 不给定排序规则的话,绘制排序是随机混乱的
            // 这个排序是从前往后的,因为对于后面的不透明物体,当检测到其的深度大于缓冲区的深度时,说明被挡住了,可以直接丢弃不绘制
            // 而如果从后往前的话,就会需要不断的覆盖绘制,性能不如从前往后
            criteria = SortingCriteria.CommonOpaque,
        };
        var drawSettings = new DrawingSettings(unlitShaderTagId, sortingSettings);  //DrawingSettings主要用于对物体的渲染顺序和使用哪个Shader Pass
        var filteringSettings = new FilteringSettings(RenderQueueRange.opaque);  //FilteringSettings主要决定哪些物体渲染,哪些不渲染根据LayerMask,RenderQueue,sortingLayer等
        //在Cull中我们得到了cullingResults,因此我们知道了哪些物体是需要绘制的
        context.DrawRenderers(cullingResults, ref drawSettings, ref filteringSettings);
        context.DrawSkybox(camera);  //添加绘制Skybox的命令
        // 再进行透明物体的绘制
        // 这个绘制顺序是从后往前的,与不透明的绘制顺序是相反的
        // 不透明的物体并不写入深度缓冲,因此从前往后并没有性能更优
        // 而且透明物体之间需要进行混合,从后往前才能得到正确的混合
        // 但事实上并不能完全保证正确,因为排序是基于整个物体的位置的,但是对于复杂的大型物体间,可能会有部分穿插,有部分在前,有部分在后,这样子就会得到错误的效果
        sortingSettings.criteria = SortingCriteria.CommonTransparent;
        drawSettings.sortingSettings = sortingSettings;
        filteringSettings.renderQueueRange = RenderQueueRange.transparent;
        context.DrawRenderers(cullingResults, ref drawSettings, ref filteringSettings);
    }


    //使用Unity默认的一些Shader绘制出使用了不支持的Shader的物体
    partial void DrawUnsupportedShaders()  //如果不改成分部方法的定义和实现声明,打包就会出错,因为这部分代码是仅在编辑器下使用的
    {
        if (errorMaterial == null)
        {
            errorMaterial = new Material(Shader.Find("Hidden/InternalErrorShader"));
        }
        //我们将不支持的Shader用Unity默认的设置直接绘制出来
        //没有设置好Shader的属性之前,绘制出来的物体是黑色的
        var drawSettings = new DrawingSettings(legacyShaderTagIds[0], new SortingSettings(camera))
        {
            overrideMaterial = errorMaterial,  //使用内置错误材质来绘制所有不支持的Shader的物体
        }
        ;
        for (int i = 1; i < legacyShaderTagIds.Length; i++)
        {
            drawSettings.SetShaderPassName(i, legacyShaderTagIds[i]);
        }
        var filteringSettings = FilteringSettings.defaultValue;
        context.DrawRenderers(cullingResults, ref drawSettings, ref filteringSettings);
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

没有对绘制排序前
在这里插入图片描述

排序后
在这里插入图片描述

将不透明物体和透明物体分开渲染
在这里插入图片描述

先用默认Shader渲染不支持的Shader
在这里插入图片描述

用内置的错误材质在编辑器下绘制
在这里插入图片描述

分部代码,让代码更清晰,且可以让Editor代码不会出现打包错误
在这里插入图片描述

绘制Gizmos

    partial void DrawGizmos()
    {
        if (Handles.ShouldRenderGizmos())  //获取Gizmos是否开启
        {
            context.DrawGizmos(camera, GizmoSubset.PreImageEffects);
            context.DrawGizmos(camera, GizmoSubset.PostImageEffects);
        }
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

绘制Gizmos前
在这里插入图片描述
绘制Gizmos后
在这里插入图片描述

UI

如果是Overlay的UI,则不受我们的管线影响
在这里插入图片描述
改成ScreenCamera或者WorldSpaceCamera后
在这里插入图片描述
但是Scene中没有UI
在这里插入图片描述

添加绘制Scene视图的代码后

    partial void PrepareForSceneWindow()
    {
        if (camera.cameraType == CameraType.SceneView)
        {
            //绘制Scene视图中的UI
            ScriptableRenderContext.EmitWorldGeometryForSceneView(camera);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在这里插入图片描述

多相机

添加新的相机
在这里插入图片描述

两个相机的绘制在FrameDebuger中都混在了一起
在这里插入图片描述
我们对每个相机单独进行Sample

   partial void PrepareBuffer()
    {
        //我们将Buffer的名称修改为相机名称,这样子就可以在FrameDebuger中看到相机对应的渲染在自己的组中,查看会比较清晰
        Profiler.BeginSample("Editor Only");  //这里会产生GC,会混下我们性能分析,所以直接标明是Editor Only
        buffer.name = SampleName = camera.name;
        Profiler.EndSample();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在这里插入图片描述
有多余的GC
在这里插入图片描述
在这里插入图片描述
把Editor下的GC单独分开
在这里插入图片描述

相机的ClearFlags

根据相机的ClearFlags的设置去调用buffer.ClearRenderTarget,主要就是决定是否清除颜色缓冲,深度缓冲
在这里插入图片描述

var flags = camera.clearFlags;
        //ClearRenderTarget需要在SetupCameraProperties之后进行Execute,不然Clear的方式会变成Draw GL(可以在FrameDebuger中看到)
        //Draw GL是使用Hidden/InternalClear Shader绘制了一个全屏的Quad来达到清除的效果,这种方式不是性能最好的
        //ClearRenderTarget需要在BeginSample之前,不然FrameDebuger中会被多一次嵌套在"Render Camera"中
        buffer.ClearRenderTarget(
            flags != CameraClearFlags.Nothing,
            flags == CameraClearFlags.Color,   //为什么可以不用管Skybox时的清理颜色缓冲?因为当选择Skybox时,会清理深度缓冲,且需要绘制Skybox,没有深度缓冲此时一定会覆盖掉之前的颜色,相当于清理了颜色缓冲
            flags == CameraClearFlags.Color ? camera.backgroundColor.linear : Color.clear  //我们用的是线性空间,所以当要使用背景颜色时,需要将其转化为线性空间的颜色
        );

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Solid Color
在这里插入图片描述
Don’t Clear
在这里插入图片描述
改变Viewport后的Skybox
在这里插入图片描述
改变Viewport后的SolidColor
在这里插入图片描述
改变Viewport后的Depth Only
在这里插入图片描述
改变Viewport后的Don’t Clear
在这里插入图片描述
没有改变Viewport之前是直接用Clear的方式
在这里插入图片描述
改变Viewport后,由于需要将清理的范围限定在Viewport中,并非整个屏幕,因此使用Shader绘制的方式清理,将范围限定
在这里插入图片描述

参考

本文主要学习自:https://catlikecoding.com/unity/tutorials/custom-srp/custom-render-pipeline/
catlikecoding是大神的博客,里面有很多教程,膜拜大神,感恩大神。

具体代码

CustomRenderPipeline.cs

using UnityEngine;
using UnityEngine.Rendering;
public class CustomRenderPipeline : RenderPipeline
{
    CameraRenderer cameraRenderer = new CameraRenderer();
    //每一帧渲染调用
    protected override void Render(ScriptableRenderContext context, Camera[] cameras)
    {
        foreach(var camera in cameras)
        {
            cameraRenderer.Render(context, camera);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

CustomRenderPipelineAsset.cs

using UnityEngine;
using UnityEngine.Rendering;  //需要使用UnityEngine.Rendering命名空间
//用于存储自定义渲染管线的设置
[CreateAssetMenu(menuName = "Rendering/Custom Render Pipeline")]
public class CustomRenderPipelineAsset : RenderPipelineAsset  //需要继承自RenderPipelineAsset
{
    protected override RenderPipeline CreatePipeline()
    {
        return new CustomRenderPipeline();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

CameraRenderer.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
//用于进行单个相机的画面渲染
public partial class CameraRenderer
{
    static ShaderTagId unlitShaderTagId = new ShaderTagId("SRPDefaultUnlit");
    ScriptableRenderContext context;
    Camera camera;
    CullingResults cullingResults;
    const string bufferName = "Render Camera";
    CommandBuffer buffer = new CommandBuffer
    {  //创建实例时可以这样直接初始化
        name = bufferName,
    };
    public void Render(ScriptableRenderContext context, Camera camera)
    {
        this.context = context;
        this.camera = camera;
        PrepareBuffer();
        PrepareForSceneWindow();
        if (!Cull())
            return;
        Setup();
        DrawVisibleGeometry();
        DrawUnsupportedShaders();
        DrawGizmos();
        Submit();
    }
    bool Cull()
    {
        //返回值表示cullingParameters是否有效,比如当相机viewport rectangle宽高被设置为0,0,无效的裁剪平面设置等
        //因此返回值能代表这个相机是否需要绘制
        if (camera.TryGetCullingParameters(out var cullingParameters))
        {
            //我们会发现在这里,对ScriptableCullingParameters的操作基本都是out和ref,这是因为ScriptableCullingParameters比较大,这样子可以优化
            cullingResults = context.Cull(ref cullingParameters);
            return true;
        }
        return false;
    }
    void Setup()
    {
        //将相机的属性设置到绘制命令中,比如相机的位置和旋转,透视还是正交投影等,这会决定视图矩阵和投影矩阵
        //就是Shader中所使用的unity_MatrixVP, View Matrix, Projection Matrix
        //我们可以在FrameDebuger中的ShaderProperties中看到unity_MatrixVP
        //如果我们不进行这项设置,unity_MatrixVP都是一样的,所以当我们旋转相机时,相机看到的东西不会发生变化
        context.SetupCameraProperties(camera);
        var flags = camera.clearFlags;
        //ClearRenderTarget需要在SetupCameraProperties之后进行Execute,不然Clear的方式会变成Draw GL(可以在FrameDebuger中看到)
        //Draw GL是使用Hidden/InternalClear Shader绘制了一个全屏的Quad来达到清除的效果,这种方式不是性能最好的
        //ClearRenderTarget需要在BeginSample之前,不然FrameDebuger中会被多一次嵌套在"Render Camera"中
        buffer.ClearRenderTarget(
            flags != CameraClearFlags.Nothing,
            flags == CameraClearFlags.Color,   //我们的Skybox是在最后绘制的
            flags == CameraClearFlags.Color ? camera.backgroundColor.linear : Color.clear  //我们用的是线性空间,所以当要使用背景颜色时,需要将其转化为线性空间的颜色
        );
        buffer.BeginSample(SampleName);
        ExecuteBuffer();  //Execute后,Buffer中的命令才会加入到context中
    }
    void DrawVisibleGeometry()
    {
        //我们需要将不透明物体和透明物体分开绘制
        //如果我们直接先绘制所有的物体,然后再绘制天空盒,我们就会看到对于透明的物体,如果其对于相机视线后面没有不透明的物体的话,就会被天空球给遮挡住
        //我们的透明shader没有写入深度缓冲,绘制天空盒时,在这个片元上如果没有不透明的物体写入过深度的话,天空盒的绘制就会直接覆盖掉
        //所以我们的绘制顺序是先不透明物体,再绘制天空盒,最后绘制透明物体
        //先进行不透明物体的绘制
        var sortingSettings = new SortingSettings(camera) //相机的透明排序取决于使用的是正交还是基于距离的排序
        {
            // 不给定排序规则的话,绘制排序是随机混乱的
            // 这个排序是从前往后的,因为对于后面的不透明物体,当检测到其的深度大于缓冲区的深度时,说明被挡住了,可以直接丢弃不绘制
            // 而如果从后往前的话,就会需要不断的覆盖绘制,性能不如从前往后
            criteria = SortingCriteria.CommonOpaque,
        };
        var drawSettings = new DrawingSettings(unlitShaderTagId, sortingSettings);  //DrawingSettings主要用于对物体的渲染顺序和使用哪个Shader Pass
        var filteringSettings = new FilteringSettings(RenderQueueRange.opaque);  //FilteringSettings主要决定哪些物体渲染,哪些不渲染根据LayerMask,RenderQueue,sortingLayer等
        //在Cull中我们得到了cullingResults,因此我们知道了哪些物体是需要绘制的
        context.DrawRenderers(cullingResults, ref drawSettings, ref filteringSettings);
        context.DrawSkybox(camera);  //添加绘制Skybox的命令
        // 再进行透明物体的绘制
        // 这个绘制顺序是从后往前的,与不透明的绘制顺序是相反的
        // 不透明的物体并不写入深度缓冲,因此从前往后并没有性能更优
        // 而且透明物体之间需要进行混合,从后往前才能得到正确的混合
        // 但事实上并不能完全保证正确,因为排序是基于整个物体的位置的,但是对于复杂的大型物体间,可能会有部分穿插,有部分在前,有部分在后,这样子就会得到错误的效果
        sortingSettings.criteria = SortingCriteria.CommonTransparent;
        drawSettings.sortingSettings = sortingSettings;
        filteringSettings.renderQueueRange = RenderQueueRange.transparent;
        context.DrawRenderers(cullingResults, ref drawSettings, ref filteringSettings);
    }
    void Submit()
    {
        buffer.EndSample(SampleName);
        ExecuteBuffer();
        context.Submit();  //提交绘制命令,进行绘制
    }
    void ExecuteBuffer()
    {
        context.ExecuteCommandBuffer(buffer);
        buffer.Clear();
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103

CameraRenderer.Editor.cs

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine.Profiling;
using UnityEngine;
using UnityEngine.Rendering;
//用于进行单个相机的画面渲染
partial class CameraRenderer
{
    //为分部方法定义声明,这样子编译时如果没有找到实现声明的方法的话,就会剔除掉对这个方法的调用
    //使用这种方法可以比较好的避免Build出错,再来查错
    partial void PrepareBuffer();
    partial void PrepareForSceneWindow();
    partial void DrawUnsupportedShaders();
    partial void DrawGizmos();
#if UNITY_EDITOR
    string SampleName { get; set; }
    static Material errorMaterial; //用于绘制不支持的shader
    //Unity默认的所有的Shader Pass的Tag
    static ShaderTagId[] legacyShaderTagIds = {
        new ShaderTagId("Always"),
        new ShaderTagId("ForwardBase"),
        new ShaderTagId("PrepassBase"),
        new ShaderTagId("Vertex"),
        new ShaderTagId("VertexLMRGBM"),
        new ShaderTagId("VertexLM"),
    };
    partial void PrepareBuffer()
    {
        //我们将Buffer的名称修改为相机名称,这样子就可以在FrameDebuger中看到相机对应的渲染在自己的组中,查看会比较清晰
        Profiler.BeginSample("Editor Only");  //这里会产生GC,会混下我们性能分析,所以直接标明是Editor Only
        buffer.name = SampleName = camera.name;
        Profiler.EndSample();
    }
    partial void PrepareForSceneWindow()
    {
        if (camera.cameraType == CameraType.SceneView)
        {
            //绘制Scene视图中的UI
            ScriptableRenderContext.EmitWorldGeometryForSceneView(camera);
        }
    }
    //使用Unity默认的一些Shader绘制出使用了不支持的Shader的物体
    partial void DrawUnsupportedShaders()  //如果不改成分部方法的定义和实现声明,打包就会出错,因为这部分代码是仅在编辑器下使用的
    {
        if (errorMaterial == null)
        {
            errorMaterial = new Material(Shader.Find("Hidden/InternalErrorShader"));
        }
        //我们将不支持的Shader用Unity默认的设置直接绘制出来
        //没有设置好Shader的属性之前,绘制出来的物体是黑色的
        var drawSettings = new DrawingSettings(legacyShaderTagIds[0], new SortingSettings(camera))
        {
            overrideMaterial = errorMaterial,  //使用内置错误材质来绘制所有不支持的Shader的物体
        }
        ;
        for (int i = 1; i < legacyShaderTagIds.Length; i++)
        {
            drawSettings.SetShaderPassName(i, legacyShaderTagIds[i]);
        }
        var filteringSettings = FilteringSettings.defaultValue;
        context.DrawRenderers(cullingResults, ref drawSettings, ref filteringSettings);
    }
    partial void DrawGizmos()
    {
        if (Handles.ShouldRenderGizmos())  //获取Gizmos是否开启
        {
            context.DrawGizmos(camera, GizmoSubset.PreImageEffects);
            context.DrawGizmos(camera, GizmoSubset.PostImageEffects);
        }
    }
#else
    const string SampleName = bufferName;
#endif
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/102767
推荐阅读
相关标签
  

闽ICP备14008679号