Upgrade a Custom Built-In Shader to URP

Unity's Render Pipeline Converter cannot automatically upgrade materials that use custom Built-In Render Pipeline shaders. Rewrite the incompatible shader code, recreate the effect in Shader Graph, or replace it with a maintained URP shader.

Upgrade One Shader First

Duplicate the project, migrate one representative material, and verify the result before changing every dependency.

  1. Record the shader's properties, passes, render states, and expected appearance.
  2. Update the shader structure and material data for URP.
  3. Check compilation, SRP Batcher compatibility, visuals, shadows, and target-platform performance.
VRChat note

Do not perform this URP migration in a VRChat project. VRChat's official setup documentation says not to use URP or HDRP. Replace imported URP effects with shaders designed for Unity's Built-in Render Pipeline and the intended VRChat platform.

Decide Whether to Rewrite, Rebuild, or Replace

Existing shader First option to evaluate
Simple textured or colored unlit shader Replace with Universal Render Pipeline/Unlit or perform the manual HLSL upgrade below
Simple material assembled from common operations Recreate it in Shader Graph
Maintained asset-store or package shader Install the publisher's URP-compatible version
Custom lighting, outlines, multiple passes, or special render states Plan a manual ShaderLab/HLSL rewrite
Effect no longer important to the project Replace it with a standard URP shader

Shader Graph Custom Function nodes can insert HLSL into vertex or fragment stages, but Unity documents important limits: they cannot add attributes to the input/output structures, define extra passes, or define extra render-state commands such as ColorMask. Use a ShaderLab shader when the effect needs those features.

Create a Migration Inventory

Before editing the shader:

  1. Back up or branch the project. Unity describes Render Pipeline Converter changes as irreversible.
  2. Find every material that uses the shader.
  3. Capture reference images under representative lighting.
  4. List every exposed property, keyword, pass, fallback, and custom render state.
  5. Identify any scripts or animation clips that address material properties by name.
  6. Confirm the active URP package and Unity version.

After a project moves to URP, materials using incompatible custom shaders appear magenta. Treat that as an error indicator, not a conversion result.

1. Change the Shader Program and Include

For the basic unlit shader demonstrated in Unity's upgrade guide:

Built-In shader element URP replacement
CGPROGRAM / ENDCG HLSLPROGRAM / ENDHLSL
UnityCG.cginc Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl
No pipeline tag Add "RenderPipeline" = "UniversalPipeline" to the SubShader tags
fixed values Use half or float

Core.hlsl supplies core SRP definitions, URP variables, matrices, and transform functions. It does not supply a complete lighting implementation or default application-specific vertex structures.

Keep the existing RenderType, queue, culling, blending, depth, stencil, and pass tags only after confirming that URP supports them and that they still describe the desired rendering behavior.

2. Define URP Attributes, Varyings, and Transforms

Replace Built-In convenience structures with explicit input and output structures:

  • An Attributes structure receives object-space position, UVs, normals, tangents, vertex color, or other mesh data the shader needs.
  • A Varyings structure carries interpolated data from the vertex stage to the fragment stage.
  • The clip-space position must use the SV_POSITION semantic.
  • Use TransformObjectToHClip to convert an object-space position to homogeneous clip space.

Unity's URP examples use suffixes to identify coordinate spaces, including OS for object space and HCS or CS for clip space. Consistent suffixes make transform mistakes easier to spot.

Add only the attributes and interpolators the shader uses. Extra interpolators consume resources and can reduce compatibility with constrained platforms.

3. Move Material Properties into UnityPerMaterial

For SRP Batcher compatibility, declare material properties inside one constant buffer:

CBUFFER_START(UnityPerMaterial)
float4 _Color;
float4 _BaseMap_ST;
CBUFFER_END

Every pass in the shader must use the same UnityPerMaterial constant-buffer layout. Unity's Inspector reports whether the shader is compatible with the SRP Batcher and warns when a material property is placed in another constant buffer.

Texture and sampler declarations stay outside that constant buffer:

TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);

4. Update Texture Sampling, Tiling, and Offset

