In this lesson, you will learn how to structure your custom shaders to prevent redundant GPU operations by leveraging specific Pass types for different rendering needs.
When we write custom shaders, it is easy to fall into the trap of putting all our logic—lighting, fog, decals, and custom effects—into a single Pass. However, the GPU executes every line of code in that pass for every pixel covered by the object, even if the output isn't needed for every rendering state. By reorganizing your shader into specialized passes, you prevent the GPU from performing unnecessary math.
The Problem: Over-Calculating
Imagine a shader that handles both base lighting and an expensive overlay effect in one pass. If you have an object that is partially obscured or doesn't require that overlay in certain conditions, the GPU still wastes cycles calculating the overlay because it is bundled with the base pass. This is a primary cause of high "pixel cost" in the Frame Debugger.
The Solution: Pass Segmentation
Instead of one "mega-pass," split your logic. Use the Tags system to ensure your passes only run when necessary. For example, keep your light-independent calculations (like simple base colors or constant data) in a separate pass that doesn't trigger complex lighting calculations.
Consider this structural change in your ShaderLab code:
// Instead of one heavy Pass
Pass {
Name "CombinedExpensivePass"
// Complex lighting AND expensive effect math here
}
// Split for performance
Pass {
Name "BasePass"
Tags { "LightMode" = "ForwardBase" }
// Only basic lighting
}
Pass {
Name "EffectPass"
Tags { "LightMode" = "Always" }
// Only the expensive effect, conditionally triggered
}
By separating these, you allow Unity’s renderer to skip the "EffectPass" if it isn't required for the current camera view or state. Furthermore, ensure you are utilizing ZWrite and ZTest appropriately within your passes. If a pass is only needed for outlines or effects that don't need to write to the depth buffer, setting ZWrite Off prevents the GPU from doing unnecessary depth-testing work, which significantly reduces the overdraw pressure on your hardware.
Key Takeaway: Never lump distinct rendering logic into a single pass; isolate expensive calculations into specialized passes to ensure the GPU only processes the instructions strictly required for the current frame.