A Unity URP technical art study featuring real-time interactive ripples, foam injection, refraction, chromatic dispersion, caustics, and stylized pool look development.
2026-07-31.123312.mp4
This project explores a real-time interactive water system built with RenderTexture simulation, Shader Graph, and C#.
Mouse clicks or scene interactors can inject disturbances into the water surface, producing expanding ripples and localized foam. The resulting height data is converted into interaction normals and masks, then combined with the base water shading, refraction, chromatic dispersion, caustics, and post-processing to create a dreamy pool scene.
- Real-time ripple simulation using rotating RenderTextures
- Click and object interaction through ripple and foam injection
- Height-map-to-normal conversion using four-neighbor sampling
- Depth-based shallow and deep water color
- Dual scrolling normal maps and procedural vertex waves
- Interaction-driven refraction and chromatic dispersion
- Separate foam simulation affecting color, normal, and smoothness
- Caustics overlay and URP post-processing
The wave simulation uses three RenderTextures:
previousHeightRT- previous-frame height datacurrentHeightRT- current height datatempRT- write target for the next frame
Each update reads the current and previous states, writes the next result into tempRT, and then swaps the C# references. The textures remain in GPU memory; only their roles are exchanged.
Previous state + current state → wave update → reference swap
Interaction is handled through a small runtime pipeline:
Mouse Click / WaterInteractor
↓
WaterSurface.cs
├─ InjectRipple()
├─ InjectFoam()
└─ UpdateWaveSimulation()
↓
Interaction Height Texture + Interaction Foam Texture
↓
Water Shader Graph
The height texture stores the simulated ripple state. The foam texture is updated separately so that foam can use its own injection, decay, and material controls.
The interaction height texture is sampled at four neighboring UV positions. The horizontal and vertical height differences form a gradient, which is converted into a ripple normal.
A separate ripple mask is derived from the absolute height value and controls where the interaction should affect the final water shading.
Height Field → Neighbor Sampling → Height Gradient → Ripple Normal
Height Field → Absolute Value → Ripple Mask
Ripple Normal + Ripple Mask → Final Water Response
float hL = SampleHeight(uv + float2(-texelSize.x, 0));
float hR = SampleHeight(uv + float2( texelSize.x, 0));
float hD = SampleHeight(uv + float2(0, -texelSize.y));
float hU = SampleHeight(uv + float2(0, texelSize.y));
float2 gradient = float2(hL - hR, hD - hU);
float3 rippleNormal = normalize(float3(gradient, 1.0));
float height = SampleHeight(uv);
float rippleMask = saturate(abs(height) * RippleMaskStrength);The base water material combines:
- Two scrolling normal maps with different tiling and speeds
- Fresnel-based edge response
- Depth-based shallow/deep color blending
- Two directional sine waves for real vertex displacement
- Adjustable smoothness and transparency
The two normal layers and two vertex-wave directions reduce visible repetition and create a less uniform surface.
Foam is simulated in a separate RenderTexture. The sampled foam value is remapped with Smoothstep and Power to control its shape and concentration.
The final foam amount is used to:
- Add foam color over the base water
- Blend water and foam smoothness
- Blend foam normal detail with the water normal
Refraction offsets the screen UV using the water normal before sampling Scene Color.
Chromatic dispersion extends this idea by sampling Scene Color three times with different UV offsets, then rebuilding the final RGB result:
- Red: positive offset
- Green: original screen UV
- Blue: negative offset
The offset direction follows the interaction normal, so the color separation changes with the ripple direction.
Caustics | Normal-Based Refraction | Chromatic Dispersion
The final scene also includes:
- Animated caustics on the pool floor
- Bloom
- Color grading
- Film Grain
- Vignette
- RenderTexture ripple simulation and buffer swapping
- Ripple and foam injection logic
- Water Shader Graph development
- Height-field normal reconstruction
- Depth color, Fresnel, and vertex-wave setup
- Refraction and chromatic dispersion
- Interactive foam material response
- Caustics and post-processing
- Scene integration and visual presentation
- Engine: Unity 2022.3.62f1c1
- Render Pipeline: Universal Render Pipeline
- Shader Development: Shader Graph
- Runtime Logic: C#
- Core Techniques: RenderTexture, Graphics.Blit, height-field simulation, Scene Color sampling
- Clone or download this repository.
- Open the project with Unity 2022.3.62f1c1
- Open the main demo scene. Main Scene: Assets/Scenes/pool.unity
- Enter Play Mode.
- Click the water surface or use the scene interactors to generate ripples.
All 3D models used in this project were sourced from third-party asset websites and remain the property of their respective creators.

