Daily free asset available! Did you claim yours today?
The cover for Unity Shader Graph: Creating Custom Shaders Without Code

Unity Shader Graph: Creating Custom Shaders Without Code

February 25, 2025

Tired of shader code? Unlock stunning visuals with Shader Graph—no coding required. Understanding Shader Graph enhances both visual quality and workflow efficiency. If you need assets to use in your shaders, consider Strafekit, an asset marketplace. If you’re looking for inspiration, perhaps building your own Low Poly Fantasy Village to display your shaders on!

Introduction to Shader Graph

Shader Graph is a visual scripting system in Unity. It builds shaders by connecting nodes in a graph-like interface. This simplifies shader creation.

Shader Graph simplifies shader logic and is easier to tweak. Real-time feedback allows rapid iteration, eliminating the need for complex HLSL code. Photograph of a vast mountain range at sunset, with the peaks catching the last rays of light

The Shader Graph interface includes the graph editor, master node, and blackboard. The graph editor visually constructs your shader. The master node defines the final output. The blackboard manages shader properties.

Shader Graph supports Built-in, URP (Universal Render Pipeline), and HDRP (High Definition Render Pipeline). Choose the pipeline that fits your project’s needs, considering each pipeline’s features and performance.

Core Concepts and Node Types

Nodes are the building blocks of Shader Graph. They perform specific operations on data.

Common node types include Input, Output, Math, Utility, and Texture nodes. Input nodes provide data. Output nodes define the final shader output. Math nodes perform calculations. Utility nodes offer helper functions. Texture nodes sample textures.

Data flows through the graph via connections between nodes. Data types include Floats, Vectors, Colors, and Textures.

Some commonly used nodes include Sample Texture 2D for sampling textures, Normal Vector to provide surface normals, and PBR Master, which defines the output for physically-based rendering shaders. Photograph of a serene lake reflecting a forest with autumnal foliage

Creating data flows connects the output of one node to the input of another. Data is manipulated as it passes through the graph.

Creating a Simple Shader: A Step-by-Step Tutorial

Let’s build a basic unlit shader:

  1. Create a new Shader Graph (Create > Shader > URP > Unlit Graph).

Create Unlit Graph

  1. Add a Texture 2D property to the blackboard. Connect it to a Sample Texture 2D node. Connect the Texture 2D property to the Texture input of the Sample Texture 2D node.

Texture and Sample Texture

  1. Connect the output of the Sample Texture 2D node to the Color input of the Unlit Master node.

Connect to Master Node

  1. Add a Color property to the blackboard. Connect it to a Multiply node, along with the Sample Texture 2D output. Connect the Multiply node to the Unlit Master node’s Color input.

Add Color and Multiply

To add simple lighting:

  1. Add a Normal Vector node (set to Tangent space).
  2. Add a Vector3 node for the light direction. Expose it as a property.
  3. Add a Dot Product node and connect the Normal Vector and light direction to it.
  4. Use a One Minus node on the Dot Product result.
  5. Connect the output of the One Minus node to the Emission input of the Unlit Master node. Photograph of a close-up of moss and lichen growing on a rock face, showcasing intricate textures

Expose parameters like the texture and color through the blackboard. This allows adjustments in the Material Inspector without modifying the shader graph.

Test your shader by creating a new material. Use the shader and apply it to a GameObject in your scene. Iterate on the shader by adjusting parameters and node connections.

Advanced Techniques and Effects

For custom lighting, use the Custom Function node. This allows direct HLSL code within the graph.

Here’s how to integrate custom HLSL code:

  1. Create a Custom Function node. Find it by right-clicking and selecting Create Node > Node > Input > Advanced > Custom Function.

Custom Function Node

  1. In the Custom Function node’s settings, set the Type to "String". Input your HLSL code as a string. Paste the HLSL code into the “Source” field.

Custom Function Settings

  1. Define your input and output variables. In the Custom Function node’s settings, add input ports by clicking the “+” button next to “Inputs.” Specify the name and data type for each input. Add output ports for your function’s return values.

Custom Function Inputs/Outputs

Here’s an example:

float MyCustomLighting(float3 normal, float3 lightDir)
{
    float diffuse = dot(normal, lightDir);
    return diffuse;
}

This HLSL code calculates diffuse lighting. The Custom Function node takes the normal and light direction as inputs and outputs the result.

To use this code:

  1. Add a Normal Vector node to the graph. Set its Space to "Tangent". Connect the Normal Vector node to the normal input of the Custom Function node.
  2. Create a Vector3 node for the light direction. Expose it as a property on the blackboard and rename it "_LightDirection". Connect this Vector3 node to the lightDir input of the Custom Function node.
  3. Connect the output of the Custom Function node to the Emission input of the Master Node.

Custom Lighting Example Photograph of a winding river cutting through a lush green valley from an aerial perspective

Procedural textures and patterns use math nodes. Fract, Sine, and Cosine nodes are useful. To create a brick pattern:

  1. Create a UV node.
  2. Create a Tiling and Offset node. Connect the UV node’s UV output to the Tiling and Offset node’s UV input. Adjust the Tiling in the Tiling and Offset node to control brick size; set X and Y to 2.
  3. Connect the Tiling and Offset node’s output to a Fract node. This creates repeating UV coordinates.
  4. Connect the Fract node’s output to two Step nodes. For the first Step node, connect a Float node (value 0.5) to the In input and the Fract node’s output to the Edge input. For the second Step node, reverse these connections: connect the Fract node’s output to the In input and the Float node (value 0.5) to the Edge input.
  5. Multiply the outputs of the two Step nodes using a Multiply node. Connect the Multiply node to the Albedo of the PBR Master node to visualize the brick pattern.

Procedural Texture Example

UV manipulation can create visual effects. Tiling and Offset controls UV coordinates. For a scrolling texture effect:

  1. Create a UV node.
  2. Create a Tiling and Offset node.
  3. Create a Time node.
  4. Create a Vector2 node. Connect the Time node to the X component of the Vector2 node. This sets the horizontal scroll direction.
  5. Connect the Vector2 node to the Offset input of the Tiling and Offset node.
  6. Connect the output of the Tiling and Offset node to the UV input of a Sample Texture 2D node. Then connect the output of the Sample Texture 2D node to the Albedo of the PBR Master node.

UV Manipulation Example Photograph of a starry night sky over a desert landscape, with the Milky Way clearly visible.

Animation uses the Time node to drive shader changes. Sine or Cosine nodes create oscillating effects. Connect the Time node to the input of a Sine node. Connect the Sine output to the Offset of a Tiling and Offset node. This creates a wave-like animation on a texture.

Optimization Strategies for Shader Graph

Shader performance is crucial.

Minimize complex calculations and simplify graphs. Reduce texture lookups. Use LODs. Create different shader variations for different levels of detail. Profiling tools in Unity help identify performance bottlenecks. Use them to optimize your shaders. Consider implementing object pooling in Unity for performance.

Integrating Shader Graph with Scripting

C# scripts can access and modify shader properties using the Material class.

using UnityEngine;

public class ShaderController : MonoBehaviour
{
    public Material myMaterial; // Assign in the Inspector

    void Update()
    {
        // Change the color of the material
        myMaterial.SetColor("_Color", Color.red);

        // Access a float property
        float currentValue = myMaterial.GetFloat("_MyFloat");
        myMaterial.SetFloat("_MyFloat", currentValue + 0.1f);
    }
}

Attach this script to a GameObject in your scene. Assign the material using your Shader Graph shader to the myMaterial field in the Inspector. The _Color and _MyFloat properties must also be defined on the Shader Graph’s blackboard for the script to function.

The _Color property refers to a Color property on your Shader Graph’s blackboard. _MyFloat represents a Float property on your Shader Graph’s blackboard. The script modifies these properties every frame.

Create dynamic effects by sending data from scripts to shaders.

myMaterial.SetFloat("_MyFloat", playerHealth); // Assuming "_MyFloat" is a float property

Use MaterialPropertyBlocks for efficient property overrides. This avoids creating new materials.

Troubleshooting Common Issues

Resolve compilation errors by checking node connections. Look for data type mismatches.

Identify performance bottlenecks using the Unity Profiler. Look for expensive nodes or texture lookups.

Visual artifacts may be caused by incorrect normal vectors. Double-check node connections and data.

Keep graphs organized and well-commented. Use subgraphs to reuse node setups. Test shaders on different platforms and devices.

Resources and Further Learning

Wayline, the comprehensive game development platform, can provide you with a range of tools to support your game development journey, including asset marketplaces and AI assistance.