Unity通用渲染管线(URP)系列(十四)——多相机(Camera Blending & Rendering Layers)

2021-01-11 14:31:14 浏览数 (1)

目录

· 1 组合相机

· 1.1 分屏

· 1.2 分层相机

· 1.3 分层 Alpha

· 1.4 自定义混合

· 1.5 渲染纹理

· 1.6 Unity UI

· 1.7 逐相机设置 Post FX

· 2 渲染层

· 2.1 剔除掩码

· 2.2 调整Rendering Layer Mask

· 2.3 发送掩码到GPU

· 2.4 重新将Int解释为浮点型

· 2.5 相机渲染层掩码

· 2.6 逐相机的灯光掩码

本文重点内容: 1、使用不同的post FX设置来渲染多个摄像机 2、使用自定义的混合来分层相机 3、支持Layer masks 4、逐相机设置灯光掩码

这是有关创建自定义脚本渲染管道的系列教程的第14部分。这次,我们重新考虑了使用多个摄像机的渲染,现在添加了post FX。

本教程是CatLikeCoding系列的一部分,原文地址见文章底部。

本教程使用Unity 2019.4.12f1制作。

(用不同的方式观察同一个场景)

1 组合相机

因为每个摄像机都执行剔除,光处理和阴影渲染等,所以最好每帧渲染尽可能少的摄像机,理想情况下只渲染一个。但是有时候我们确实需要同时渲染多个不同的观察点。示例就包括分屏多人游戏,后视镜,俯视角的小地图,游戏中的摄像头和3D人物肖像。

在第一人称游戏中,人物的手和工具如何? 无论第一人称游戏中的人物手持的是什么,由于各种原因,它所显示的视角往往与场景的其他部分不同。这可以通过另一个摄像头来完成,但也可以通过调整视图矩阵来渲染,但仍然使用同一个摄像头。

1.1 分屏

让我们首先考虑由两个并排摄像头组成的分屏方案。左摄像机的视口矩形宽度设置为0.5。右摄像机的宽度也为0.5,其X位置设置为0.5。如果我们不使用后处理功能的话,那么它将按预期工作。

(不带Post FX的分屏,展示了不同视角下的同一个场景)

但如果启用后置FX,它将失败。两台摄像机都以正确的大小渲染,但它们最终会覆盖整个摄像机目标缓冲区,只有最后一个可见。

(带有Post FX的分屏 不正确)

发生这种情况是因为调用SetRenderTarget会重置视口以覆盖整个目标。要将视口应用于最终的FX Pass后,我们需要在设置目标之后且在绘制之前设置视口。通过复制PostFXStack.Draw,将其重命名为DrawFinal并在SetRenderTarget之后直接调用缓冲区的SetViewport(以相机的pixelRect作为参数)来实现。因为这是最后的Draw,所以我们可以用硬编码值替换除源参数以外的所有参数。

在DoColorGradingAndToneMapping的末尾调用新方法而不是常规Draw。

(带有 post FX的分屏,显示正确)

1.2 分层相机

除了渲染到单独的区域外,我们还可以使摄影机视口重叠。最简单的示例是使用覆盖整个屏幕的常规主摄像头,然后添加第二个摄像头,该摄像头稍后以相同的视图但较小的视口渲染。我将第二个视口缩小为一半,并通过将其XY位置设置为0.25居中。

(2个分层摄像机)

如果我们不使用Post FX,则可以通过将其设置为仅清除深度来将顶部相机图层变成部分透明的叠加层。这将删除其天空盒,从而显示下面的图层。但这在使用后置FX时不起作用,因为随后我们将其强制为CameraClearFlags.Color,因此我们将改为查看相机的背景色,默认情况下为深蓝色。

(第二个摄像机设置为clear depth 没有和有Post FX)

为了使Post FX可以使用图层透明度,我们可以做的一件事就是更改PostFXStack着色器的最终Pass,以便它执行alpha混合,而不是默认的One Zero模式。

