联系电话:18652913986
行业资讯位置:首页 > 行业资讯
自定义管线1
发表时间:2023-11-14     阅读次数:     字体:【
1
新的渲染管线


为了渲染各种各样的物件,Unity 必须确定必须绘制什么形状、在哪里、何时以及使用什么设置,这可能会非常复杂,具体取决于涉及的效果数量。灯光、阴影、透明度、图像效果、体积效果等都必须以正确的顺序处理才能获得最终图像,这就是渲染管道的作用。

在此之前,Unity 仅支持几种内置的渲染方式。Unity 2018 引入了可编写脚本的渲染管道(简称 RP),使我们可以做任何我们想做的事情,同时仍然能够依赖 Unity 来执行剔除等基本操作。Unity 2018 还添加了使用这种新方法制作的两个实验性 RP:LWRP 和HDRP。LWRP在 Unity 2019.3 中正式更名为URP。

URP 注定会取代当前的旧 RP 作为默认值。现在看来它是一款最适合的 RP,而且也很容易定制。本系列不是自定义 RP,而是从头开始创建整个 RP。

本教程通过使用前向渲染绘制未照亮的形状为最小 RP 奠定了基础。绘制完成以后,我们就可以在后面的教程中扩展我们的管道,添加照明、阴影、不同的渲染方法和更高级的功能。


1.1
项目设置



在Unity 2022.3.5f1或更高版本中创建一个新的3D项目。我们将创建自己的管道,因此不要选择任何 RP 项目模板。项目打开后,你可以转到包管理器并删除所有不需要的包。我们只会使用本教程中的Unity UI包用于尝试绘制 UI,所以可以保留这个包。

我们将专门在线性颜色空间中工作,但 Unity 2019.2 仍然使用伽玛空间作为默认值。通过以下方式进入播放器设置Edit / Project Settings进而Player,然后切换Color Space在下面Other Settings节至Linear

图片
颜色空间设置为线性

用standard、unlit的不透明和透明材质来设置默认场景。Unlit/Transparent只有一张贴图,所以用下面的uv球体贴图。

图片uv球体alpha贴图,黑色背景

我在测试场景中放置了一些立方体,它们都是不透明的。红色的使用的材质是Standard,而绿色和黄色的使用材质Unlit/Color。蓝色球体使用Standard并且Rendering Mode设置Transparent,而白色球体则使Unlit/Transparent。

图片
测试场景

1.2
Pipline Assets



目前,Unity 使用默认渲染管道。要将其替换为自定义渲染管道,我们首先必须为其创建资产类型。这里将使用与 Unity 用于URP 大致相同的文件夹结构。创建一个Custom RP资产文件夹和Runtime子文件夹。在其中放置一个新的 C# 脚本作为该CustomRenderPipelineAsset类型。

图片
文件夹结构

