Unity Shader Graph: Creating Custom Shaders Without Code
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.
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.
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:
- Create a new Shader Graph (Create > Shader > URP > Unlit Graph).
- 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.
- Connect the output of the Sample Texture 2D node to the Color input of the Unlit Master node.
- 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.
To add simple lighting:
- Add a Normal Vector node (set to Tangent space).
- Add a Vector3 node for the light direction. Expose it as a property.
- Add a Dot Product node and connect the Normal Vector and light direction to it.
- Use a One Minus node on the Dot Product result.
- Connect the output of the One Minus node to the Emission input of the Unlit Master node.
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:
- Create a Custom Function node. Find it by right-clicking and selecting Create Node > Node > Input > Advanced > Custom Function.
- 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.
- 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.
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:
- 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.
- 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.
- Connect the output of the Custom Function node to the Emission input of the Master Node.
Procedural textures and patterns use math nodes. Fract, Sine, and Cosine nodes are useful. To create a brick pattern:
- Create a UV node.
- 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.
- Connect the Tiling and Offset node’s output to a Fract node. This creates repeating UV coordinates.
- 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.
- 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.
UV manipulation can create visual effects. Tiling and Offset controls UV coordinates. For a scrolling texture effect:
- Create a UV node.
- Create a Tiling and Offset node.
- Create a Time node.
- Create a Vector2 node. Connect the Time node to the X component of the Vector2 node. This sets the horizontal scroll direction.
- Connect the Vector2 node to the Offset input of the Tiling and Offset node.
- 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.
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.
- Official Unity documentation: Shader Graph Documentation provides detailed information.
- Unity Learn: Introduction to Shader Graph provides a comprehensive introduction.
- Community forums: Find solutions and share your work on the Unity forums.
- Lively Livestream: Watch this Shader Graph livestream tutorial from Feb 2019.