这需要我们总是在FinalDraw中加载目标缓冲区。

现在,将重叠式摄像机的背景色的Alpha设置为零。只要我们禁用Bloom,这似乎就可以工作。我添加了两个非常明亮的自发光对象,以使Bloom是否开启变得显而易见。

(Bloom 禁用和开启)

它在bloom中不起作用,因为这种效果目前不能保持透明度。我们可以通过调整最终的bloom通道来解决这个问题,这样它就可以保持高分辨率源纹理的原始透明度。我们需要同时调整BloomAddPassFragment和BloomScatterFinalPassFragment,因为它们都可以用于最终的绘制。

(分层透明和Bloom)

透明现在对bloom有效,但是bloom对透明区域的贡献不再可见。我们可以通过将最终通道切换为预乘alpha混合来保存bloom。这需要我们将相机的背景色设置为纯透明黑色,因为它将被添加到下面的图层中。

(Bloom会影响透明区域)

1.3 分层 Alpha

当前的分层方法仅在我们的着色器产生可用于相机图层混合的有意义的Alpha值时才有效。我们之前并不关心写入的alpha值,因为我们从未将它们用于任何用途。但是现在,如果两个具有alpha 0.5的对象最终渲染到同一纹理像素,则该纹理像素的最终alpha应该为0.25。并且当两个Alpha值之一为1时,结果应始终为1。当第二个Alpha值为零时,应保留原始Alpha。混合alpha时,使用OneOneMinusSrcAlpha可以覆盖所有这些情况。通过在颜色混合模式之后添加逗号和alpha模式,我们可以为Alpha通道与颜色分别配置着色器的混合模式。为我们的Lit和Unlit着色器的常规Pass执行此操作。

只要使用适当的alpha值,此方法就起作用,这通常意味着写入深度的对象也应始终产生1的alpha。对于不透明的材质来说,这似乎很简单,但是如果最终使用的base map也包含变化的alpha,那么它将出错。剪辑材质也可能出错,因为它们依赖于alpha阈值来丢弃片段。如果片段被剪切,它尚能正常工作,但是如果不是,则其alpha应该变为1。

(alpha为零的不透明立方体将添加到Base Map层,而不是替换它)

确保Alpha对于我们的着色器正确运行的最快方法是在LitInput和UnlitInput中将_ZWrite添加到UnityPerMaterial缓冲区。

然后将带有alpha参数的GetFinalAlpha函数添加到两个输入文件中。如果_ZWrite设置为1,则返回1,否则返回所提供的值。

通过LitPassFragment中的此函数过滤表面alpha,以在末尾获得正确的alpha值。

在UnlitPassFragment中对base alpha做同样的操作。

1.4 自定义混合

与上一个相机图层融合仅对叠加摄影机有意义。底部相机将与相机目标的任何初始内容(随机的或前一帧的累积)混合,除非编辑器提供了清除的目标。因此,第一台相机应使用One Zero模式进行混合。为了支持替换,覆盖和更多奇怪的分层选项,我们将为启用post FX的像机添加可配置的最终混合模式。我们将为这些设置创建一个新的可序列化的CameraSettings配置类,就像对阴影所做的那样。为了方便起见,将源混合模式和目标混合模式都包装在一个内部FinalBlendMode结构中,然后默认将其设置为One Zero混合。

我们无法将这些设置直接添加到Camera组件中,因此我们将创建一个补充的CustomRenderPipelineCamera组件。只能将其添加到作为相机的游戏对象一次,并且只能添加一次。为它提供CameraSettings配置字段以及随附的getter属性。由于设置是一个类,因此该属性必须确保该类存在,因此如有必要,请创建一个新的设置对象实例。而类不存在的可能原因是,该组件尚未由编辑器序列化,或者在运行时将其添加到摄像机之后。

??怎么用? 它是null运算符。它是下面的简写

该属性的更详细形式是

现在,我们可以在CameraRenderer.Render的开头获取相机的CustomRenderPipelineCamera组件。为了支持没有自定义设置的相机,我们将检查组件是否存在。如果是,使用其设置,否则将使用一个默认设置对象,该对象将创建一次并将引用存储在静态字段中。然后,当我们设置栈时,我们将采用最终的混合模式。

现在,PostFXStack需要跟踪摄像机的最终混合模式。

因此,它可以在DrawFinal开始时设置新的_FinalSrcBlend和_FinalDstBlend浮动着色器属性。另外,如果目标混合模式不为零,我们只需要关心加载目标缓冲区。

最后,请在最终Pass中使用新属性,而不要使用硬编码的混合模式。

从现在开始,没有设置混合模式的相机将使用默认的One **Zero模式覆盖目标缓冲区的内容。叠加摄像机必须具有不同的最终混合模式,通常为OneOneMinusSrcAlpha。

(overlay相机的设置组件)

1.5 渲染纹理

除了创建分屏显示或直接对相机进行分层之外,通常还可以将摄像头用于游戏内显示或作为GUI的一部分。在这些情况下,相机的目标必须是渲染纹理,无论是资产还是在运行时创建的纹理。作为示例,我通过Assets / Create / Render Texture 创建了200×100渲染纹理。我没有给它提供深度缓冲区,因为我渲染了带有Post FX的相机,该相机使用深度缓冲区创建了自己的中间渲染纹理。

(Render Texture 资产)

然后,我通过将其连接到摄像机的Target Texture属性来创建一个将场景渲染为该纹理的摄像机。

(设置 相机目标纹理)

与常规渲染一样,底部相机需要将One Zero设置为其最终混合模式。编辑器最初将渲染Clear后的黑色纹理,但是此后,渲染纹理将包含最后渲染到该纹理的内容。正常情况下,多个摄影机可以使用任何视口渲染到相同的渲染纹理。唯一的区别是Unity会先自动渲染具有渲染纹理目标的摄像机,然后再渲染那些渲染到显示器的摄像机。首先,具有目标纹理的摄像机按深度递增的顺序渲染,然后是没有目标纹理的。

1.6 Unity UI

可以像任何常规纹理一样使用渲染纹理。但要通过Unity的UI显示它,我们需要使用通过GameObject / UI / Raw Image创建的raw image组件的游戏对象。

(UI raw image,按钮有部分重叠)

raw image使用默认的UI材质,该材质执行标准SrcAlpha OneMinusSrcAlpha混合。因此透明度是可行的,但Bloom不可以叠加,除非显示纹理,否则像素完美的双线性过滤将使相机的黑色背景颜色在透明边缘周围显示为黑色轮廓。

为了支持其他混合模式,我们需要创建一个自定义UI着色器。先复制Default-UI着色器,通过_SrcBlend和_DstBlend着色器属性添加对可配置混合的支持,来完成此操作。我还调整了着色器代码,以更好地匹配本教程系列的样式。

这是Pass,除了样式外,未经修改。

(使用预乘alpha混合自定义UI着色器的Raw UI图像。)

在哪里可以找到默认的UI着色器源代码? 转到Unity的档案下载,找到所需的Unity版本,然后从任一下拉菜单中选择“内置着色器”。着色器位于DefaultResourcesExtra / UI文件夹中。 https://unity3d.com/get-unity/download/archive

1.7 逐相机设置 Post FX

当使用多个像机时,应该可以为每个摄像机使用不同的post FX,所以让我们添加对它的支持。给CameraSettings一个开关,以控制它是否覆盖全局post FX设置,还是仅用于它自己的PostFXSettings 字段。

(相机post FX 覆盖设置)

让CameraRenderer.Render检查相机是否覆盖FX后设置。如果是的话,请将渲染管线提供的设置替换为相机的设置。