资产类型必须从UnityEngine.Rendering namespace的RenderPiplineAsset中继承

    using System.Collections.Generic;using UnityEngine;using UnityEngine.Rendering;publicclassCustomRenderPipelineAsset : RenderPipelineAsset {}

    RP 资源的主要目的是为 Unity 提供一种方法来获取负责渲染的管道对象实例。资产本身只是一个句柄和一个存储设置的地方。我们还没有任何设置,所以我们所要做的就是为 Unity 提供一种获取管道对象实例的方法。这是通过重写抽象CreatePipeline方法来完成的,该方法应该返回一个RenderPipeline实例。但我们还没有定义自定义 RP 类型,因此首先返回null.CreatePipeline方法是使用访问修饰符定义的 protected,这意味着只有定义该方法的类(RenderPipelineAsset也就是扩展该方法的类)才能访问该方法。

      protectedoverrideRenderPipeline CreatePipeline () {return null;}

      现在我们需要将这种类型的资产添加到我们的项目中。

        [CreateAssetMenu]publicclassCustomRenderPipelineAsset : RenderPipelineAsset { … }

        这会在Asset/Create菜单下面创建一个空的,让我们整理一下并把它放在一个Rendering子菜单下。通过将menuName属性的属性设置为Rendering/Custom Render Pipeline来做到这一点。
        可以在属性类型之后直接在圆括号内设置此属性。

          [CreateAssetMenu(menuName = "Rendering/Custom Render Pipeline")]
          publicclassCustomRenderPipelineAsset : RenderPipelineAsset { … }

          使用新菜单项将资产添加到项目中,然后转到Graphics项目设置并在下面选择它Scriptable Render Pipeline Settings。

          图片
          选择自定义RP

          替换默认 RP 后让项目发生了一些变化。首先,许多选项从图形设置中隐藏了,这在信息面板中可以看到。其次,我们禁用了默认 RP,但没有提供有效的替换,因此不再渲染任何内容。游戏窗口、场景窗口和材质预览不再起作用。如果你通过Window / Analysis / Frame Debugger打开帧调试器,你会看到游戏窗口中确实没有绘制任何内容。

          1.3
          渲染管线实例



          创建一个CustomRenderPipeline类并将其作为脚本文件CustomRenderPipelineAsset放在相同的文件夹下. 这就是我们的资产返回的 RP 实例所使用的类型,因此它必须扩展RenderPipeline

            using UnityEngine;
            using UnityEngine.Rendering;publicclassCustomRenderPipeline : RenderPipeline {}

            RenderPipeline定义了一个受保护的抽象Render方法,我们必须重写该方法才能创建具体的管道。它有两个参数:一个是ScriptableRenderContex,一个是camera数组,并设置函数为空。

              protectedoverridevoid Render (

              ScriptableRenderContext context, Camera[] cameras) {}

              此方法是为自定义 SRP 定义的入口点,但由于相机阵列参数需要为每一帧分配内存,因此引入了具有列表参数的替代方法。
              我们可以在 Unity 2022 中使用该版本,但仍然必须保留另一个版本,因为它被声明为抽象的,即使它不会被使用。
              注意后面的分析器屏幕截图包括相机阵列的旧分配。

                protectedoverridevoid Render (
                ScriptableRenderContext context, Listcameras) { }

                让 CustomRenderPipeline : RenderPipeline返回一个新的CustomRenderPipeline实例,这会为我们提供一个有效且实用的管道,尽管它还没有渲染任何东西。

                  protectedoverrideRenderPipeline CreatePipeline () {
                  returnnewCustomRenderPipeline();}

                  2
                  渲染



                  Unity 在每帧调用渲染管线实例的 Render 方法。它传递一个上下文结构,该结构提供与本机引擎的连接,我们可以使用它来进行渲染。它还传递一系列摄像机,因为场景中可以有多个活动摄像机。RP 负责按照提供的顺序渲染所有这些摄像机。

                  2.2
                  相机渲染器


                  每个相机都独立渲染。因此,我们不会让CustomRenderPipeline渲染所有相机,而是将这一责任转发给一个专门用于渲染一个相机的新类。为其命名CameraRenderer并为其提供一个Render带有上下文和相机参数的公共方法。为了方便起见,我们将这些参数存储在字段中。

                    using UnityEngine;
                    using UnityEngine.Rendering;publicclassCameraRenderer {ScriptableRenderContext context;Camera camera;publicvoid Render (ScriptableRenderContext context, Camera camera) {this.context = context;this.camera = camera;}}

                    在创建渲染器时创建CustomRenderPipeline一个实例,然后使用它循环渲染所有摄像机。

                      CameraRenderer renderer = newCameraRenderer();
                      protectedoverridevoid Render (ScriptableRenderContext context, Camera[] cameras) {}protectedoverridevoid Render ( ScriptableRenderContext context, Listcameras) {for (int i = 0; i < cameras.Count; i++) {renderer.Render(context, cameras[i]);}}

                      我们的相机渲染器大致相当于URP 的可编写脚本的渲染器。这种方法将使未来支持每个摄像机的不同渲染方法变得简单,例如一种用于第一人称视图,一种用于 3D 地图叠加,或者前向渲染与延迟渲染。但现在我们将以相同的方式渲染所有摄像机。

                      2.3
                      绘制天空盒


                      CameraRenderer.Render的功能是绘制其相机可以看到的所有几何图形。为了清楚起见,将特定任务隔离在单独的DrawVisibleGeometry方法中。我们首先让它绘制默认的天空盒,这可以通过DrawSkybox以相机作为参数调用上下文来完成。

                        publicvoid Render (ScriptableRenderContext context, Camera camera) {this.context = context;this.camera = camera;DrawVisibleGeometry();}void DrawVisibleGeometry () {context.DrawSkybox(camera);}

                        以上还没有使天空盒成功被绘制。这是因为我们向上下文发出的命令被缓冲了。我们必须通过调用Submit上下文来提交排队的工作以供执行。让我们在一个单独的Submit方法中执行此操作,并在 之后调用DrawVisibleGeometry。

                          publicvoid Render (ScriptableRenderContext context, Camera camera) {
                          this.context = context;this.camera = camera;DrawVisibleGeometry();Submit();}void Submit () {context.Submit();}

                          天空盒最终出现在游戏和场景窗口中。启用它后,你还可以在帧调试器中看到它。它被列为Camera.RenderSkybox,其中有一个Draw Mesh在下面,代表实际的绘制调用。这对应于游戏窗口的渲染。帧调试器不会显示其他窗口中的绘图。

                          图片

                          图片
                          天空盒被绘制

                          注意一下,相机的方向当前不会影响天空盒的渲染方式。我们将相机传递给DrawSkybox,但这仅用于确定是否应该绘制天空盒,这是通过相机的清除标志控制的。
                          为了正确渲染天空盒以及整个场景,我们必须设置视图投影矩阵。该变换矩阵将相机的位置和方向(视图矩阵)与相机的透视或正交投影(投影矩阵)相结合。它在着色器中被称为unity_MatrixVP,绘制几何体时使用的着色器属性之一。您可以在帧调试器中检查该矩阵ShaderProperties选择绘制调用时的部分。
                          目前,unity_MatrixVP矩阵总是相同的。我们必须通过该方法将相机的属性应用于上下文SetupCameraProperties。这设置了矩阵以及其他一些属性。在调用 之前DrawVisibleGeometry以单独的Setup方法执行此操作。

                            publicvoid Render (ScriptableRenderContext context, Camera camera) {
                            this.context = context;this.camera = camera;Setup();DrawVisibleGeometry();Submit();}void Setup () {context.SetupCameraProperties(camera);}

                            图片
                            天空盒,正确对齐



                            文章转载自

                            Thepoly


                             
                            上一篇:相机的自动模式和场景模式
                            下一篇:自定义管线