Bird
Raised Fist0
Unityframework~5 mins

Custom shader fundamentals in Unity

Choose your learning style10 modes available
Introduction

Custom shaders let you control how objects look in a game. They help create special effects like shiny surfaces or glowing lights.

You want to make a character's skin look shiny or wet.
You need a glowing effect on a magic item.
You want to create a cartoon or stylized look for your game.
You want to add special lighting that Unity's built-in shaders don't have.
You want to optimize graphics by writing simple shaders for specific effects.
Syntax
Unity
Shader "Custom/ExampleShader" {
  Properties {
    _Color ("Color", Color) = (1,1,1,1)
  }
  SubShader {
    Pass {
      CGPROGRAM
      #pragma vertex vert
      #pragma fragment frag

      struct appdata {
        float4 vertex : POSITION;
      };

      struct v2f {
        float4 pos : SV_POSITION;
      };

      fixed4 _Color;

      v2f vert (appdata v) {
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);
        return o;
      }

      fixed4 frag (v2f i) : SV_Target {
        return _Color;
      }
      ENDCG
    }
  }
}

The Properties block defines inputs you can change in the editor.

The SubShader contains the actual code that runs on the GPU.

Examples
This shader colors everything red by default.
Unity
Shader "Custom/SimpleColor" {
  Properties {
    _Color ("Color", Color) = (1,0,0,1)
  }
  SubShader {
    Pass {
      CGPROGRAM
      #pragma vertex vert
      #pragma fragment frag

      struct appdata { float4 vertex : POSITION; };
      struct v2f { float4 pos : SV_POSITION; };

      fixed4 _Color;

      v2f vert(appdata v) {
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);
        return o;
      }

      fixed4 frag(v2f i) : SV_Target {
        return _Color;
      }
      ENDCG
    }
  }
}
This shader applies a texture image to the object.
Unity
Shader "Custom/TextureShader" {
  Properties {
    _MainTex ("Texture", 2D) = "white" {}
  }
  SubShader {
    Pass {
      CGPROGRAM
      #pragma vertex vert
      #pragma fragment frag

      sampler2D _MainTex;
      struct appdata {
        float4 vertex : POSITION;
        float2 uv : TEXCOORD0;
      };
      struct v2f {
        float4 pos : SV_POSITION;
        float2 uv : TEXCOORD0;
      };

      v2f vert(appdata v) {
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);
        o.uv = v.uv;
        return o;
      }

      fixed4 frag(v2f i) : SV_Target {
        return tex2D(_MainTex, i.uv);
      }
      ENDCG
    }
  }
}
Sample Program

This shader colors any object with a blue color by default. You can change the color in the Unity editor. It uses a simple vertex and fragment program to set the color.

Unity
Shader "Custom/BasicColorShader" {
  Properties {
    _Color ("Color", Color) = (0, 0.5, 1, 1)
  }
  SubShader {
    Tags { "RenderType"="Opaque" }
    Pass {
      CGPROGRAM
      #pragma vertex vert
      #pragma fragment frag

      struct appdata {
        float4 vertex : POSITION;
      };

      struct v2f {
        float4 pos : SV_POSITION;
      };

      fixed4 _Color;

      v2f vert(appdata v) {
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);
        return o;
      }

      fixed4 frag(v2f i) : SV_Target {
        return _Color;
      }
      ENDCG
    }
  }
  FallBack "Diffuse"
}
OutputSuccess
Important Notes

Always test your shader on simple objects first to see how it looks.

Use the Unity editor's material inspector to tweak shader properties live.

Shaders run on the graphics card, so small mistakes can cause no rendering or errors.

Summary

Custom shaders let you control how objects look by writing small programs.

They use a Properties block for inputs and a SubShader block for GPU code.

Start simple, then add effects like textures or lighting step by step.

Practice

(1/5)
1. What is the main purpose of a custom shader in Unity?
easy
A. To control exactly how objects look by writing small programs
B. To create 3D models from scratch
C. To write game logic and rules
D. To manage user input events

Solution

  1. Step 1: Understand the role of shaders

    Shaders define how surfaces appear by controlling colors, lighting, and textures.
  2. Step 2: Identify what custom shaders do

    Custom shaders let you write small programs to control object appearance beyond built-in options.
  3. Final Answer:

    To control exactly how objects look by writing small programs -> Option A
  4. Quick Check:

    Custom shader = control object look [OK]
Hint: Shaders control object appearance with code [OK]
Common Mistakes:
  • Confusing shaders with 3D modeling
  • Thinking shaders handle game logic
  • Assuming shaders manage input
2. Which of the following is the correct way to start a simple Unity shader code block?
easy
A. ShaderBegin
B. StartShader
C. CGPROGRAM
D. ProgramCG

Solution

  1. Step 1: Recall Unity shader syntax

    Unity shaders use CGPROGRAM to start the programmable shader code block.
  2. Step 2: Check options for correct keyword

    Only CGPROGRAM is the valid keyword to begin the shader code section.
  3. Final Answer:

    CGPROGRAM -> Option C
  4. Quick Check:

    Shader code block starts with CGPROGRAM [OK]
Hint: Look for CGPROGRAM to start shader code [OK]
Common Mistakes:
  • Using made-up keywords like StartShader
  • Confusing CGPROGRAM with other terms
  • Misspelling the keyword
3. What will be the output color of this simple shader fragment code?
fixed4 frag() : SV_Target {
    return fixed4(1, 0, 0, 1);
}
medium
A. Solid red color
B. Solid green color
C. Solid blue color
D. Transparent black color

Solution

  1. Step 1: Analyze the returned color vector

    The function returns fixed4(1, 0, 0, 1), which means red=1, green=0, blue=0, alpha=1.
  2. Step 2: Interpret color components

    Red is full intensity, green and blue are zero, alpha is fully opaque.
  3. Final Answer:

    Solid red color -> Option A
  4. Quick Check:

    fixed4(1,0,0,1) = red [OK]
Hint: RGBA (1,0,0,1) means solid red [OK]
Common Mistakes:
  • Mixing up color channel order
  • Ignoring alpha channel
  • Assuming zero means white
4. Identify the error in this shader snippet:
Shader "Custom/Simple" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
    }
    SubShader {
        Pass {
            CGPROGRAM
            fixed4 frag() : SV_Target {
                return _Color;
            }
            ENDCG
        }
    }
}
medium
A. Missing semicolon after _Color property declaration
B. _Color cannot be used directly in fragment shader
C. frag function missing input parameters
D. Incorrect use of ENDCG keyword

Solution

  1. Step 1: Check Properties block syntax

    The property declaration is correct: no semicolon needed, parentheses balanced.
  2. Step 2: Verify shader code block

    Properties like _Color must be declared inside CGPROGRAM as 'uniform fixed4 _Color;' to be used; direct use causes undeclared identifier error.
  3. Final Answer:

    _Color cannot be used directly in fragment shader -> Option B
  4. Quick Check:

    Properties need uniform declaration [OK]
Hint: Declare Properties as uniforms in CGPROGRAM [OK]
Common Mistakes:
  • Assuming semicolons are needed in Properties
  • Not declaring uniform for _Color
  • Thinking ENDCG is incorrect
5. You want to write a custom shader that colors an object red only if its x-coordinate is positive, otherwise blue. Which fragment shader code correctly implements this?
hard
A. fixed4 frag() : SV_Target { if (POSITION.x > 0) return fixed4(1,0,0,1); else return fixed4(0,0,1,1); }
B. fixed4 frag(float3 pos) : SV_Target { if (pos.x > 0) return fixed4(1,0,0,1); else return fixed4(0,0,1,1); }
C. fixed4 frag(float3 pos : TEXCOORD0) : SV_Target { return pos.x > 0 ? fixed4(1,0,0,1) : fixed4(0,0,1,1); }
D. fixed4 frag(float3 pos : POSITION) : SV_Target { return pos.x > 0 ? fixed4(1,0,0,1) : fixed4(0,0,1,1); }

Solution

  1. Step 1: Understand input semantics

    To access vertex position in fragment shader, use input parameter with semantic POSITION.
  2. Step 2: Check code correctness

    fixed4 frag(float3 pos : POSITION) : SV_Target { return pos.x > 0 ? fixed4(1,0,0,1) : fixed4(0,0,1,1); } correctly declares pos with POSITION semantic and uses conditional operator to return red or blue.
  3. Step 3: Identify errors in other options

    fixed4 frag(float3 pos) : SV_Target { if (pos.x > 0) return fixed4(1,0,0,1); else return fixed4(0,0,1,1); } lacks semantic, fixed4 frag(float3 pos : TEXCOORD0) : SV_Target { return pos.x > 0 ? fixed4(1,0,0,1) : fixed4(0,0,1,1); } uses TEXCOORD0 which is wrong for position, fixed4 frag() : SV_Target { if (POSITION.x > 0) return fixed4(1,0,0,1); else return fixed4(0,0,1,1); } uses POSITION as variable incorrectly.
  4. Final Answer:

    fixed4 frag(float3 pos : POSITION) : SV_Target { return pos.x > 0 ? fixed4(1,0,0,1) : fixed4(0,0,1,1); } -> Option D
  5. Quick Check:

    Use POSITION semantic for position input [OK]
Hint: Use POSITION semantic to get vertex position [OK]
Common Mistakes:
  • Missing POSITION semantic in input
  • Using wrong semantic like TEXCOORD0
  • Referencing POSITION as variable directly
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard