Unity Term Book
Graphics & Rendering

Material & Shader

Shader là chương trình GPU định nghĩa cách vẽ pixel — Material là instance của Shader với giá trị cụ thể như màu sắc, texture và độ bóng (smoothness).

Hãy tưởng tượng...

Shader là công thức pha sơn (màu gỗ, màu kim loại, màu kính...). Material là mỗi hộp sơn cụ thể — cùng công thức sơn kim loại nhưng một hộp màu vàng, một hộp màu bạc, một hộp độ bóng cao hơn. Bạn có thể tạo ra hàng nghìn Material từ chỉ một Shader, mỗi cái trông khác nhau. Một GameObject dùng Mesh Renderer để "tô màu" mesh bằng Material được chỉ định.

Khái niệm chi tiết

Shader là chương trình HLSL (High Level Shading Language) chạy trực tiếp trên GPU. Nó gồm ít nhất hai phần: Vertex Shader (biến đổi vị trí đỉnh từ 3D sang màn hình 2D) và Fragment/Pixel Shader (tính màu cuối cùng của mỗi pixel). Unity trừu tượng hoá điều này qua ShaderLab syntax và Surface Shader hoặc URP’s Lit/Unlit Shader Graph.

Material là asset tham chiếu đến một Shader và lưu giá trị cho các properties của Shader đó: màu chính (_BaseColor), texture ( _BaseMap), độ metallic (_Metallic), độ nhám ( _Smoothness)… Mỗi Renderer có thể có một hay nhiều Material slot tương ứng với số sub-mesh.

Về hiệu năng, Material instancing rất quan trọng: nhiều object dùng cùng một Material asset sẽ được gom vào một draw call (GPU instancing hoặc static batching). Nếu dùng renderer.material (tạo bản sao riêng) thay vì renderer.sharedMaterial, mỗi object sẽ có draw call riêng và tốn nhiều bộ nhớ hơn.

Unity cung cấp sẵn các Shader tiêu chuẩn: URP/Lit (PBR đầy đủ), URP/Unlit (không tính ánh sáng), Sprites/Default (2D), Particles/Standard Unlit (particle). Shader Graph và HLSL code cho phép tạo hiệu ứng hoàn toàn tuỳ chỉnh.

Sơ đồ quan hệ Shader → Material → Renderer

📄 Shader (HLSL program)

URP/Lit — định nghĩa properties + render logic

_BaseColor_Metallic_BaseMap
▼ instantiate

Material A

_BaseColor = Red
_Metallic = 0.9

Material B

_BaseColor = Blue
_Metallic = 0.0

Material C

_BaseColor = Gold
_Metallic = 1.0

▼ assign to

🏠 House
Mat A

🚗 Car
Mat B

🏆 Trophy
Mat C

🔑 Key
Mat C

Trophy + Key dùng chung Mat C → 1 draw call (batching)

Hướng dẫn thực hành

1

Tạo Material mới

Project → chuột phải → Create → Material. Đổi tên rõ ràng (Metal_Gold, Ground_Grass...).

2

Chọn Shader phù hợp

Inspector → Shader dropdown: URP/Lit cho PBR, URP/Unlit cho vật phát sáng tự nhiên, Sprites/Default cho 2D.

3

Gán Texture và điều chỉnh properties

Kéo thả texture vào slot Base Map, điều chỉnh Metallic (0=plastic, 1=metal), Smoothness (0=matte, 1=mirror).

4

Gán Material vào MeshRenderer

Kéo Material vào slot Materials[0] của MeshRenderer, hoặc kéo thẳng lên GameObject trong Scene View.

5

Thay đổi property qua script

renderer.sharedMaterial.SetColor("_BaseColor", color) — hoặc dùng MaterialPropertyBlock để tránh tạo bản sao Material.

Trình mô phỏng tương tác

Điều chỉnh các tham số PBR để xem cầu thay đổi bề mặt — mô phỏng Material Inspector trong Unity.

// Material Properties

_BaseColor = "#4488ff"

_Metallic = 0.50

_Smoothness = 0.70

_Emission = 0.00

Shader: URP/Lit

Nhựa bóng màu xanh

Ví dụ Code

Cơ bản

Thay đổi màu và độ metallic của Material tại runtime.

using UnityEngine;

public class MaterialController : MonoBehaviour
{
  Renderer rend;

  void Start()
  {
      rend = GetComponent<Renderer>();

      // sharedMaterial: dùng chung với tất cả object → không tạo bản sao
      rend.sharedMaterial.SetColor("_BaseColor", Color.red);

      // material (không shared): tạo bản sao riêng cho object này
      rend.material.SetFloat("_Metallic", 0.9f);
      rend.material.SetFloat("_Smoothness", 0.8f);
  }

  public void Flash(Color color)
  {
      // Kỹ thuật nhấp nháy: set emission color
      rend.material.SetColor("_EmissionColor", color * 2f);
      DynamicGI.SetEmissive(rend, color * 2f);
  }
}

Ví dụ Code

Nâng cao

MaterialPropertyBlock — thay đổi property từng object mà không tạo bản sao Material, không phá batching.

using UnityEngine;

// Tô màu từng instance khác nhau mà KHÔNG phá 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);          // Đọc trạng thái hiện tại
      mpb.SetColor(ColorID, color);       // Chỉnh màu
      rend.SetPropertyBlock(mpb);          // Áp dụng — không tạo Material mới
  }

  public void SetEmission(Color emitColor)
  {
      rend.GetPropertyBlock(mpb);
      mpb.SetColor(EmitID, emitColor);
      rend.SetPropertyBlock(mpb);
  }
}

📌 Ghi nhớ nhanh

  • Shader = công thức, Material = instance với giá trị cụ thể
  • sharedMaterial: dùng chung, không tốn RAM thêm
  • MaterialPropertyBlock: per-instance, không phá batching
  • Metallic=1+Smoothness=1 = gương hoàn hảo
  • Shader.PropertyToID() nhanh hơn truyền string mỗi frame

⚠️ Lỗi thường gặp

  • ❌ Dùng renderer.material trong vòng lặp spawn

    Mỗi lần tạo bản sao Material mới → rò rỉ bộ nhớ

    ✅ Dùng MaterialPropertyBlock hoặc sharedMaterial

  • ❌ Quên bật Emission trong Material settings

    SetColor("_EmissionColor",...) không có tác dụng nếu chưa bật

    ✅ Material Inspector → tick "Emission" checkbox trước