【转载】内置管线 Shader 升级到 URP 详细手册

1,428 阅读6分钟

原文链接

作者:洪智
链接:www.jianshu.com/p/3fef69e2e…
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

正文

本文基于 Unity2020.2,URP10,更新于 20210303

本文初版内容翻译自:From Built-in to URP

译者:大智
后续补充:大智

内置管线 Shader 升级到 URP 详细手册

总体结构

1、在 SubShader 的 Tags 中添加 "RenderPipeline" = "UniversalPipeline"
2、所有 URP 着色器都是 HLSL 编写的,使用宏 HLSL 包含的 shader 代码
3、使用 HLSLINCLUDE 替代 CGINCLUDE

内置管线URP
CGPROGRAM HLSLPROGRAMHLSL程序
ENDCG ENDHLSLENDHLSL
CGINCLUDE HLSLINCLUDEHLSLINCLUDE

Include文件

内容内置管线URP
CoreUnityCG.cgincPackages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl
LightAutoLight.cgincPackages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl
ShadowAutoLight.cgincPackages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl
表面着色器Lighting.cgincURP 内没有,可以参考项目:在此处
其他有用的包括:

灯光模式 LightMode

内置管线URP
ForwardBaseUniversalForward
ForwardAdd移除
Deferred 以及相关尚未支持
Vertex 及相关移除
ShadowCasterShadowCaster
MotionVectors尚未支持

支持的其他照明模式包括:

  • DepthOnly
  • Meta (for lightmap baking)
  • Universal2D

变体 Variants

URP 支持某些变体,因此,根据你使用的功能,可能需要使用 #pragma multi_compile 添加一些关键字:

  • _MAIN_LIGHT_SHADOWS
  • _MAIN_LIGHT_SHADOWS_CASCADE
  • _ADDITIONAL_LIGHTS_VERTEX
  • _ADDITIONAL_LIGHTS
  • _ADDITIONAL_LIGHT_SHADOWS
  • _SHADOWS_SOFT
  • _MIXED_LIGHTING_SUBTRACTIVE

预定义的着色器宏

辅助宏

内置管线URP
UNITY_PROJ_COORD(a)移除了,使用 a.xy / a.w 代替
UNITY_INITIALIZE_OUTPUT(typename)ZERO_INITIALIZE(typename)

阴影贴图

必须 include “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl”

内置管线URP
UNITY_DECLARE_SHADOWMAPtexTEXTURE2D_SHADOW_PARAMtextureNamesamplerName
UNITY_SAMPLE_SHADOWtexuvSAMPLE_TEXTURE2D_SHADOWtextureNamesamplerNamecoord3
UNITY_SAMPLE_SHADOW_PROJtexuvSAMPLE_TEXTURE2D_SHADOWtextureNamesamplerNamecoord4.xyz/coord4.w

纹理/采样器声明宏

Unity 有很多纹理/采样器宏来改善 API 之间的交叉兼容性,但是人们并不习惯使用它们。URP 中这些宏的名称有所变化。由于数量很多,全部的宏可以在 API includes中查看,下面主要列举一些常用的:

内置管线URP
UNITY_DECLARE_TEX2DnameTEXTURE2DtextureName); SAMPLERsamplerName);
UNITY_DECLARE_TEX2D_NOSAMPLERnameTEXTURE2DtextureName);
UNITY_DECLARE_TEX2DARRAYnameTEXTURE2D_ARRAYtextureName); SAMPLERsamplerName);
UNITY_SAMPLE_TEX2DnameuvSAMPLE_TEXTURE2DtextureNamesamplerNamecoord2
UNITY_SAMPLE_TEX2D_SAMPLERnamesamplernameuvSAMPLE_TEXTURE2DtextureNamesamplerNamecoord2
UNITY_SAMPLE_TEX2DARRAYnameuvSAMPLE_TEXTURE2D_ARRAYtextureNamesamplerNamecoord2index
UNITY_SAMPLE_TEX2DARRAY_LODnameuvlodSAMPLE_TEXTURE2D_ARRAY_LODtextureNamesamplerNamecoord2indexlod

需要注意 SCREENSPACE_TEXTURE 变成了 TEXTURE2D_X。如果你想要在 VR 中(Single Pass InstancedMulti-view 模式)制作屏幕效果,你必须使用 TEXTURE2D_X 定义纹理。这个宏会为你处理正确的纹理声明(是否为数组)。对这个纹理采样的时候必须使用 SAMPLE_TEXTURE2D_X,并且对 uv 使用 UnityStereoTransformScreenSpaceTex

Shader 辅助函数

下列函数可以在此文件中找到:“Packages/com.unity.render-pipelines.core/ShaderLibrary/SpaceTransforms.hlsl”.

顶点转换函数

内置管线URP
float4 UnityObjectToClipPosfloat3 posfloat4 TransformObjectToHClipfloat3 positionOS
float3 UnityObjectToViewPosfloat3 posTransformWorldToViewTransformObjectToWorldpositionOS))

法线转换函数

内置管线URP
float4 UnityObjectToWorldNormalfloat3 posfloat4 TransformObjectToWorldNormalfloat3 normalOS

通用辅助函数

内置管线URP
float3 UnityWorldSpaceViewDirfloat4 vGetCameraPositionWS() - i.worldPos
float3 UnityObjectToWorldDirfloat4 vTransformObjectToWorldDir(real3 dirOS)
float3 ObjSpaceViewDirfloat4 v移除了,可以使用TransformWorldToObject(GetCameraPositionWS()) - objectSpacePosition ;
float2 ParallaxOffsethalf hhalf heighthalf3 viewDir移除了。可以从 UnityCG.cginc 复制过来
fixed Luminancefixed3 creal Luminancereal3 linearRgbInclude “Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl”
fixed3 DecodeLightmapfixed4 colorreal3 DecodeLightmap(real4 encodedIlluminance, real4 decodeInstructions)Include “Packages/com.unity.render-pipelines.core/ShaderLibrary/EntityLighting.hlsl” URP 中的 decodeInstructionshalf4(LIGHTMAP_HDR_MULTIPLIER, LIGHTMAP_HDR_EXPONENT, 0.0h, 0.0h)
float4 EncodeFloatRGBAfloat v移除了。可以从 UnityCG.cginc 复制过来
float DecodeFloatRGBAfloat4 enc移除了。可以从 UnityCG.cginc 复制过来
float2 EncodeFloatRGfloat v移除了。可以从 UnityCG.cginc 复制过来
float DecodeFloatRGfloat2 enc移除了。可以从 UnityCG.cginc 复制过来
float2 EncodeViewNormalStereofloat3 n移除了。可以从 UnityCG.cginc 复制过来
float3 DecodeViewNormalStereofloat4 enc4移除了。可以从 UnityCG.cginc 复制过来
TANGENT_SPACE_ROTATION

前向渲染辅助函数

内置管线URP
float3 UnityWorldSpaceLightDir(float4 v)_MainLightPosition.xyz - TransformObjectToWorld(objectSpacePosition)Include “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl”
float3 ObjSpaceLightDir(float4 v)TransformWorldToObject( _MainLightPosition.xyz)-objectSpacePositionInclude “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl”
float3 Shade4PointLights()可以使用 half3 VertexLighting(float3 positionWS, half3 normalWS)对于 VertexLighting(...) include “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl”

屏幕空间辅助函数

内置管线URP
float4 ComputeScreenPos(float4 clipPos)float4 ComputeScreenPosfloat4 positionCSInclude “Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderVariablesFunctions.hlsl”
float4 ComputeGrabScreenPos(float4 clipPos)移除了

顶点照明辅助函数

内置管线URP
float3 ShadeVertexLights (float4 vertex, float3 normal)移除了,可以尝试使用UNITY_LIGHTMODEL_AMBIENT.xyz + VertexLighting(...)对于VertexLighting(...) include “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl”

可以在“Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl”中找到很多工具函数。

内置着色器变量

除了光照相关的变量外,其他的变量名都基本没变

照明

内置管线URP
_LightColor0_MainLightColorInclude “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl”
_WorldSpaceLightPos0_MainLightPositionInclude “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl”
_LightMatrix0移除了。目前尚不支持 Cookie
unity_4LightPosX0unity_4LightPosY0unity_4LightPosZ0在 URP 中,其他光源存储在数组/缓冲区中(取决于平台)。使用Light GetAdditionalLight(uint i, float3 positionWS)获取额外光源信息Include “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl”
unity_4LightAtten0在 URP 中,其他光源存储在数组/缓冲区中(取决于平台)。使用Light GetAdditionalLight(uint i, float3 positionWS)获取额外光源信息Include “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl”
unity_LightColor在 URP 中,其他光源存储在数组/缓冲区中(取决于平台)。使用Light GetAdditionalLight(uint i, float3 positionWS)获取额外光源信息Include “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl”
unity_WorldToShadowfloat4x4 _MainLightWorldToShadow[MAX_SHADOW_CASCADES + 1] 或者_AdditionalLightsWorldToShadow[MAX_VISIBLE_LIGHTS]Include “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl”

如果要使用循环所有其他灯光 GetAdditionalLight(...)GetAdditionalLightsCount() 可以使用来查询其他灯光计数。

其他

阴影

有关阴影的更多信息,“Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl”

内置管线URP
UNITY_SHADOW_COORDSx移除了。DIY,例如float4 shadowCoord : TEXCOORD0;
TRANSFER_SHADOWaa.shadowCoord = TransformWorldToShadowCoordworldSpacePosition启用 cascades 时,对片段执行此操作以避免视觉鬼影
SHADOWS_SCREEN移除了。不支持。

有关雾的更多信息,“Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderVariablesFunctions.hlsl”.

内置管线URP
UNITY_FOG_COORDS(x)移除了。DIY,例如float fogCoord : TEXCOORD0;
UNITY_TRANSFER_FOG(o*,outpos)o.fogCoord = ComputeFogFactor(clipSpacePosition.z);
UNITY_APPLY_FOG(coordcol)color = MixFog(colori.fogCoord);

深度

要使用相机深度纹理,需要 include “Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl” ,然后会自动声明 _CameraDepthTexture,也会包含辅助函数 SampleSceneDepth(...)LoadSceneDepth(...)

内置管线URP
LinearEyeDepthsceneZLinearEyeDepthsceneZ_ZBufferParamsInclude “Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl”
Linear01DepthsceneZLinear01DepthsceneZ_ZBufferParamsInclude “Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl”

其他中的其他

内置管线URP
ShadeSH9normalSampleSHnormalInclude “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl”
unity_ColorSpaceLuminance移除了。使用 Luminance()Include “Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl”

后处理/视觉特效

URP 不支持OnPreCullOnPreRenderOnPostRenderOnRenderImage 这些方法。URP 支持OnRenderObjectOnWillRenderObject,但是如果在 URP 中使用你可能会发现问题。因此,如果你曾经在旧管线创建视觉效果时使用它们,那么现在你需要学习新方法了。URP包含以下注入点:

  • beginCameraRendering(ScriptableRenderContext context, Camera camera)
  • endCameraRendering(ScriptableRenderContext context, Camera camera)
  • beginFrameRendering(ScriptableRenderContext context,Camera[] cameras)
  • endFrameRendering(ScriptableRenderContext context,Camera[] cameras)

用法示例:

**

void OnEnable()
{
    RenderPipelineManager.beginCameraRendering += MyCameraRendering;
}

void OnDisable()
{
    RenderPipelineManager.beginCameraRendering -= MyCameraRendering;
}

void MyCameraRendering(ScriptableRenderContext context, Camera camera)
{
    ...
    if(camera == myEffectCamera)
    {
    ...
    }
    ...
}

就像我说的那样,OnWillRenderObject 是受支持的,但是,如果你需要在其中执行渲染调用(例如,水反射/折射),它将无法正常工作。调用 Camera.Render(),你将看到以下消息:

Recursive rendering is not supported in SRP (are you calling Camera.Render from within a render pipeline?)

翻译过来就是:

SRP 不支持递归渲染(你是从渲染管道中调用 Camera.Render 吗?)

