Create Stunning Water Effects in Unity with Shader Graph
Imagine your game world. What’s missing? Is it the gentle lapping of waves against a shore, or a shimmering oasis in a desert landscape? Water can bring life and dynamism to any environment, and with Unity’s Shader Graph, even beginners can craft stunning, believable water effects. Don’t be intimidated. We’re going to break down the process of creating a visually appealing and performant water system step-by-step. Forget complicated code – we’ll harness the power of visual scripting to create something truly beautiful.
Setting Up the Water Plane and Material
First, we need a surface for our water. In your Unity scene, create a Plane (GameObject > 3D Object > Plane). Scale it to the desired size. Larger planes may need more subdivisions for the wave effect, but start small for performance.
Next, create a new material (Project window > Create > Material) and name it something descriptive, like "WaterMaterial".
Now, create a new Shader Graph (Project window > Create > Shader > Unlit Graph) and name it "WaterShader". Double-click on the “WaterShader” to open the Shader Graph editor. Then, assign this shader to the "WaterMaterial".
Finally, drag the “WaterMaterial” onto the Plane in your scene. You’ll now be able to preview the effects of the shader graph on the plane.
Pitfall Alert: Forgetting to assign the material to the plane is a common mistake! Double-check that your plane is using the material you’re editing.
Wave Displacement with Shader Graph
The key to realistic water is movement. We’ll simulate waves using the Normal Vector and Time nodes. This approach allows for simple, effective wave displacement without requiring complex physics simulations.
Create a Time Node: In the Shader Graph, create a Time node. This node outputs the game time, which we’ll use to animate the waves.
Create a Normal Vector Node: Create a Normal Vector node and set its space to Tangent. This gives us the surface normal, which we’ll use to offset the vertices.
Create a Tiling and Offset Node: Create a Tiling and Offset node. This allows us to move the normal map across the surface of the water.
Sample Texture2D Node: Use the Sample Texture 2D node to load a Normal Map texture. Set the Texture parameter to a seamless normal map. This texture determines the shape of the waves. A simple, subtle normal map works best.
Connect the Nodes: Connect the Time node’s output to the Offset input of the Tiling and Offset node. Connect the UV output of the Tiling and Offset node to the UV input of the Sample Texture 2D node.
Multiply and Add: Multiply the output of the Sample Texture 2D node by a Float node (set to a small value like 0.1) to control the wave amplitude. Add this result to the Normal Vector node. Connect the output of the addition to the Normal input of the Master node.
Common Mistake: Setting the amplitude too high will result in distorted, unrealistic-looking water. Start with a small value and increase it gradually.
Basic Reflections using Reflection Probes
Reflections add a crucial layer of realism to water. We’ll use Reflection Probes to simulate reflections, a performant solution for most scenarios.
Create a Reflection Probe: In your scene, create a Reflection Probe (GameObject > Light > Reflection Probe). Position it above the water surface.
Adjust the Probe: Adjust the probe’s size and position to encompass the area you want reflected in the water.
Sample Reflection Probe Node: In the Shader Graph, create a Sample Reflection Probe node.
World Reflection Vector Node: Create a World Reflection Vector Node. Connect the Normal Vector (World Space) to the Normal Input of the World Reflection Vector node.
Connect the Nodes: Connect the World Reflection Vector node’s output to the Vector input of the Sample Reflection Probe node. Connect the output of the Sample Reflection Probe node to the Emission input of the Master node.
Challenge: Static Reflection Probes only capture a snapshot of the scene. For dynamic reflections, consider using Realtime Reflection Probes (at a performance cost) or screen space reflections.
Customizable Color Controls
Giving users control over the water’s color adds a layer of customization. We’ll use properties in the Shader Graph to expose color controls in the material inspector.
Create Color Properties: In the Shader Graph’s Blackboard (the panel on the left), create two Color properties: “DeepWaterColor” and "ShallowWaterColor".
Lerp Node: Create a Lerp (Linear Interpolation) node.
Depth Node: Create a Scene Depth Node.
One Minus Node: Create a One Minus node and connect the Scene Depth node to it.
Connect the Nodes: Connect “DeepWaterColor” to the A input of the Lerp node and “ShallowWaterColor” to the B input. Connect the One Minus node to the T input. Connect the output of the Lerp node to the Base Color input of the Master node.
Actionable Insight: Experiment with different color combinations to achieve the desired water appearance. Consider using a gradient texture for more complex color variations.
Scripting Basic Parameter Control
While the Shader Graph provides visual control, scripting can add dynamic parameter adjustments. Let’s create a simple script to control the wave amplitude.
Create a C# Script: Create a new C# script (Project window > Create > C# Script) named "WaterController".
Attach the Script: Attach the “WaterController” script to the Plane GameObject in your scene.
Script Code: Add the following code to the “WaterController” script:
using UnityEngine;
public class WaterController : MonoBehaviour
{
public float waveAmplitude = 0.1f;
private Material waterMaterial;
void Start()
{
waterMaterial = GetComponent<Renderer>().material;
}
void Update()
{
waterMaterial.SetFloat("_Amplitude", waveAmplitude);
}
}
- Expose the Parameter: In the Shader Graph, create a Float property named "_Amplitude". Connect this property to the multiplication node we used for wave amplitude. Ensure its Reference is exactly "_Amplitude".
Value Proposition: Scripting allows for real-time adjustments based on game events, like weather changes or player interactions, adding another level of immersion.
By following these steps, you can create a visually appealing and performant water system in Unity using Shader Graph. Remember to experiment with different textures, colors, and parameters to achieve the desired effect. Don’t be afraid to push the boundaries and create something truly unique. The possibilities are endless!