Skip to content

Instantly share code, notes, and snippets.

@unvestigate
Created September 16, 2025 19:02
Show Gist options
  • Select an option

  • Save unvestigate/0d82591956c2573f1a42f4a6ad25fc8e to your computer and use it in GitHub Desktop.

Select an option

Save unvestigate/0d82591956c2573f1a42f4a6ad25fc8e to your computer and use it in GitHub Desktop.
Water surface shader
#include "hlsl_include/basis_shader.h"
#include "hlsl_include/lighting_scene.h"
#include "hlsl_include/pixel_data.h"
#include "hlsl_include/water.h"
#autobind Texture2D gWaterDUDVMap;
//#autobind Texture2D gWaterNormalMap;
struct VertexIn
{
float3 position : POSITION;
float3 normal : NORMAL;
float2 texCoord : TEXCOORD0;
};
struct VertexOut
{
float3 positionWS : POSITION; // World space position.
float2 texCoord : TEXCOORD0; // Texture coordinate.
float4 position : SV_POSITION; // Clip space position.
float4 positionClip : POSITION2;
/*#ifdef BASIS_ENABLE_CUSTOM_CLIPPING_PLANES
float customClipDistance : SV_ClipDistance0;
#endif*/
};
VertexOut VS(VertexIn vin)
{
VertexOut vout;
/*
#if BASIS_SPIRV == 1
vin.world = transpose(vin.world);
#endif
*/
float4x4 world = gWaterWorld;
float4x4 worldview = mul(gCameraView, world);
float4x4 wvp = mul(gCameraProjection, worldview);
vout.position = mul(wvp, float4(vin.position, 1.0f));
vout.positionWS = mul(world, float4(vin.position, 1.0f)).xyz;
vout.positionClip = vout.position;
vout.texCoord = vin.texCoord;
/*#ifdef BASIS_ENABLE_CUSTOM_CLIPPING_PLANES
vout.customClipDistance = dot(float4(vout.positionWS, 1.0f), gCustomClippingPlane);
#endif*/
return vout;
}
PixelData PS(VertexOut pin)
{
float3 pixelToCamera = gCameraPosition - pin.positionWS;
float2 texCoords = pin.texCoord * float2(gWaterTexScaleX, gWaterTexScaleY);
// Calculate the projected texture coordinates, for sampling from the relf/refr textures.
float2 ndc = ((pin.positionClip.xy / pin.positionClip.w) * 0.5f) + 0.5f;
float2 reflectionCoords = float2(ndc.x, ndc.y);
float2 refractionCoords = float2(ndc.x, -ndc.y);
// Sample the refraction pass depth buffer and compare the value to the value of the current pixel
// to get the water depth along the view vector, for this surface pixel.
float rawDepth = __BasisWaterRefrDepthMap.Sample(__BasisSampler_PointWrap, refractionCoords).x;
float bottomToCameraDistance = getLinearDepthBufferValue_BrokenFixMe(rawDepth);
float surfaceToCameraDistance = getLinearDepthBufferValue_BrokenFixMe(pin.position.z);
float waterDepth = bottomToCameraDistance - surfaceToCameraDistance;
// Calculate the texture coordinates for distortion on the water surface.
float distortionTime = gWorldTime * gWaterWaveAnimationSpeed;
float2 distortedTexCoords = gWaterDUDVMap.Sample(__BasisSampler_LinearWrap, float2(texCoords.x + distortionTime, texCoords.y)).xy * gWaterWaveScale;
distortedTexCoords = texCoords + float2(distortedTexCoords.x, distortedTexCoords.y + distortionTime);
// The total distortion is multiplied by the wave strength as well as the water depth.
// This makes the distrortion smaller when the water is shallower, helping eliminate
// artifacts caused by the distortion.
float2 totalDistortion = (gWaterDUDVMap.Sample(__BasisSampler_LinearWrap, distortedTexCoords).xy * 2.0f - 1.0f) * gWaterWaveStrength * saturate(waterDepth);
// Apply the distortion to the texture coords for the relf/refr textures.
reflectionCoords += totalDistortion;
reflectionCoords.x = clamp(reflectionCoords.x, 0.001, 0.999);
refractionCoords += totalDistortion;
refractionCoords.x = clamp(refractionCoords.x, 0.001, 0.999);
refractionCoords.y = clamp(refractionCoords.y, -0.999, -0.001);
float4 reflColor = __BasisWaterReflMap.Sample(__BasisSampler_LinearWrap, reflectionCoords);
float4 refrColor = __BasisWaterRefrMap.Sample(__BasisSampler_LinearWrap, refractionCoords);
// Apply the "deep" water color to the refractionColor.
float depthColorFactor = remapFloat(waterDepth, gDeepWaterFadeStart, gDeepWaterFadeEnd, 0.0f, 1.0f);
refrColor = lerp(refrColor, gDeepWaterColor, depthColorFactor);
float3 normal = float3(0.0f, 1.0f, 0.0f);
// Calculate the fresnel value, to determine how much reflection vs refraction.
float3 viewVector = normalize(pixelToCamera);
float reflectionFactor = saturate(dot(viewVector, normal));
reflectionFactor = pow(reflectionFactor, gWaterReflectionStrength);
// Apply the fresnel.
float4 litColor = lerp(reflColor, refrColor, saturate(reflectionFactor));
// Apply color tinting.
litColor = lerp(litColor, gWaterColorTint, gWaterColorTintAmount);
// Apply fog.
litColor = applyFogToPixel(pin.positionWS, gCameraPosition.xyz, litColor);
// Fade out the water in shallow areas.
litColor.a = saturate(waterDepth / gWaterFullOpacityDepth);
return getPixelData(litColor, float4(0.0f, 0.0f, 0.0f, 0.0f));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment