Unity SRP自定义渲染管线学习1.1:初步搭建

写在前面

要在屏幕上绘制图像,我们得知道几个要素:绘制什么东西(What),何时绘制(When),在屏幕上哪个位置绘制(Where),用什么设置绘制(How)。另外还要把灯光、阴影、透明效果、后处理效果等等都要正确的绘制出来,才能得到最后的一张图像,这也是渲染管线所要做的。

以前Unity只开放了很小部分供我们对绘制进行设置,Unity2018开始,Unity支持自定义渲染管线(Scriptable Render Pipeline),虽然还是要依赖Unity的一些基础方法,比如调用绘制天空盒,剔除等,但这样我们可以自己对绘制进行更多的设置。Unity 2018提供了两个实验性的自定义渲染管线:轻量级渲染管线和高清渲染管线。在Unity 2019中轻量级渲染管线成为正式功能,并且在2019.3中改名为通用渲染管线。
在这里插入图片描述

通用渲染管线的目的是替换现在用的旧的默认管线,顾名思义,就是希望这个渲染管线能够满足大部分的需求,并且呢,也容易去自己修改一部分内容。

我们这里主要是从0开始自己定义渲染管线,并不是使用Unity提供的管线。

软件环境

我使用的Unity版本:Unity2020.3.10f1

自定义渲染管线

工程设置

因为我们是自定义渲染管线,所以不要选Unity的渲染管线,选择3D
在这里插入图片描述

打开设置,将色彩空间切换为线性空间
在这里插入图片描述

然后我们像下面随便创建一些物体,使用不同的材质,Unlit/Color,Standard(Mode设置Transparent),Unlit/Transparent
在这里插入图片描述

创建渲染管线

新建脚本(因为直接上的都是最终代码,有些地方直接贴上去可能会报错,一步一步做的话,可以先注掉报错的部分)
在这里插入图片描述

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);
        }
    }
}

在这里插入图片描述

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();
    }
}

然后创建我们的渲染管线资源
在这里插入图片描述
在这里插入图片描述

完成创建后,我们需要在ProjectSettings-Graphics中设置我们的渲染管线,设置后我们会发现整个设置页面都变了,很多设置都消失了。另外由于我们的渲染管线中还没有写任何的渲染设置,因此现在什么东西都不会被绘制了,无论是在Scene视图还是Game视图,甚至FrameDebugger中也是什么东西都没有了。这就是渲染管线的真正开始,从0开始自定义我们的渲染管线
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

绘制天空盒

我们先来绘制下天空盒,很简单的操作
关键代码

context.DrawSkybox(camera);  //添加绘制Skybox的命令

context.Submit();  //提交绘制命令,进行绘制

然后我们就能看到天空盒了,并且在FrameDebuger中可以看到绘制的DrawCall
在这里插入图片描述
在这里插入图片描述
但是这个时候当我们旋转相机时,画面并不会发生变化,这是因为我们没有将相机的旋转这些属性应用到Shader中
关键代码

//将相机的属性设置到绘制命令中,比如相机的位置和旋转,透视还是正交投影等,这会决定视图矩阵和投影矩阵
        //就是Shader中所使用的unity_MatrixVP, View Matrix, Projection Matrix
        //我们可以在FrameDebuger中的ShaderProperties中看到unity_MatrixVP
        //如果我们不进行这项设置,unity_MatrixVP都是一样的,所以当我们旋转相机时,相机看到的东西不会发生变化
        context.SetupCameraProperties(camera);

另外,我们可以通过添加BufferName来让Profile和FrameDebuger中相应的部分显示我们想要的名称,这样便于分析
关键代码

   const string bufferName = "Render Camera";
    CommandBuffer buffer = new CommandBuffer
    {  //创建实例时可以这样直接初始化
        name = bufferName,
    };

 buffer.BeginSample(SampleName);     
buffer.EndSample(SampleName);

在这里插入图片描述

ClearRenderTarget

我们在每一次绘制之前,都应该清除一些必要的数据,这样以保证正确的绘制
关键代码

   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  //我们用的是线性空间,所以当要使用背景颜色时,需要将其转化为线性空间的颜色
        );

要注意ClearRenderTarget的调用位置,不然会使用内置Shader绘制的方法来清除,这样子性能不好
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Cull

通过Cull来确定是否绘制
关键代码

    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;
    }

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>