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.
Duplicate the project, migrate one representative material, and verify the result before changing every dependency.
- Record the shader's properties, passes, render states, and expected appearance.
- Update the shader structure and material data for URP.
- Check compilation, SRP Batcher compatibility, visuals, shadows, and target-platform performance.
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:
- Back up or branch the project. Unity describes Render Pipeline Converter changes as irreversible.
- Find every material that uses the shader.
- Capture reference images under representative lighting.
- List every exposed property, keyword, pass, fallback, and custom render state.
- Identify any scripts or animation clips that address material properties by name.
- 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
Attributesstructure receives object-space position, UVs, normals, tangents, vertex color, or other mesh data the shader needs. - A
Varyingsstructure carries interpolated data from the vertex stage to the fragment stage. - The clip-space position must use the
SV_POSITIONsemantic. - Use
TransformObjectToHClipto 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:
- Rename the main texture to
_BaseMap. - Add
[MainTexture]to_BaseMapand[MainColor]to the primary color property when those properties represent the material's main texture and color. - Store tiling and offset in
_BaseMap_ST. - Transform UVs with
TRANSFORM_TEX. - 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:
- Resolve every shader compiler error and warning.
- Confirm the material no longer renders magenta.
- Check the shader's SRP Batcher status in the Inspector.
- Inspect the frame with Unity's Frame Debugger to confirm the expected passes run.
- Compare lighting, shadows, transparency, culling, depth, fog, and instancing.
- Test every renderer path, quality level, graphics API, and platform the application supports.
- 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
- Unity 6 Manual: Upgrade Custom Shaders for URP Compatibility
- Unity 6 Manual: Convert Shaders to URP with the Render Pipeline Converter
- Unity 6 Manual: Introduction to Custom Lighting in URP
- Unity 6 Manual: URP ShaderLab Pass Tags
- Unity 6 Manual: Scriptable Render Pipeline Batcher
- Unity Shader Graph: Package Documentation
- VRChat Creation: Getting Started