在这种情况下,URP 中应该将 OnWillRenderObject 替换为 begin/endCameraRendering(如上面的例子),并调用 RenderSingleCamera(),而不是 Camera.Render()。更改上面的示例,你将获得以下内容

**

void MyCameraRendering(ScriptableRenderContext context, Camera camera)
{
    ...
    if(camera == myEffectCamera)
    {
    ...
        UniversalRenderPipeline.RenderSingleCamera(context, camera);
    }
    ...
}

使用后处理的另一种方法是使用 ScriptableRendererFeature这篇文章很好地解释了使用 RenderFeature 的描边效果。ScriptableRendererFeature 可以让你将 ScriptableRenderPass(es) 注入到渲染管线的不同阶段,因此是创建后处理效果的强大工具。注入位置可以包含以下:

  • BeforeRendering
  • BeforeRenderingShadows
  • AfterRenderingShadows
  • BeforeRenderingPrepasses
  • AfterRenderingPrePasses
  • BeforeRenderingOpaques
  • AfterRenderingOpaques
  • BeforeRenderingSkybox
  • AfterRenderingSkybox
  • BeforeRenderingTransparents
  • AfterRenderingTransparents
  • BeforeRenderingPostProcessing
  • AfterRenderingPostProcessing
  • AfterRendering

这是 ScriptableRendererFeature 使用自定义材质执行 Blit 的简单示例:

**

public class CustomRenderPassFeature : ScriptableRendererFeature
{
    class CustomRenderPass : ScriptableRenderPass
    {
        CustomRPSettings _CustomRPSettings;
        RenderTargetHandle _TemporaryColorTexture;

        private RenderTargetIdentifier _Source;
        private RenderTargetHandle _Destination;

        public CustomRenderPass(CustomRPSettings settings)
        {
            _CustomRPSettings = settings;
        }

        public void Setup(RenderTargetIdentifier source, RenderTargetHandle destination)
        {
            _Source = source;
            _Destination = destination;
        }

        public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
        {
            _TemporaryColorTexture.Init("_TemporaryColorTexture");
        }

        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            CommandBuffer cmd = CommandBufferPool.Get("My Pass");

            if (_Destination == RenderTargetHandle.CameraTarget)
            {
                cmd.GetTemporaryRT(_TemporaryColorTexture.id, renderingData.cameraData.cameraTargetDescriptor, FilterMode.Point);
                cmd.Blit(_Source, _TemporaryColorTexture.Identifier());
                cmd.Blit(_TemporaryColorTexture.Identifier(), _Source, _CustomRPSettings.m_Material);
            }
            else
            {
                cmd.Blit(_Source, _Destination.Identifier(), _CustomRPSettings.m_Material, 0);
            }

            context.ExecuteCommandBuffer(cmd);
            CommandBufferPool.Release(cmd);
        }

        public override void FrameCleanup(CommandBuffer cmd)
        {
            if (_Destination == RenderTargetHandle.CameraTarget)
            {
                cmd.ReleaseTemporaryRT(_TemporaryColorTexture.id);
            }
        }
    }

    [System.Serializable]
    public class CustomRPSettings
    {
        public Material m_Material;
    }

    public CustomRPSettings m_CustomRPSettings = new CustomRPSettings();
    CustomRenderPass _ScriptablePass;

    public override void Create()
    {
        _ScriptablePass = new CustomRenderPass(m_CustomRPSettings);

        _ScriptablePass.renderPassEvent = RenderPassEvent.AfterRenderingOpaques;
    }

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        _ScriptablePass.Setup(renderer.cameraColorTarget, RenderTargetHandle.CameraTarget);
        renderer.EnqueuePass(_ScriptablePass);
    }
}

你可以通过单击 “Create > Rendering > Universal Render Pipeline > Renderer Feature” 来创建一个 ScriptableRendererFeature。你创建的功能必须添加到你的中 ForwardRenderer。为此,选择 ForwardRenderer,单击 Add Renderer Feature,然后选择要添加的功能。你可以在 Inspector 中公开属性,例如上面的例子中包含了一个材质球属性。