Unity's upgrade example uses these URP conventions:

  1. Rename the main texture to _BaseMap.
  2. Add [MainTexture] to _BaseMap and [MainColor] to the primary color property when those properties represent the material's main texture and color.
  3. Store tiling and offset in _BaseMap_ST.
  4. Transform UVs with TRANSFORM_TEX.
  5. Sample the texture with SAMPLE_TEXTURE2D.

Remove [NoScaleOffset] when the material should expose Tiling and Offset. Define the vertex function after the constant-buffer declaration when it uses TRANSFORM_TEX, because the macro reads the matching _ST value.

5. Rebuild Lighting and Additional Passes

The structural changes above are enough for Unity's simple unlit example, not for every custom shader.

A lit or multi-pass shader may also need:

  • URP lighting functions and light data
  • main-light and additional-light handling
  • normal and tangent transforms
  • baked and indirect lighting
  • shadow receiving
  • a compatible shadow-caster pass
  • depth, depth-normals, or metadata passes required by active URP features
  • revised blending, stencil, or culling behavior

Use Unity's custom-lighting documentation for HLSL lighting functions. If the shader depends on custom passes or render states that Shader Graph cannot express, keep the implementation in ShaderLab/HLSL or pair it with a Renderer Feature where appropriate.

6. Restore Material Data

Changing property names can disconnect existing material values, scripts, and animation clips. Compare the migrated material with the inventory:

  • base texture, color, and tiling
  • normal, mask, emission, and detail textures
  • transparency mode and blend state
  • cutoff thresholds
  • keywords and shader variants
  • render queue
  • script and animation property bindings

When possible, preserve property names. When a rename is required, migrate the stored values deliberately instead of relying on the material to infer the new mapping.

7. Validate the Shader

Use a representative scene and target build:

  1. Resolve every shader compiler error and warning.
  2. Confirm the material no longer renders magenta.
  3. Check the shader's SRP Batcher status in the Inspector.
  4. Inspect the frame with Unity's Frame Debugger to confirm the expected passes run.
  5. Compare lighting, shadows, transparency, culling, depth, fog, and instancing.
  6. Test every renderer path, quality level, graphics API, and platform the application supports.
  7. Profile the migrated shader in the real scene.

An Editor preview proves neither platform compatibility nor acceptable runtime cost.

Common Built-In-to-URP Replacements

Built-In pattern URP approach
UnityObjectToClipPos TransformObjectToHClip
appdata convenience input Explicit Attributes structure
v2f output Explicit Varyings structure
tex2D SAMPLE_TEXTURE2D with TEXTURE2D and SAMPLER declarations
Standalone material values A consistent UnityPerMaterial constant buffer
fixed precision half or float
UnityCG.cginc URP ShaderLibrary includes appropriate to the feature

These mappings address common structural differences. They do not automatically reproduce a Built-In surface shader's lighting model or generate missing passes.

Troubleshooting

Help! The material is still magenta after adding the URP tag.

Open the Console and fix the first shader compiler error. The pipeline tag only tells Unity which pipeline the SubShader targets; incompatible includes, functions, types, semantics, or pass code still need to be rewritten.

Help! The shader works but the Inspector says SRP Batcher incompatible.

Place every material property in one UnityPerMaterial constant buffer and use the same buffer layout in every pass. Keep texture and sampler declarations outside the constant buffer.

Help! Tiling and Offset do not affect the texture.

Confirm the texture property has a matching _ST value in UnityPerMaterial, transform the UV with TRANSFORM_TEX, and remove [NoScaleOffset] when those controls should be visible.

Help! The object renders but no longer receives or casts shadows.

The basic unlit conversion does not recreate a custom lighting model or every supporting pass. Review the shader's lighting code, LightMode pass tags, shadow receiving logic, and shadow-caster implementation against the current URP documentation.

Help! Existing materials lost their textures or animated values.

Compare the old and new property names. Restore stored values and update scripts or animation bindings that reference renamed properties such as _MainTex and _BaseMap.

Help! Shader Graph cannot reproduce an extra pass or render state.

Shader Graph Custom Function nodes cannot define extra passes, add input or output structure fields, or define render states such as ColorMask. Use a ShaderLab/HLSL shader for those requirements.

Official References

Continue Learning