From Built-in to URP

Unity’s Scriptable Render Pipeline represents a great advance on the way that unity deals with graphics, giving more power to the users to customize the pipeline the way they want. I have started to use the Universal Render Pipeline (URP) recently and, despite all its advantages over the built-in pipeline, it still suffers of lack of documentation. I mean, you can find information of every function available in the package documentation, but it’s still hard to find examples and translations from built-in to URP. Unity’s docs are (were?) good because when you look for something, you usually find the explanation of that and sometimes it’s followed by an example of how to use that.

I am pretty sure that Unity is working on improving the current situation of the docs (specially regarding packages), but while that’s not available I decided to write this article with a kind of translation from built-in to URP. I’m doing this not only to help others, but to help myself as well, so I can find all things in one place.

Before starting, here are some useful links to help you dive into URP stuff:

Summary

Okay! So let’s go! All things here are mostly based on version 7.3 (version I’m using currently) sooo… you know… things might change. Some of the sections follow the built-in documentation order. The article is organized as follows:

  1. General Structure
  2. Shader Include Files
  3. Light Modes
  4. Variants
  5. Predefined Shader Preprocessor Macros
    1. Helpers
    2. Shadow Mapping
    3. Texture/Sampler Declaration Macros
  6. Built-in Shader Helper Functions
    1. Vertex Transformation Functions
    2. Generic Helper Functions
    3. Forward Rendering Helper Functions
    4. Screen-space Helper Functions
    5. Vertex-lit Helper Functions
  7. Built-in Shader Variables
    1. Lighting
  8. Random Stuff
    1. Shadows
    2. Fog
    3. Depth
    4. Etc.
  9. Post-processing/VFX
  10. Conclusion

General Structure 

First of all, add "RenderPipeline" = "UniversalPipeline" to your tags. Next, all URP shaders are written using HLSL embraced by HLSLPROGRAM/ENDHLSL/etc. macros. To avoid headaches, use them as well.

Built-inURP
CGPROGRAM
HLSLPROGRAM
HLSLPROGRAM
ENDCG
ENDHLSL
ENDHLSL
CGINCLUDE
HLSLINCLUDE
HLSLINCLUDE

Shader Include Files 

ContentBuilt-inURP
CoreUnity.cgincPackages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl
LightAutoLight.cgincPackages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl
ShadowsAutoLight.cgincPackages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl
Surface shadersLighting.cgincNone, but you can find a side project for this here

Other useful includes:

Light Modes 

Built-inURP
ForwardBaseUniversalForward
ForwardAddGone
Deferred and relatedUniversalGBuffer seems to have just been added to URP
Vertex and relatedGone
ShadowCasterShadowCaster
MotionVectorsNot suppoted yet

The other light modes supported are:

  • DepthOnly
  • Meta (for lightmap baking)
  • Universal2D

Variants 

URP support some variants, so depending on the things you are using, you might need to add some #pragma multi_compile for some of the following keywords:

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

Predefined Shader Preprocessor Macros 

Helpers 

Built-inURP
UNITY_PROJ_COORD(a)Gone. Do a.xy/a.w instead
UNITY_INITIALIZE_OUTPUT(typename)ZERO_INITIALIZE(typename)

Shadow Mapping 

You must include “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl”.

Built-inURP
UNITY_DECLARE_SHADOWMAP(tex)TEXTURE2D_SHADOW_PARAM(textureNamesamplerName)
UNITY_SAMPLE_SHADOW(texuv)SAMPLE_TEXTURE2D_SHADOW(textureNamesamplerNamecoord3)
UNITY_SAMPLE_SHADOW_PROJ(texuv)SAMPLE_TEXTURE2D_SHADOW(textureNamesamplerNamecoord4.xyz/coord4.w)

Texture/Sampler Declaration Macros 

Unity has a bunch of texture/sampler macros to improve cross compatibility between APIs, but people are not used to use them. Those still exist in URP, but now with different names and new additions. I will not put all of them here because it’s a lot, but you can check their definitions per platform in the API includes.

Built-inURP
UNITY_DECLARE_TEX2D(name)TEXTURE2D(textureName); SAMPLER(samplerName);
UNITY_DECLARE_TEX2D_NOSAMPLER(name)TEXTURE2D(textureName);
UNITY_DECLARE_TEX2DARRAY(name)TEXTURE2D_ARRAY(textureName); SAMPLER(samplerName);
UNITY_SAMPLE_TEX2D(nameuv)SAMPLE_TEXTURE2D(textureNamesamplerNamecoord2)
UNITY_SAMPLE_TEX2D_SAMPLER(namesamplernameuv)SAMPLE_TEXTURE2D(textureNamesamplerNamecoord2)
UNITY_SAMPLE_TEX2DARRAY(nameuv)SAMPLE_TEXTURE2D_ARRAY(textureNamesamplerNamecoord2index)
UNITY_SAMPLE_TEX2DARRAY_LOD(nameuvlod)SAMPLE_TEXTURE2D_ARRAY_LOD(textureNamesamplerNamecoord2indexlod)

Important to note that SCREENSPACE_TEXTURE has become TEXTURE2D_X. If you are working on some screen space effect for VR in Single Pass Instanced or Multi-view modes, you must declare the textures used with TEXTURE2D_X. This macro will handle for you the correct texture (array or not) declaration. You also have to sample the textures using SAMPLE_TEXTURE2D_X and use UnityStereoTransformScreenSpaceTex for the uv.

Built-in Shader Helper Functions 

You can find them all in “Packages/com.unity.render-pipelines.core/ShaderLibrary/SpaceTransforms.hlsl”.

Vertex Transformation Functions 

Built-inURP
float4 UnityObjectToClipPos(float3 pos)float4 TransformObjectToHClip(float3 positionOS)
float3 UnityObjectToViewPos(float3 pos)TransformWorldToView(TransformObjectToWorld(positionOS))

Generic Helper Functions 

Built-inURP
float3 WorldSpaceViewDir (float4 v)float3 GetWorldSpaceViewDir(float3 positionWS)Include “Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderVariablesFunctions.hlsl”
float3 ObjSpaceViewDir (float4 v)Gone. Do TransformWorldToObject(GetCameraPositionWS()) - objectSpacePosition;
float2 ParallaxOffset (half hhalf heighthalf3 viewDir)Gone? Copy from UnityCG.cginc
fixed Luminance (fixed3 c)real Luminance(real3 linearRgb)Include “Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl”
fixed3 DecodeLightmap (fixed4 color)real3 DecodeLightmap(real4 encodedIlluminancereal4 decodeInstructions)Include “Packages/com.unity.render-pipelines.core/ShaderLibrary/EntityLighting.hlsl”
decodeInstructions is used as half4(LIGHTMAP_HDR_MULTIPLIER, LIGHTMAP_HDR_EXPONENT, 0.0h, 0.0h) by URP
float4 EncodeFloatRGBA (float v)Gone? Copy from UnityCG.cginc
float DecodeFloatRGBA (float4 enc)Gone? Copy from UnityCG.cginc
float2 EncodeFloatRG (float v)Gone? Copy from UnityCG.cginc
float DecodeFloatRG (float2 enc)Gone? Copy from UnityCG.cginc
float2 EncodeViewNormalStereo (float3 n)Gone? Copy from UnityCG.cginc
float3 DecodeViewNormalStereo (float4 enc4)Gone? Copy from UnityCG.cginc

Forward Rendering Helper Functions 

