z4gon / water-caustics-shader-unity

Water caustics shader with triplanar projection, made in Unity for URP

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Water Caustics Shader

Water Caustics Shader implemented with Shader Graph in Unity 2021.3.10f1

Screenshots

Picture

7.mp4

Table of Content

Resources

Scene Setup

  • Built a basic pool and put some primitive objects inside.
  • Used the stlylized skybox to make the scene look more polished.

Picture

HLSL

Main Light

  • Based on the example for implementing custom lighting in Shader Graph, we define the custom HLSL function to get the main light.
  • SHADERGRAPH_PREVIEW will be true while developing the shader in the Shader Graph node editor.
void MainLight_half(
    in float3 WorldPos,
    out half3 Direction,
    out half3 Color,
    out half DistanceAtten,
    out half ShadowAtten
)
{
    #if SHADERGRAPH_PREVIEW
        Direction = half3(0.5, 0.5, 0);
        Color = 1;
        DistanceAtten = 1;
        ShadowAtten = 1;
    #else
        #if SHADOWS_SCREEN
            half4 clipPos = TransformWorldToHClip(WorldPos);
            half4 shadowCoord = ComputeScreenPos(clipPos);
        #else
            half4 shadowCoord = TransformWorldToShadowCoord(WorldPos);
        #endif

        Light mainLight = GetMainLight(shadowCoord);
        Direction = mainLight.direction;
        Color = mainLight.color;
        DistanceAtten = mainLight.distanceAttenuation;
        ShadowAtten = mainLight.shadowAttenuation;
    #endif
}

UV Rotation

float2 Unity_Rotate_Degrees_float(float2 UV, float2 Center, float Rotation)
{
    Rotation = Rotation * (3.1415926f/180.0f);
    UV -= Center;

    float s = sin(Rotation);
    float c = cos(Rotation);

    float2x2 rMatrix = float2x2(c, -s, s, c);

    rMatrix *= 0.5;
    rMatrix += 0.5;
    rMatrix = rMatrix * 2 - 1;

    UV.xy = mul(UV.xy, rMatrix);
    UV += Center;

    return UV;
}

Triplanar Projection

  • Based on the HLSL code from the Shader Graph Node to do Triplanar Projection.
  • The modifications for this include passing in a Speed and Rotation for the UVs.
  • The offset will be calculated with Time and Speed.
  • The rotation will be used with the previously defined HLSL function.
  • This returns a projection based on the world position of the vertices, using each plane as an UV mapping.
    • This means the projection will always be the same, no matter the shape or position of the object.
    • There will be a blending done using the Normals orientation and a blend parameter.
void TriplanarProjection_float(
    in Texture2D Texture,
    in SamplerState Sampler,
    in float3 Position,         // world space
    in float3 Normal,           // world space
    in float Tile,
    in float Blend,

    // UV manipulation
    in float Speed,
    in float Rotation,

    out float4 Out
)
{
    float3 Node_UV = Position * Tile;

    // animate UVs
    float Offset_UV = _Time.y * Speed;

    float3 Node_Blend = pow(abs(Normal), Blend);
    Node_Blend /= dot(Node_Blend, 1.0);

    float4 Node_X = SAMPLE_TEXTURE2D(Texture, Sampler, Unity_Rotate_Degrees_float(Node_UV.zy, 0, Rotation) + Offset_UV);
    float4 Node_Y = SAMPLE_TEXTURE2D(Texture, Sampler, Unity_Rotate_Degrees_float(Node_UV.xz, 0, Rotation) + Offset_UV);
    float4 Node_Z = SAMPLE_TEXTURE2D(Texture, Sampler, Unity_Rotate_Degrees_float(Node_UV.xy, 0, Rotation) + Offset_UV);

    Out = Node_X * Node_Blend.x + Node_Y * Node_Blend.y + Node_Z * Node_Blend.z;
}

Shader Graph

Caustics Tiling and Speed

  • Define a Custom Function Node using the Triplanar Projection we defined in HLSL.
  • Set a Caustics Texture, a Tiling and a Speed for the Offset of the UVs.

Picture

Caustics Texture Rotation

  • Make one of the Caustic Textures rotated by the amount determined by Caustic Texture Rotation.

Picture

Caustics Distortion

  • Use the Caustics Distortion Speed multiplied by Time, to offset a Vector2 composed by the RG channels of the World Position of the vertices.

  • Divide by Caustics Distortion Factor, to further modify the distortion.

  • Use Caustics Distortion Scale to control the scale of the noise.

Picture

5.mp4

Caustics Distorted Triplanar

  • Use the distorted World Position of the vertices as input to sample the Triplanar Projection.

Picture

Caustics End Result

  • The end result simulates the caustics of the water surface, but a much lower computational cost.

Picture

1.mp4

Additive Caustics

  • Add the caustics color to a primary Texture, to overlay on top of whatever color the object has.

Picture

6.mp4

Oclussion

  • Use a Custom Function Node to execute the MainLight HLSL function we wrote earlier.
  • Get the Direction, Color, Distance Attenuation and Shadow Attenuation for the main light.
  • Calculate a simple Lambert shading using the dot product of the light direction and the Normal Vector.
  • Multiply the Distance and Shadow Attenuations by the Color of the light.
  • Multiply everything together to calculate a colorized oclussion mask for the caustics.

Picture

Oclussion Screenshots

  • Using different angles of light.
8.mp4

Picture Picture Picture

Global Illumination

  • Use the Baked GI Node to access the Ambient/Global Illumination from the Scene.
  • Multiply it by the Albedo color coming from the Main Texture.

Picture

10.mp4

Complete Graph

  • Multiply the Oclussion by the Caustics to mask it out.
  • Multiply the Ocluded Caustics by the Albedo + Global Illumination.

Picture

About

Water caustics shader with triplanar projection, made in Unity for URP


Languages

Language:HLSL 100.0%