Every shader must include at least one SubShader. In the SubShader, you define one or more passes (each of which contains your actual shader code) and specify metadata that informs Unity how and when to use that SubShader. This setup is crucial because Unity selects the most compatible SubShader based on your target hardware and active render pipeline.
Basic Structure Example:
SubShader {
Tags { "RenderType" = "Opaque" }
LOD 100
Pass {
// For Built-in RP, you might see CGPROGRAM/ENDCG,
// while URP/HDRP typically use HLSLPROGRAM/ENDHLSL.
CGPROGRAM
// Compiler directives and shader code go here...
ENDCG
}
}
Tags are key–value pairs that provide essential metadata for a SubShader. They affect shader behavior, drawing order, and how the shader interacts with replacement systems or post-processing. Below is an overview of the main tags and their pipeline-specific nuances.
Queue tags determine the order in which objects are drawn by the GPU. The render queue affects layering, transparency, and masking. The GPU sorts draw calls by these numerical values:
Tags { "Queue" = "Geometry" }
Background:Geometry:AlphaTest:Transparent:Overlay:Pipeline-Specific Notes:
Built-in & URP: Queue tags are set directly in the shader and appear in the material Inspector.
HDRP (2024/2025): Queue tags can be overridden by Material Settings (Rendering Priority). To enforce draw order in HDRP, set the queue in the SubShader tags and adjust Material Order in the Inspector.
The RenderType tag categorizes shaders into logical groups for global replacements or post-processing. This tag helps Unity identify which shaders can be replaced at runtime (e.g., via Camera.RenderWithShader).
Tags { "RenderType"="Opaque" }
Common RenderType Values:
Opaque:Transparent:TransparentCutout:Background:Overlay:Built-in & URP: Directly specified and visible.
HDRP: Although still used, HDRP may manage some transparency or advanced effects via additional material settings.
Beyond Queue and RenderType, several other tags refine shader behavior:
TreeOpaque: Used for opaque tree bark.TreeTransparentCutout: Used for tree leaves that use alpha cutout.TreeBillboard: Used for trees rendered as billboards.Grass: For full 3D grass models.GrassBillboard: For grass rendered as billboards to improve performance.Beyond Queue and RenderType, several other tags refine shader behavior:
RequireOptions:DisableBatching:ForceNoShadowCasting:Tags { "ForceNoShadowCasting" = "True" }
IgnoreProjector:CanUseSpriteAtlas:PreviewType:GrabPass:GrabPass { "TextureName" }
UsePass:UsePass "Shader/PassName"
Usage: RenderType tags help with shader replacement systems (e.g., using Camera.RenderWithShader) and enable filtering when applying post-processing effects.
Pipeline-Specific Notes:
Built-in & URP: RenderType tags work as defined and help organize shader behavior.
HDRP: Although available, HDRP’s advanced material system often uses additional parameters to control transparency and rendering effects.
(Works only with URP/Built-in RP) Tags are labels that show how and when our shaders are processed. Like a GameObject Tag, these can be used to recognize how a shader will be rendered or how a group of shaders will behave graphically.
Blending is the cornerstone of creating visually rich materials like glass, fire, smoke, and holograms. Let’s break down this critical stage in the rendering pipeline.
Blending combines the fragment shader’s output color (SrcValue) with the color already in the render target (DstValue). Think of it as layering in Photoshop—but in real-time 3D. This “merging” stage occurs after the fragment shader and is responsible for incorporating transparency, depth, and stencil data into the final pixel color.
Blending options can be written in different fields: within the SubShader field or the Pass field, the position will depend on the number of passes and the final result that we need.
Blending combines fragment shader output (SrcValue) with the existing render target color (DstValue) using the equation:
FinalColor = SrcFactor * SrcValue [OP] DstFactor * DstValue
Default operation ([OP]) is Add, making the typical equation:
FinalColor = SrcFactor * SrcValue + DstFactor * DstValue
| Factor | RGB Value | Use Case |
|---|---|---|
| One | (1,1,1) |
Full color contribution (e.g., additive glow). |
| SrcAlpha | (A,A,A) |
Standard transparency (UI, glass). |
| OneMinusSrcAlpha | (1-A,1-A,1-A) |
Inverse alpha masking (soft particles). |
| DstColor | (R,G,B) |
Multiplicative effects (stained glass). |
Let’s say we have an RGB pixel with the following destination values:
DstValue = [0.5R, 0.45G, 0.35B]
[0.5R, 0.45G, 0.35B][0.25R, 0.20G, 0.12B][0.75R, 0.65G, 0.47B]Some frequently used blend factors include:
Off: Disables blending.One: (1, 1, 1) – Leaves the value unchanged.Zero: (0, 0, 0) – Eliminates the value.SrcColor / SrcAlpha: Uses the corresponding components from the source.OneMinusSrcColor / OneMinusSrcAlpha: Uses (1 - source component) values.DstColor / DstAlpha: Uses the destination color or alpha.OneMinusDstColor / OneMinusDstAlpha: Uses (1 - destination component) values.To use blending in your shader, you'll need to modify the “Queue” tag—by default, it’s set to “Geometry” (making the object opaque). Change it to “Transparent” to indicate that the object should be rendered after opaque objects and blended accordingly.
Blend presets provide standard configurations for frequently used blending effects. The table below outlines these presets, the associated Blend command, and a brief description of their typical usage.
| Blend Preset | Blend Command | Description |
|---|---|---|
| Traditional Alpha Blending | Blend SrcAlpha OneMinusSrcAlpha |
The classic method for transparent materials, where the source’s alpha determines transparency. |
| Additive Blending | Blend One One |
Often used for particles or glow effects, as it adds color values, making bright effects stand out. |
| Mild Additive Blending | Blend OneMinusDstColor One |
A softer additive effect where the influence of the destination color is partially accounted for. |
| Multiplicative Blending | Blend DstColor Zero |
Multiplies the source with the destination, useful for effects like stained glass. |
| Multiplicative Blending x2 | Blend DstColor SrcColor |
An intensified version of multiplicative blending, doubling the effect for richer colors. |
| Overlay Blending | Blend SrcColor One |
Uses the source color as the dominant factor, creating an overlay effect. |
| Soft Light Blending | Blend OneMinusSrcColor One |
Produces a soft lighting effect, ideal for subtle highlights and shadows. |
| Negative Color Blending | Blend Zero OneMinusSrcColor |
Creates a negative color effect by inverting the source color’s contribution. |
Pipeline-Specific Notes on Blending:
Built-in Render Pipeline: Blending commands are written directly in the shader. Depth writes (ZWrite) are typically disabled for transparent materials.
URP (2024): You can set blend factors in the shader; however, URP’s material system may also allow artists to adjust these settings via the Inspector. Use HLSLPROGRAM/ENDHLSL blocks in URP shaders.
HDRP: Blending is often managed indirectly via material settings—especially for transparency. In HDRP, you might not see explicit Blend commands in the shader code because transparency is handled through the Surface Type setting (Opaque vs. Transparent) in the material Inspector.
Common Blend Presets quick reference:
// 1. Traditional Alpha Blending (Transparent Materials)
Blend SrcAlpha OneMinusSrcAlpha
// 2. Additive Blending (Fire, Lasers)
Blend One One
// 3. Multiplicative Blending (Projectors, Shadows)
Blend DstColor Zero
Option 1: Hardcode in the shader
SubShader {
Tags { "Queue" = "Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off // Disable depth writes for proper transparency
Pass {
// Shader code here...
}
}
Option 2: Let artists control blending via the Material Inspector
Properties {
[Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend ("Src Factor", Float) = 1
[Enum(UnityEngine.Rendering.BlendMode)] _DstBlend ("Dst Factor", Float) = 0
}
SubShader {
Blend [_SrcBlend] [_DstBlend]
Pass {
// Shader code here...
}
}
Pipeline-specific architectural blocks comparison:
Built-in Render Pipeline
SubShader {
Tags { "Queue"="Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off // Disable depth writes for correct transparency
}
URP (2024)
SubShader {
Tags { "RenderPipeline"="UniversalPipeline" "Queue"="Transparent" }
Blend [_SrcBlend] [_DstBlend]
ZWrite Off
}
| Issue | Fix |
|---|---|
| HDRP Transparency Not Working | Set Surface Type → Transparent in Material Inspector. |
| URP Additive Artifacts | Adjust render queue: Tags { "Queue"="Transparent+100" }. |
| Depth Sorting Issues | Use Offset -1, -1 to manually adjust depth in Built-in Pipeline. |
In some cases, standard blending (such as using the "SrcAlpha OneMinusSrcAlpha" blend mode) produces smooth gradients of transparency using fractional alpha values. However, for certain effects—like vegetation cutouts or space portal effects—you might require a binary (on/off) approach to transparency. This is where AlphaToMask comes into play.
Shader "Custom/AlphaToMaskExample" {
Properties {
// Define your texture and other properties here
_MainTex ("Texture", 2D) = "white" {}
}
SubShader {
Tags { "RenderType" = "Opaque" }
// Activate the AlphaToMask command
AlphaToMask On
Pass {
// Shader code that writes to the alpha channel
CGPROGRAM
// Your shader code here...
ENDCG
}
}
}
Note: Unlike blending, you don't need to modify the Render Queue or add additional transparency tags when using AlphaToMask. The fourth color channel “A” is automatically treated as a binary mask.
AlphaToMask On in the SubShader or Pass converts the alpha output into a binary mask without any further changes.The ColorMask command allows you to restrict the GPU to writing only specific color channels (Red, Green, Blue, or Alpha). By default, the GPU writes all channels (RGBA), but you might want to limit the output for certain effects or optimizations.
Purpose: ColorMask lets you select which components of the output color should be written to the render target. This is useful, for example, if you want to create a red-only effect or isolate one channel for debugging.
Usage Examples:
ColorMask R: Only the red channel is written, so the output appears red.ColorMask G: Only the green channel is written.ColorMask B: Only the blue channel is written.ColorMask A: Only the alpha channel is written, which can be used to debug transparency.ColorMask RG: Writes both red and green channels, allowing for channel mixing.Shader "Custom/ColorMaskExample" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
}
SubShader {
Tags { "Queue"="Geometry" }
// Limit output to the RGB channels (no alpha)
ColorMask RGB
Pass {
CGPROGRAM
// Your shader code here...
ENDCG
}
}
}
Pipeline Note: The ColorMask command is compatible with both the Built-in Render Pipeline and Scriptable Render Pipelines (URP/HDRP). You can declare it at either the SubShader or Pass level, depending on the desired scope.