Material & Shader
A Shader is a GPU program that defines how pixels are drawn — a Material is an instance of that Shader with concrete values like color, texture, and smoothness.
Imagine...
A Shader is a paint recipe (wood, metal, glass...). A Material is a specific can of that paint — same metal recipe, but one can is gold, one is silver, one is glossier. You can make thousands of Materials from a single Shader, each looking different. A GameObject uses a Mesh Renderer to "paint" the mesh with the assigned Material.
The concept in detail
A Shader is an HLSL (High Level Shading Language)
program that runs on the GPU. It has at least two stages: a Vertex Shader (transforms
3D vertices onto the 2D screen) and a Fragment/Pixel Shader (computes the final color
of each pixel). Unity wraps this in ShaderLab, Surface Shaders, or URP’s
Lit/Unlit Shader Graph.
A Material is an asset that references a Shader and
stores values for its properties: main color (_BaseColor), texture (
_BaseMap), metallic (_Metallic), roughness (
_Smoothness)… Each Renderer can have one or more Material slots, matching
sub-meshes.
For performance, Material instancing matters: many
objects sharing the same Material asset can collapse into one draw call (GPU
instancing or static batching). If you use renderer.material (a private copy)
instead of renderer.sharedMaterial, each object gets its own draw call and extra
memory.
Unity ships standard shaders: URP/Lit (full PBR), URP/Unlit (no
lighting), Sprites/Default (2D), Particles/Standard Unlit
(particles). Shader Graph and HLSL let you build fully custom effects.
Shader → Material → Renderer
📄 Shader (HLSL program)
URP/Lit — defines properties + render logic
Material A
_BaseColor = Red
_Metallic = 0.9
Material B
_BaseColor = Blue
_Metallic = 0.0
Material C
_BaseColor = Gold
_Metallic = 1.0
🏠 House
Mat A
🚗 Car
Mat B
🏆 Trophy
Mat C
🔑 Key
Mat C
Trophy + Key share Mat C → 1 draw call (batching)
Hands-on steps
Create a new Material
Project → right-click → Create → Material. Name it clearly (Metal_Gold, Ground_Grass...).
Pick the right Shader
Inspector → Shader dropdown: URP/Lit for PBR, URP/Unlit for self-lit surfaces, Sprites/Default for 2D.
Assign textures and tweak properties
Drop a texture onto Base Map. Metallic (0=plastic, 1=metal), Smoothness (0=matte, 1=mirror).
Assign the Material to a MeshRenderer
Drop it into MeshRenderer Materials[0], or drag it onto the GameObject in the Scene view.
Change a property from script
renderer.sharedMaterial.SetColor("_BaseColor", color) — or use a MaterialPropertyBlock to avoid cloning the Material.
Interactive simulator
Tune PBR parameters and watch the sphere's surface change — a stand-in for Unity's Material Inspector.
// Material Properties
_BaseColor = "#4488ff"
_Metallic = 0.50
_Smoothness = 0.70
_Emission = 0.00
Shader: URP/Lit
Shiny blue plastic
Code example
BasicChange a Material's color and metallic at runtime.
using UnityEngine;
public class MaterialController : MonoBehaviour
{
Renderer rend;
void Start()
{
rend = GetComponent<Renderer>();
// sharedMaterial: shared with every object → no extra copy
rend.sharedMaterial.SetColor("_BaseColor", Color.red);
// material (not shared): a private copy for this object
rend.material.SetFloat("_Metallic", 0.9f);
rend.material.SetFloat("_Smoothness", 0.8f);
}
public void Flash(Color color)
{
// Flash trick: set emission color
rend.material.SetColor("_EmissionColor", color * 2f);
DynamicGI.SetEmissive(rend, color * 2f);
}
}Code example
AdvancedMaterialPropertyBlock — per-object properties without cloning the Material or breaking batching.
using UnityEngine;
// Color each instance differently WITHOUT breaking GPU Instancing/Batching
public class InstanceColorizer : MonoBehaviour
{
static readonly int ColorID = Shader.PropertyToID("_BaseColor");
static readonly int EmitID = Shader.PropertyToID("_EmissionColor");
MaterialPropertyBlock mpb;
Renderer rend;
void Awake()
{
rend = GetComponent<Renderer>();
mpb = new MaterialPropertyBlock();
}
public void SetColor(Color color)
{
rend.GetPropertyBlock(mpb); // Read current state
mpb.SetColor(ColorID, color); // Change color
rend.SetPropertyBlock(mpb); // Apply — no new Material
}
public void SetEmission(Color emitColor)
{
rend.GetPropertyBlock(mpb);
mpb.SetColor(EmitID, emitColor);
rend.SetPropertyBlock(mpb);
}
}📌 Quick recap
- ▸Shader = recipe, Material = instance with concrete values
- ▸sharedMaterial: shared, no extra RAM
- ▸MaterialPropertyBlock: per-instance, keeps batching
- ▸Metallic=1 + Smoothness=1 = a perfect mirror
- ▸Shader.PropertyToID() is faster than a string every frame
⚠️ Common mistakes
❌ Using renderer.material inside a spawn loop
A new Material clone every time → memory leak
✅ Use a MaterialPropertyBlock or sharedMaterial
❌ Forgetting to enable Emission on the Material
SetColor("_EmissionColor",...) does nothing until Emission is on
✅ Material Inspector → tick the "Emission" checkbox first