现在,每个摄像机都可以使用默认或自定义的Post FX。例如,我让底部相机使用默认值,关闭了叠加相机的Post FX,并为渲染纹理相机提供了不同的Post FX,比如,并具有冷温度变化和中性色调映射。

(逐相机设置不同的post FX)

2 渲染层

当同时显示多个摄像机视图时,我们并不总是希望为所有摄像机渲染相同的场景。例如,我们可以渲染主视图和人物肖像。Unity一次仅支持一个全局场景,因此我们需要使用一种方法来限制每台摄像机看到的内容。

2.1 剔除掩码

每个游戏对象都属于一个层。场景窗口可以通过编辑器右上方的Layers下拉菜单过滤显示的层。同样,每个摄像机都具有Culling Mask属性,该属性可用于限制以相同方式显示的内容。在渲染的剔除步骤期间应用此掩码。

每个对象只属于一个层,而剔除掩码可以包含多个层。例如,你可以有两个相机都渲染默认的层,一个也渲染忽略raycast,而另一个也渲染水。因此,有些对象在两个相机上都显示,而另一些对象只对其中一个或另一个可见,而其他对象可能根本不会被渲染。

(分屏显示,相机的 Culling Mask 不同)

为什么更改对象的层没有任何作用呢? 有可能,但是也有一个BUG,即Undo/Redo层更改可能不会影响对象是否被渲染。切换播放模式或再次明确更改层可以解决此问题。

灯光也有剔除掩码。这个想法是,被灯光剔除的对象的行为就像该灯光不存在一样。该对象不会被灯光照亮,也不会为其投射阴影。但是,如果我们使用定向光进行尝试,则仅会影响其阴影。

(应用于定向光的掩码仅影响阴影)

如果我们禁用了RP的Use Lights Per Object选项,则尝试使用其他灯光类型也会发生相同的情况。

(相同的剔除掩码应用于明亮的点光源)

如果启用Use Lights Per Object,则灯光剔除将按预期方式进行,但仅适用于点光源和聚光灯。

(点光源,lights-per-object开启)

我们获得这些结果的原因是,Unity在将每个对象的光照索引发送到GPU时应用了灯光剔除掩码。因此,如果我们不使用这些剔除方法将无法正常工作。而且它永远不会对定向光起作用,因为我们始终将其应用于所有对象。阴影总是会被正确剔除,因为从光源的角度渲染阴影投射器时,就像使用相机一样使用灯光的剔除掩码。

我们目前的方法无法完全支持灯光的剔除遮挡。但此限制不是致命的,HDRP不支持灯光的剔除掩码。Unity提供渲染层作为SRP的替代方案。使用渲染层而不是游戏对象层有两个好处。首先,渲染器不仅限于单个层,这使它们更加灵活。其次,渲染层不用于其他任何东西,而默认层也用于物理。

在继续渲染图层之前,让我们在灯光的检查器中将其剔除掩码设置为除“ Everything”以外的其他内容时显示警告。可以通过其cullingMask整数属性(其中-1代表所有层)来提供灯光的剔除掩码。如果CustomLightEditor的目标的掩码设置为其他任何设置,请在OnInspectorGUI的末尾调用EditorGUILayout.HelpBox,并使用Tips指示剔除掩码的字符串仅影响阴影,用MessageType.Warning来显示警告图标。

(为灯光设置 剔除掩码)

我们可以更具体一点,提到“Use Lights Per Object”设置对非定向灯光有所不同。

2.2 调整Rendering Layer Mask

使用SRP时,灯光和MeshRenderer组件的检查器将显示Rendering Layer Mask属性,该属性在使用默认RP时会隐藏。

(MeshRenderer 上的Rendering Layer Mask)

默认情况下,下拉列表显示32个层,分别命名为Layer1,Layer2等。可以通过覆盖RenderPipelineAsset.renderingLayerMaskNames getter属性为每个RP配置这些层的名称。因为这纯粹是下拉菜单的装饰,所以我们只需要对Unity编辑器执行此操作。因此,将CustomRenderPipelineAsset转换为局部类。

