Skip to content

Instantly share code, notes, and snippets.

@SabinT
Last active August 5, 2021 13:59
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SabinT/d354f59c4cf385316b30a207349ccfc2 to your computer and use it in GitHub Desktop.
Save SabinT/d354f59c4cf385316b30a207349ccfc2 to your computer and use it in GitHub Desktop.
A water shader in Unity (generates vertex displacement and per-pixel normasl)
#ifndef WaterSineSeries
#define WaterSineSeries
// Based on GPU GEMS, Chapter 1: Effective Water Simulation from Physical Models
// https://developer.download.nvidia.com/books/HTML/gpugems/gpugems_ch01.html
#define TWO_PI 6.2831853
/**
* x,y: coordinates
* t: time
* L: wavelength
* A: amplitude
* dir: direction of wavefront
* speed: wavelengths per second
* Outputs: h (displacement), derivatives along X and Y
*/
void singleWave(
float2 xy,
float t,
float L,
float A,
float2 dir,
float speed,
out float h,
out float2 derivatives)
{
float w = TWO_PI / L; // frequency
float phi = speed * w;
float freqFactor = dot(dir, xy) * w;
float phase = t * phi;
h = A * sin(freqFactor + phase);
float dX = w * dir.x * A * cos(freqFactor + phase);
float dY = w * dir.y * A * cos(freqFactor + phase);
derivatives = float2(dX, dY);
}
/**
* Returns the displacement of water surface on a 2-D plane.
* The wave is modeled as a sum of sine series.
* Direction of waves is the normalized vector outwards from UV origin.
* Use UV co-ordinates as (x, y) for consistent per-pixel normal calculations.
* Params:
* xy: 2-D Coordinates on water surface
* center: center of waves
* scale: scale.xz is multiplied with "xy", scale.y is multiplied with amplitude.
* t: Time in seconds
* wavelengthMultiplier: Gets multiplied to the wavelengths of all harmonics/constituent waves.
* Default primary wavelength is 0.1 units.
* speedMultiplier: Gets multiplied to the speed of all harmonics.
Default reference speed is 1 wavelength/second.
* displacement (out): The units by which to raise the vertex at position.
* normal (out): The normal of the displaced surface. Note: You may have to rotate this normal according to
* the object's orientation.
*/
void water_float(
float2 xy,
float2 center,
float3 scale,
float t,
float waveLengthMultiplier,
float speedMultiplier,
out float displacement,
out float3 normal)
{
// xy = (xy - center)* scale.xz; // Scale the plane
float ampMultiplier = scale.y;
// TODO make constituent waves customizable
// TODO add 3D scaler
// Accumulators
float h = 0;
float2 derivatives = float2(0,0);
float2 xyTransformed = (xy - center) * scale.xz;
float2 dir = normalize(xyTransformed);
// Wave 1
float A1 = ampMultiplier * 0.1;
float L1 = waveLengthMultiplier * 1.0;
float speed1 = speedMultiplier * 1.0;
float h1;
float2 derivatives1;
singleWave(
xyTransformed,
t,
L1,
A1,
dir,
speed1,
h1, // out
derivatives1 // out
);
h = h + h1;
derivatives = derivatives + derivatives1;
// Wave 2
float A2 = ampMultiplier * 0.09;
float L2 = waveLengthMultiplier * 0.5;
float speed2 = speedMultiplier * 2;
float h2;
float2 derivatives2;
singleWave(
xyTransformed - float2(0.0, 0.0),
t,
L2,
A2,
dir,
speed2,
h2, // out
derivatives2 // out
);
h = h + h2;
derivatives = derivatives + derivatives2;
displacement = h;
normal = normalize(float3(-derivatives.x, -derivatives.y, 1));
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment