unity绘制管道_【译文】unity可编程渲染管道#1——自定义管道

前言

Scriptable Render Pipeline定制流水线控制渲染创建管道资产和实例。剔除,过滤,排序,渲染。保持记忆清洁。提供良好的编辑体验。这是涵盖Unity可编写脚本的渲染系列教程的第一部分管道。本教程假设您首先完成了基础知识系列,并且已经完成了程序网格教程。渲染系列的前几部分也很有用。本教程使用Unity 2018.3.0f2。新渲染管道的诞生。

1. 创建管道

要渲染任何东西,Unity必须确定必须绘制哪些形状,其中,什么时候和什么设置。这可能变得非常复杂,具体取决于用到的各种特效。灯光,阴影,透明度,图像特效,体积效果(volumetric effects),所有这些都必须以正确的顺序处理才能得出最终的图像。此过程称为渲染管道。

Unity 2017支持两个预定义的渲染管道,一个用于前向渲染和一个用于延迟渲染。它还支持较旧的延迟渲染(Unity 5中引入的方法)。这些管道是固定的。你可以启用,禁用或覆盖重写管道的某些部分,但不可能大幅度偏离原本的设计。

Unity 2018增加了对可编写脚本(scriptable render pipeline)的渲染管道的支持,使从头开始设计管道变为可能,但许多单独的具体操作仍然需要依赖Unity,如剔除(culling)。Unity 2018引入了两条新的管道新方法,轻量级渲染管道(LWRP)和高清渲染管道(HDRP)。这两个渲染管道技术仍处于预览阶段,其技术文档(API)也为实验技术。但现在它足够稳定,足够我们创建我们自己的管道。

这部分内容有剧透

请点击后再查看

(译者注:作者的版本是2018.3.0,但在2019.1的release post 里已经撕掉预览标签, production-ready)

在本教程中,我们将设置一个绘制未点亮形状(unlit shapes)的最小渲染管道

这部分内容有剧透

请点击后再查看

译者注:若你想看基础关照或者其他高级特效,请移步后续文章)。

一旦我们设置完这个基础渲染管线,我们可以在以后的教程中扩展我们的管道,添加照明,阴影和更高级的功能。

1.1 项目设置

打开Unity 2018并创建一个新项目。我正在使用Unity 2018.2.9f1,但是任何2018.2或更高版本也应该有效。关闭unity analytics。我们将创建自己的管道,因此不要选择其中一个管道选项。项目打开后,通过Window / Package Manager转到包管理器并删除默认包含的所有包,因为我们不需要它们。仅保留无法删除的程序包管理器UI。

我们将在线性色彩空间中工作,但Unity 2018仍然使用伽马空间默认。(译者注:unity 的发展趋势是从Gamma 空间发展到Linear space,并保留对Gamma空间的支持,以便旧满足项目升级的可持续性,但未来Gamma空间将会被淘汰。 详情请查阅unity 上海大会关于对关照技术的演讲说明。)。所以通过Edit / Project Settings / Player 转到player 并切换“Other Settings”部分中的Color space为“linear”。

我们需要一些简单的材料来测试我们的管道。我创造了四种材料。首先,带有红色Albedo的默认标准不透明材质。第二,同样的材质,但渲染模式设置为透明和蓝色的albedo减少alpha值。第三,使用Unlit / Color着色器并将其颜色设置为的材质黄色。最后使用Unlit / Transparent着色器的材质没有任何变化,所以看起来是纯白色。

使用所有四种材料填充场景中的一些物体。场景显示四种材料。

1.2 管道资产(pipeline asset)

目前,Unity使用默认的正向渲染管道(forward rendering pipeline)。要使用自定义管道,我们必须在graphics settings中选择一个,可以通过Edit / Project Settings / Graphics找到。

要设置我们自己的管道,我们必须将pipeline asset分配给Scriptable Render Pipeline settings。这些Asset 必须扩展(extend) RenderPipelineAsset,这是一个 ScriptableObject类型。为我们的自定义管道资产创建一个新脚本。我们只需将我们的管道命名为MyPipeline。因此,它的asset类型将是MyPipelineAsset,并且必须对定义在 UnityEngine.Experimental.Rendering 命名空间的 RenderPipelineAsset.进行扩展。using UnityEngine;

using UnityEngine.Experimental.Rendering;

public class MyPipelineAsset : RenderPipelineAsset {}

它总是在实验名称空间中(experimental namespace)吗?

这部分内容有剧透

请点击后再查看

它会在某个时刻移出实验命名空间,或者转移到UnityEngine.Rendering 或另一个命名空间。当发生这种情况时,这只需要更新using语句,除非API也被更改。

管道资产的主要目的是为Unity提供一种方法来获取一个负责渲染的管道对象实例, zhe个资产本身只是一个handle和存储管道设置的位置。我们还没有进行任何设置,所以全部我们要做的是给Unity一个获取我们的管道对象实例的方法。这是通过覆盖InternalCreatePipeline方法。但我们还没有定义我们的管道对象类型,所以我们暂时先只返回null。

InternalCreatePipeline的返回类型是IRenderPipeline。该类型的前缀表示它是一个接口类型。public class MyPipelineAsset : RenderPipelineAsset {

protected override IRenderPipeline InternalCreatePipeline () {

return null; }

}

什么是界面?

接口就像一个类,除了它定义了一个功能性协议但不提供具体实现。它只定义属性,事件,索引器和方法签名,且都是公开(public)的。任何扩展接口的类型需要包含接口定义的实现。惯例是使用I为接口类型名称添加前缀。

因为接口不包含具体实现,所以类可能甚至结构扩展了多个接口。如果发生多个接口定义相同的东西,他们只是同意该功能应该存在。这不肯能在类中发生 ,即使是抽象类。因为这可能导致冲突实现(conflicting implementations)。

现在我们需要将这种类型的资产添加到我们的项目中。为了实现这一点,请添加一个CreateAssetMenu属性给MyPipelineAsset。[CreateAssetMenu]

public class MyPipelineAsset : RenderPipelineAsset {}

这会在“Asset / Create 菜单中建立一个新的条目(入口)。看在整洁的份上,我们把它放在一个叫Rendering的子菜单里。通过将属性的menuName属性设置为Rendering/My Pipeline来完成此操作。可以在属性类型后直接设置属性,在圆括号内。[CreateAssetMenu(menuName = "Rendering/My Pipeline")]

public class MyPipelineAsset : RenderPipelineAsset {}

使用新菜单项将资产添加到项目中,并将其命名为My Pipeline。管道资产及其脚本。

然后将其分配给Scriptable Render Pipeline Settings。(译者注:若读者使用的是更高版本或使用的不是实验命名空间的Rendering,请观阅完第二节,具体实现 RenderPipelineAsset 和 RenderPipeline。)

我们现在已经替换了默认管道,有些事情也随之变换了。首先,很多选项已从graphics settings,消失,在unity的信息面板也可以看到。其次,因为我们绕过了默认管道而没有提供有效的替换,没有任何东西得到渲染。游戏窗口,场景窗口和材质预览不再起作用,尽管是场景窗口仍然显示天空盒。如果打开Frame  Debugger—— 通过Window / Analysis /Frame Debugger - 启用它,您将看到确实没有任何东西被绘制出来游戏窗口。

1.3 管道实例

要创建有效的管道,我们必须提供一个对象实来实现IRenderPipeline并让其负责渲染过程。所以创建一个类,命名为MyPipeline。using UnityEngine;

using UnityEngine.Experimental.Rendering;

public class MyPipeline : IRenderPipeline {}

虽然我们可以自己实现IRenderPipeline,但让它扩展抽象RenderPipeline类更加方便。因为我们可以在实现RenderPipeline的基础上来实现我们的功能。public class MyPipeline :RenderPipeline {}

现在我们可以在InternalCreatePipeline中返回MyPipeline的新实例。这意味着我们已经有一个有效的管道,虽然它仍然没有渲染任何东西protected override IRenderPipeline InternalCreatePipeline () {

returnnew MyPipeline();

}

2 渲染

管道对象负责渲染每个帧。Unity所做的是调用管道的Render方法,这个方法的参数为context和camera。这个方法不仅适用于游戏窗口,也在场景窗口和材质预览编辑中使用。让我们通过适当的配置,找出需要的东西来渲染,并以正确的顺序执行所有操作。

2.1 Context

RenderPipeline包含一个实现IRenderPipeline 接口的Render方法。它的第一个参数是render contex,它是一个ScriptableRenderContext结构体,充当内在代码的门面(acting as a facade for native code,俺意译的)。它的第二个参数是一个包含所有需要渲染的相机的数组。