然后为其创建一个覆盖属性的仅编辑器脚本资产。它返回一个字符串数组,我们可以在静态构造函数方法中创建它。我们将以与默认名称相同的名称开头,不同之处在于Layer字和数字之间的空格。

这会稍微更改渲染层标签。它适用于MeshRenderer组件,但不幸的是灯光的属性无法响应更改。出现渲染层下拉菜单,但未应用调整。我们无法直接解决此问题,但是可以添加我们自己的有效版本的属性。首先在CustomLightEditor中为其创建GUIContent,并使用相同的标签和工具提示来指示这是其上方属性的功能版本。

然后创建一个DrawRenderingLayerMask方法,该方法是LightEditor.DrawRenderingLayerMask的替代方法,该方法的确将更改后的值分配给该属性。要使下拉菜单使用RP的层名称,我们不能简单地依赖EditorGUILayout.PropertyField。我们需要从设置中获取相关属性,确保处理多重选择的混合值,掩码获取为整数,将其显示,然后将更改后的值分配回该属性。这是默认灯光检查器版本所缺少的最后一步。

通过调用EditorGUILayout来显示下拉列表。带有标签、掩码和GraphicsSettings.currentRenderPipeline的MaskField。renderingLayerMaskNames作为参数。

在调用base.OnInspectorGUI之后直接调用新方法,因此额外的Rendering Layer Mask属性直接显示在非功能性方法的下方。另外,我们现在需要始终调用ApplyModifiedProperties来确保将对渲染层掩码的更改应用于灯光。

(为灯光设置的额外的渲染层掩码属性)

除了选择Everything或Layer 32选项所产生的结果与未选择Nothing相同,我们的属性版本确实会应用更改。发生这种情况是因为光的渲染层掩码在内部存储为无符号整数uint。这是有必须的,因为它用作位掩码,但是SerializedProperty仅支持获取和设置带符号的整数值。

Everything选项由-1表示,该属性钳位为零。第32层对应于最高位,该位代表比int.MaxValue大的数字,该属性也用零代替。

我们可以通过简单地删除最后一层,将渲染层名称的数量减少到31来解决第二个问题。这仍然是很多个层。HDRP仅支持八个。

通过删除一层,Everything选项现在由一个值表示,该值除最高位外都设置了,与int.MaxValue相匹配。因此,我们可以通过在存储int.MaxValue时显示-1来解决第一个问题。默认属性不执行此操作,这就是为什么在适当情况下显示Mixed...而不是Everything的原因。HDRP也受此困扰。

(功能渲染层掩码属性)

我们最终可以正确调整灯光的渲染层的掩码属性。但是默认情况下不使用该掩码,因此没有任何更改。可以通过在Shadows中启用ShadowDrawingSettings的useRenderingLayerMaskTest来将其应用于阴影。对所有灯光都执行此操作,因此在RenderDirectionalShadows,RenderSpotShadows和RenderPointShadows中进行。现在,我们可以通过配置对象和灯光的渲染层掩码来消除阴影。

2.3 发送掩码到GPU

要将渲染层掩码应用到我们的Lit着色器的光照计算中,对象和光照的掩码都必须在GPU侧可用。要访问对象的掩码,我们需要在unity_WorldTransformParams的下方,向UnityInput中的UnityPerDraw结构添加一个float4 unity_RenderingLayer字段。掩码存储在其第一个组件中。

我们将掩码作为uint添加到Surface结构中,因为它是位掩码。

在LitPassFragment中设置表面的掩码时,我们需要使用asuint固有函数。这将使用原始数据,而无需执行从float到uint的数字类型转换,这会改变位模式。

我们需要对Light结构执行相同的操作,因此也为其渲染层掩码指定一个uint字段。

我们负责将遮罩发送到GPU。通过将其存储在_DirectionalLightDirections和_OtherLightDirections数组的未使用的第四部分中,来完成此操作。为了清楚起见,将AndMasks后缀添加到其名称中。