Built-inURP
float3 WorldSpaceLightDir (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 ()Gone. You can try to use half3 VertexLighting(float3 positionWS, half3 normalWS)For VertexLighting(...) include “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl”

Screen-space Helper Functions 

Built-inURP
float4 ComputeScreenPos (float4 clipPos)float4 ComputeScreenPos(float4 positionCS)Include “Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderVariablesFunctions.hlsl”
float4 ComputeGrabScreenPos (float4 clipPos)Gone.

Vertex-lit Helper Functions 

Built-inURP
float3 ShadeVertexLights (float4 vertexfloat3 normal)Gone. You can try to use UNITY_LIGHTMODEL_AMBIENT.xyz + VertexLighting(...)For VertexLighting(...) include “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl”

A bunch of utilities can be found in “Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl”.

Built-in Shader Variables 

Most of the shader variables remains the same, except by lighting.

Lighting 

Built-inURP
_LightColor0_MainLightColorInclude “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl”
_WorldSpaceLightPos0_MainLightPositionInclude “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl”
_LightMatrix0Gone ? Cookies are not supported yet
unity_4LightPosX0unity_4LightPosY0unity_4LightPosZ0In URP, additional lights are stored in an array/buffer (depending on platform). Retrieve light information using Light GetAdditionalLight(uint i, float3 positionWS)Include “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl”
unity_4LightAtten0In URP, additional lights are stored in an array/buffer (depending on platform). Retrieve light information using Light GetAdditionalLight(uint i, float3 positionWS)Include “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl”
unity_LightColorIn URP, additional lights are stored in an array/buffer (depending on platform). Retrieve light information using Light GetAdditionalLight(uint i, float3 positionWS)Include “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl”
unity_WorldToShadowfloat4x4 _MainLightWorldToShadow[MAX_SHADOW_CASCADES + 1] or _AdditionalLightsWorldToShadow[MAX_VISIBLE_LIGHTS]Include “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl”

If you want to loop over all additional lights using GetAdditionalLight(...), you can query the additional lights count by using GetAdditionalLightsCount().

Random Stuff 

Shadows 

For more info about shadows, check “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl”.

Built-inURP
UNITY_SHADOW_COORDS(x)Gone? DIY, e.g. float4 shadowCoord : TEXCOORD0;
TRANSFER_SHADOW(a)a.shadowCoord = TransformWorldToShadowCoord(worldSpacePosition)With cascades on, do this on fragment to avoid visual artifacts
SHADOWS_SCREENGone. Not supported.

Fog 

For more info about fog, check “Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderVariablesFunctions.hlsl”.

Built-inURP
UNITY_FOG_COORDS(x)Gone? DIY, e.g. float fogCoord : TEXCOORD0;
UNITY_TRANSFER_FOG(ooutpos)o.fogCoord = ComputeFogFactor(clipSpacePosition.z);
UNITY_APPLY_FOG(coordcol)color = MixFog(colori.fogCoord);

Depth 

To use the camera depth texture, include “Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl” and the _CameraDepthTexture will be declared for you as well as helper the functions SampleSceneDepth(...) and LoadSceneDepth(...).

Built-inURP
LinearEyeDepth(sceneZ)LinearEyeDepth(sceneZ_ZBufferParams)Include “Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl”
Linear01Depth(sceneZ)Linear01Depth(sceneZ_ZBufferParams)Include “Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl”

Etc. 

Built-inURP
ShadeSH9(normal)SampleSH(normal)Include “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl”
unity_ColorSpaceLuminanceGone. Use Luminance()Include “Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl”

Post-processing/VFX 

URP does not support OnPreCullOnPreRenderOnPostRender and OnRenderImage. It does support OnRenderObject and OnWillRenderObject, but you might find issues depending on what you want to do. So, If you used to use those when creating your visual effects, I recommend learning how to use the new approaches available. The RenderPipelineManager provides the following injection points in the pipeline:

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

Example of usage:

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

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

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

Like I said, OnWillRenderObject is supported, however if you need to perform a render call inside of it (e.g. water reflection/refraction), it will not work. As soon as you call Camera.Render(), you will see the following message:

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

In this case, replace the OnWillRenderObject by begin/endCameraRendering (like the example above) and call RenderSingleCamera() from URP instead of Camera.Render(). Changing the example above, you would have something like

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

The other approach to work with Post-processing is to use a ScriptableRendererFeatureThis post has a great explanation of an outline effect using this feature. A ScriptableRendererFeature allows you to inject ScriptableRenderPass(es) at different stages of the pipeline, thus being a powerful tool for creating post-processing effects. The injection places are the following:

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

This is a simple example of a ScriptableRendererFeature performing a blit with a custom material:

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);
    }
}

You can create a ScriptableRendererFeature by clicking on “Create > Rendering > Universal Render Pipeline > Renderer Feature”. The feature that you have created has to be added to your ForwardRenderer. To do so, select the ForwardRenderer, click on “Add Renderer Feature” and select the feature to be added. You can expose properties in the feature inspector, for example, if you add the example above, you will see a material slot available.

Conclusion 

That’s it for now. I will try to keep this updated (narrator: he won’t) according to the new things I’m learning or new features that I see added. If you have comments or suggestions, you can find me on twitter.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值