LWRP的开篇总章中有这么一句话
The URP uses single-pass forward rendering. Use this pipeline to get optimized real-time performance on several platforms.
以及这样一张图片
这么没头没脑的一句话其实让很多人望而却步(也许没有很多人只是我),单pass遮挡效果怎么做?外拓描边如何实现等等?
当时内心的第一想法是不可能 肯定有办法去实现类似效果,于是找到了官方为实现类似效果做的示例 LWRP-CustomRendererExamples
看下他是如何实现遮挡显示的效果的。
这里看到他是通过指定renderertype的方式重新给rendererPipelineAssets设定一个renderData,然后看下RenderData
RenderData中默认的forwardRenderer渲染了非character层的所有东西。然后通过RendererFeatures的方式再after rendering opaques去绘制character层两次。然后在深度测试的使用决定每个像素到底是如何绘制
这是另外一个负责渲染正常character模型的RendererFeatures。
另外一个外拓描边的例子也是大同小异 通过renderFeature的方式来实现多pass。
但是个人感觉这种实现的方式不太好。可能会给GPU造成额外的压力。造成不必要的overdraw。
归根结底 这一切都是所谓的single pass导致的。因为没有更详细的说明 去源码里寻找答案
using System.Collections.Generic;
namespace UnityEngine.Rendering.LWRP
{
///
/// Draw objects into the given color and depth target
///
/// You can use this pass to render objects that have a material and/or shader
/// with the pass names LightweightForward or SRPDefaultUnlit.
///
internal class DrawObjectsPass : ScriptableRenderPass
{
FilteringSettings m_FilteringSettings;
RenderStateBlock m_RenderStateBlock;
List<ShaderTagId> m_ShaderTagIdList = new List<ShaderTagId>();
string m_ProfilerTag;
bool m_IsOpaque;
public DrawObjectsPass(string profilerTag, bool opaque, RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState, int stencilReference)
{
m_ProfilerTag = profilerTag;
m_ShaderTagIdList.Add(new ShaderTagId("LightweightForward"));
m_ShaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit"));
renderPassEvent = evt;
m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask);
m_RenderStateBlock = new RenderStateBlock(RenderStateMask.Nothing);
m_IsOpaque = opaque;
if (stencilState.enabled)
{
m_RenderStateBlock.stencilReference = stencilReference;
m_RenderStateBlock.mask = RenderStateMask.Stencil;
m_RenderStateBlock.stencilState = stencilState;
}
}
///
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
CommandBuffer cmd = CommandBufferPool.Get(m_ProfilerTag);
using (new ProfilingSample(cmd, m_ProfilerTag))
{
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
Camera camera = renderingData.cameraData.camera;
var sortFlags = (m_IsOpaque) ? renderingData.cameraData.defaultOpaqueSortFlags : SortingCriteria.CommonTransparent;
var drawSettings = CreateDrawingSettings(m_ShaderTagIdList, ref renderingData, sortFlags);
context.DrawRenderers(renderingData.cullResults, ref drawSettings, ref m_FilteringSettings, ref m_RenderStateBlock);
// Render objects that did not match any shader pass with error shader
RenderingUtils.RenderObjectsWithError(context, ref renderingData.cullResults, camera, m_FilteringSettings, SortingCriteria.None);
}
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
}
}
这里有两句代码引起了我的注意
m_ShaderTagIdList.Add(new ShaderTagId("LightweightForward"));
m_ShaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit"));
因为之前看过一些lwrp的shader代码 知道通过制定lightmode为LightweightForward的方式 为着色器指定渲染光照计算的pass。SRPDefaultUnlit看名字应该是给无灯光情况下使用的pass。那么如果一个shader同时有这两个pass会怎样呢?代码里我没找到答案 于是决定写一个shader试试同时存在这两个pass的shader
主要的2个pass代码如下
// ------------------------------------------------------------------
// Forward pass. Shades all light in a single pass. GI + emission + Fog
Pass
{
// Lightmode matches the ShaderPassName set in LightweightRenderPipeline.cs. SRPDefaultUnlit and passes with
// no LightMode tag are also rendered by Lightweight Render Pipeline
Name "ForwardLit"
Tags{"LightMode" = "LightweightForward"}
Blend[_SrcBlend][_DstBlend]
ZWrite[_ZWrite]
Cull[_Cull]
HLSLPROGRAM
// Required to compile gles 2.0 with standard SRP library
// All shaders must be compiled with HLSLcc and currently only gles is not using HLSLcc by default
#pragma prefer_hlslcc gles
#pragma exclude_renderers d3d11_9x
#pragma target 2.0
// -------------------------------------
// Material Keywords
#pragma shader_feature _NORMALMAP
#pragma shader_feature _ALPHATEST_ON
#pragma shader_feature _ALPHAPREMULTIPLY_ON
#pragma shader_feature _EMISSION
#pragma shader_feature _METALLICSPECGLOSSMAP
#pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
#pragma shader_feature _OCCLUSIONMAP
#pragma shader_feature _SPECULARHIGHLIGHTS_OFF
#pragma shader_feature _ENVIRONMENTREFLECTIONS_OFF
#pragma shader_feature _SPECULAR_SETUP
#pragma shader_feature _RECEIVE_SHADOWS_OFF
// -------------------------------------
// Lightweight Pipeline keywords
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
#pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS
#pragma multi_compile _ _SHADOWS_SOFT
#pragma multi_compile _ _MIXED_LIGHTING_SUBTRACTIVE
// -------------------------------------
// Unity defined keywords
#pragma multi_compile _ DIRLIGHTMAP_COMBINED
#pragma multi_compile _ LIGHTMAP_ON
#pragma multi_compile_fog
//--------------------------------------
// GPU Instancing
#pragma multi_compile_instancing
#pragma vertex LitPassVertex
#pragma fragment LitPassFragment
#include "LitInput.hlsl"
#include "LitForwardPass.hlsl"
ENDHLSL
}
Pass
{
Cull Front
ZWrite On
ColorMask RGB
Tags{"LightMode" = "SRPDefaultUnlit"}
Name "OUTLINE"
HLSLPROGRAM
#include "Packages/com.unity.render-pipelines.lightweight/ShaderLibrary/Core.hlsl"
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
CBUFFER_START(UnityPerMaterial)
float _Outline;
float4 _OutlineColor;
CBUFFER_END
struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
};
struct Varyings
{
float4 positionCS : SV_POSITION;
half fogCoord : TEXCOORD0;
half4 color : COLOR;
};
Varyings vert(Attributes input)
{
Varyings output = (Varyings)0;
input.positionOS.xyz += input.normalOS.xyz * _Outline;
VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
output.positionCS = vertexInput.positionCS;
output.color = _OutlineColor;
output.fogCoord = ComputeFogFactor(output.positionCS.z);
return output;
}
half4 frag(Varyings i) : SV_Target
{
i.color.rgb = MixFog(i.color.rgb, i.fogCoord);
return _MainLightColor;
}
ENDHLSL
}
LightweightForward代码基本是从litshader拷贝过来的。SRPDefaultUnlit里面也没什么特殊 只是一个法线外拓的描边。和一些没有修改回去的测试代码。代码比较好懂 但这不是重点,重点是第二个pass起效果了!
所以我们在LWRP中是可以直接通过shader实现多pass的效果的。。
后期为了测试 我还添加了名为Test的lightmode pass,计算了灯光颜色等,发现都没有什么异常
也就是说 在lwrp中所谓的singlepass 只是他可以在一个pass中完成光照计算。并不是不让使用其他的pass
(以上只是自己看下来的理解,如果说错了把谁引入歧途不负责任)