在GetDirectionalLight中复制掩码。

和GetOtherLight中。

在CPU端,调整我们的Lighting类中的标识符和数组名称以使其匹配。然后还复制灯光的渲染层遮罩。我们从SetupDirectionalLight开始,它现在还需要直接访问Light对象。让我们将其添加为参数。

对SetupSpotLight进行相同的更改,还添加一个Light参数以保持一致。

然后对SetupPointLight进行此操作,现在还需要更改其他LightDirectionsAndMask。由于它不使用方向,因此可以将其设置为零。

现在,我们需要在SetupLights中捕获一次Light对象,并将其传递给所有的设置方法。不久之后,我们还将在灯光下进行其他操作。

返回GPU端,向Lighting添加一个RenderingLayersOverlap函数,该函数返回表面的掩码和灯光的掩码是否重叠。这是通过检查位掩码的按位与运算是否为非零来完成的。

着色器支持按位操作吗? 是的,除非你的目标是OpenGL ES 2.0,但我们不支持2.0。

现在,我们可以使用此方法来检查是否需要在GetLighting的三个循环中添加灯光。

我们不能将检查放在另一个GetLighting函数中吗? 可以,这样会减少代码量。但是,在这种情况下,着色器编译器不会生成分支。如果不需要的话,灯光总是会被计算和丢弃。你可以使用UNITY_BRANCH强制分支,但是如果跳过灯光时返回零,则仍然可以得到不必要的添加。这个问题当然也可以被解决解决,但是此时代码变得有些臃肿。

2.4 重新将Int解释为浮点型

尽管渲染掩码此时会影响照明,但它并没有正确地这样做。Light.renderingLayerMask属性将其位掩码公开为int,并且在转换过程中会出现乱码,从而在light setup方法中浮动。无法直接将整数数组发送到GPU,因此我们必须以某种方式将int重新解释为浮点数,而无需进行转换,但是C#无法直接使用asuint等效项。

由于C#是强类型的,因此我们无法像HLSL那样简单地重新解释C#中的数据。我们可以通过使用并集结构来重命名数据类型。通过向int添加ReinterpretAsFloat扩展方法来隐藏此方法。为此方法创建一个静态的ReinterpretExtensions类,该类最初只是执行常规的类型转换。

在三个light设置方法中使用ReinterpretAsFloat,而不是依赖于隐式转换。

然后在ReinterpretExtensions内部定义一个带有int和float字段的结构类型。在ReinterpretAsFloat中初始化此类型的默认变量,设置其整数值,然后返回其float值。

为了将其转换为重新解释,我们需要使结构的两个字段重叠,以便它们共享相同的数据。这是可以的,因为两种类型的大小均为四个字节。我们通过将StructLayout属性附加到类型(设置为LayoutKind.Explicit)来使结构的布局明确。然后,我们将FieldOffset属性添加到其字段中,以指示应将字段数据放置在何处。将两个偏移都设置为零,以便它们重叠。这些属性来自System.Runtime.InteropServices命名空间。

现在,该结构的int和float字段表示相同的数据,但解释不同。这样可以保持位掩码完整无缺,并且渲染层掩码现在可以正常工作。

(方向光现在忽略了一半的对象)

为什么不使用Unsafe的代码? 可以,但是需要为项目显式启用不安全的代码,这使得共享代码更加困难。此外,也有可能团队可能根本不允许使用不安全的代码。Unity结构方法避免了这些问题。

2.5 相机渲染层掩码

除了使用现有的剔除掩码之外,我们还可以使用渲染层掩码来限制相机的渲染。Camera没有渲染层掩码属性,但是我们可以将其添加到CameraSettings中。将其设置为int,因为灯光的掩码也作为int暴露。默认情况下将其设置为-1,代表所有层。

(相机渲染层掩码 暴露为整数)