RenderPipeline.Render 不绘制任何东西,但检查管道对象能否渲染。如果不能渲染,它将引发异常。我们将覆盖这一方法并调用基本实现,以保持此检查。public class MyPipeline : RenderPipeline {

public override void Render ( ScriptableRenderContext renderContext, Camera[] cameras) {

base.Render(renderContext, cameras);}

}

我们通过render context向 Unity引擎发出命令,渲染事物并控制渲染状态。其中一个最简单的例子是绘制天空盒,可以通过调用DrawSkyBox方法来完成。

base.Render(renderContext, cameras);renderContext.DrawSkybox();

DrawSkybox需要一个相机作为参数。我们将简单地使用相机数组的第一个元素。renderContext.DrawSkybox(cameras[0]);

我们仍然没有看到天空盒出现在游戏窗口中。那是因为我们向上下文发出的命令是缓冲的。实际工作发生在我们通过Submit方法提交它以供执行。renderContext.DrawSkybox(cameras[0]);

renderContext.Submit();

现在你终于能看到天空盒出现在游戏窗口里,你也能从frame debugger 里看到:

2.2 相机

我们提供了一系列相机,因为可以存在需要多个相机渲染的场景。多摄像机设置的示例用途是分屏多人游戏,迷你地图和后视镜。每个相机都需要单独处理。

此时我们先不担心多相机支持我们的管道。只需创建一个替代单个相机的Render方法。由它画天空盒然后提交。所以我们按照相机提交void Render (ScriptableRenderContext context, Camera camera) {

context.DrawSkybox(camera);

context.Submit();

}

为相机阵列的每个元素调用新方法。我使用foreach循环遍历,就像Unity的管道也使用这种方法来遍历摄像头。public override void Render (

ScriptableRenderContext renderContext, Camera[] cameras

) {

base.Render(renderContext, cameras);

//renderContext.DrawSkybox(cameras[0]);

//renderContext.Submit();

foreach (var camera in cameras) {

Render(renderContext, camera);

}

}

foreach如何运作?

这部分内容有剧透

请点击后再查看

foreach (var e in a) { … }  就像 for(int i = 0 ; i

当a不是数组而是其它可枚举的其他东西时(如 List, HashSet),迭代器就会进入,你最终可能会创建临时对象,最好避免。但使用带数组的foreach是安全的。使用var来定义元素变量很常见,所以我也使用它。它的类型是的元素类型a的元素类型。

请注意,相机的方向当前不会影响天空盒怎样渲染。我们将相机传递给DrawSkybox,但这仅用于确定是否应该绘制天空盒,这是通过相机的clear flags决定的。

要正确渲染天空盒 -——以及整个场景 —— 我们必须设置视图 -投影矩阵(VP matrix)。这个变换矩阵结合了相机的位置和方向 - 视图矩阵 - 使用相机的透视或正交投影 - 投影矩阵。您可以在帧调试器中看到此矩阵。它是unity_MatrixVP,是绘制某些东西时使用的着色器属性之一。

目前,unity_MatrixVP矩阵始终是相同的。我们必须通过SetupCameraProperties方法将相机的属性添加到context中, 这就像设置其它设置一样设置我们的矩阵。void Render (ScriptableRenderContext context, Camera camera) {

context.SetupCameraProperties(camera);

context.DrawSkybox(camera);

context.Submit();

}

现在天空盒被正确渲染,将游戏窗口和场景窗口中的相机属性也考虑在内。

2.3 命令缓冲区

context 会延迟实际的渲染直到我们提交它。 在此之前, 我们通过配置和向其添加命令以供以后执行。一些任务 —— 比如绘制天空盒—— 可以通过专用方法来执行,但其他命令的分发需通过单独的命令缓冲区。

可以通过实例化一个在UnityEngine.Rendering命名空间中定义的CommandBuffer。 CommandBuffer已存在在添加脚本化渲染管道之前,它们不是实验性的。 在绘制天空盒之前先创建这样一个缓冲区。using UnityEngine;

using UnityEngine.Rendering;

using UnityEngine.Experimental.Rendering;

public class MyPipeline : RenderPipeline {

void Render (ScriptableRenderContext context, Camera camera) {

context.SetupCameraProperties(camera);

var buffer = new CommandBuffer();

context.DrawSkybox(camera);

context.Submit();

}

}

我们可以通过其ExecuteCommandBuffer方法指示context 执行 这个buffer。 同样的,这不会立即执行命令,而是将它们复制到context的内部缓冲区。var buffer = new CommandBuffer();

context.ExecuteCommandBuffer(buffer);

Command b

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值