要将掩码显示为下拉菜单,我们需要为其创建自定义GUI。但是,与其为整个CameraSettings类创建一个自定义编辑器,不如让它仅用于渲染层掩码。

首先,要指示字段表示渲染层掩码,请创建一个扩展PropertyAttribute的RenderingLayerMaskFieldAttribute类。这只是一个标记属性,不需要执行其他任何操作。请注意,这不是编辑器类型,因此不应放在Editor文件夹中。

将此属性附加到我们的渲染层掩码字段。

现在,创建一个继承自PropertyDrawer的自定义属性drawer编辑器类,并为我们的属性类型添加CustomPropertyDrawer属性。将CustomLightEditor.DrawRenderingLayerMask复制到其中,将其重命名为Draw,并将其设为public static。然后为其指定三个参数:位置Rect,序列化的属性和GUIContent标签。使用它们来调用EditorGUI.MaskField而不是EditorGUILayout.MaskField。

如果属性的基础类型为uint,则仅需单独处理-1。如果其type属性等于“ uint”,就是这种情况。

然后重写OnGUI方法,只需将其调用转发给Draw。

(Rendering layer mask下拉菜单)

为了使Draw更易于使用,请添加没有Rect参数的版本。调用EditorGUILayout.GetControlRect以从布局引擎获取单行位置rect。

现在,我们可以从CustomLightEditor中删除DrawRenderingLayerMask方法,然后调用RenderingLayerMaskDrawer.Draw。

要应用相机的渲染层遮罩,请为其添加一个参数到CameraRenderer.DrawVisibleGeometry并将其作为名为renderingLayerMask的参数传递给FilteringSettings构造函数方法,并转换为uint。

然后在Render中调用DrawVisibleGeometry时传递渲染层掩码。

现在可以使用更灵活的渲染层掩码来控制摄影机的渲染。例如,即使照相机看不到阴影,我们也可以让一些对象投射阴影,而无需特殊的仅阴影对象。

(仅渲染不受灯光影响的对象,和地面)

需要记住的一件事是,只有剔除掩码用于剔除,所以如果要排除很多对象,常规剔除掩码的性能会更好。

2.6 逐相机的灯光掩码

尽管Unity的RP并没有这样做,但是除了几何图形之外,还可以为每个像机设置灯光掩码。我们将再次为此使用渲染层,但是由于它是非标准行为,因此我们可以通过在CameraSettings中为其添加开关来使其可选。

(相机设置为 mask lights)

我们要做的就是跳过Lighting.SetupLights中的遮罩灯。为此,向该方法添加一个rendering layer mask参数,然后检查每个光源的渲染层掩码是否与提供的掩码重叠。如果是这样,请继续执行switch语句以设置指示灯,否则请跳过它。

Lighting.Setup必须通过渲染层掩码。

并且我们必须在CameraRenderer.Render中提供相机的掩码,但前提是它仅适用于灯光,否则请使用-1。

现在,我们可以执行以下操作:让两个摄像机渲染相同的场景,但是使用不同的灯光,而不必在两者之间进行调整。这也使得在世界原点轻松渲染独立的场景(如人物肖像)而不会受到主要场景的灯光影响。请注意,这仅适用于实时照明,不会影响完全烘焙的光,并且不会消除混合光的烘焙间接影响。

(两个相机用不同的光看同一个场景)

你如何为该场景配置掩码? 所有可见对象的渲染层掩码都设置为everything。定向光的掩码设置为单个层,点光的掩码设置为不同的单层。左相机的掩码设置为除点光源层以外的所有内容。右相机的掩码设置为除定向光的图层以外的所有内容。结果是每个摄像机只能看到两个灯光中的一个。

下一章,粒子。

欢迎扫描二维码,查看更多精彩内容。点击 阅读原文 可以跳转原教程。

本文翻译自 Jasper Flick的系列教程

原文地址:

https://catlikecoding.com/unity/tutorials

0 人点赞