Initial commit

This commit is contained in:
2025-12-17 16:47:48 +00:00
commit 13813f3363
4964 changed files with 1079753 additions and 0 deletions

View File

@@ -0,0 +1,310 @@
#include "common.h"
// Tunables
#define CFG_TEXTURE_TILING 0.2
#define CFG_TEXTURE_DETILING 0.1
#define CFG_SPECULAR 2
#define CFG_GLOSS 900
#define CFG_NORMAL_STRENGTH 0.25
#define CFG_REFRACTION_STRENGTH 0.05
#define CFG_FRESNEL_OFFSET 0.3
#define CFG_SSR_STEPS 8
#define CFG_SSR_START_DISTANCE 1
#define CFG_SSR_STEP_CLAMP 0.2
#define CFG_SSR_DEPTH_CUTOFF 10
// Shader code
struct Appdata
{
ATTR_INT4 Position : POSITION;
ATTR_INT4 Normal : NORMAL;
ATTR_INT4 Material0 : TEXCOORD0;
ATTR_INT4 Material1 : TEXCOORD1;
};
struct VertexOutput
{
float4 HPosition : POSITION;
float4 Weights_Wave: COLOR0;
float3 Tangents: COLOR1;
float2 Uv0: TEXCOORD0;
float2 Uv1: TEXCOORD1;
float2 Uv2: TEXCOORD2;
float4 LightPosition_Fog : TEXCOORD3;
float3 Normal: TEXCOORD4;
float4 View_Depth: TEXCOORD5;
#ifdef PIN_HQ
float4 PositionScreen: TEXCOORD6;
#endif
};
WORLD_MATRIX_ARRAY(WorldMatrixArray, 72);
uniform float4 WaveParams; // .x = frequency .y = phase .z = height .w = lerp
uniform float4 WaterColor; // deep water color
uniform float4 WaterParams; // .x = refraction depth scale, .y = refraction depth offset
float3 displacePosition(float3 position, float waveFactor)
{
float x = sin((position.z - position.x) * WaveParams.x - WaveParams.y);
float z = sin((position.z + position.x) * WaveParams.x + WaveParams.y);
float p = (x + z) * WaveParams.z;
float3 result = position;
result.y += p * waveFactor;
return result;
}
float4 clipToScreen(float4 pos)
{
#ifdef GLSL
pos.xy = pos.xy * 0.5 + 0.5 * pos.w;
#else
pos.xy = pos.xy * float2(0.5, -0.5) + 0.5 * pos.w;
#endif
return pos;
}
float2 getUV(float3 position, ATTR_INT projection, float seed)
{
float3 u = WorldMatrixArray[1 + int(projection)].xyz;
float3 v = WorldMatrixArray[19 + int(projection)].xyz;
float2 uv = float2(dot(position, u), dot(position, v)) * (0.25 * CFG_TEXTURE_TILING) + CFG_TEXTURE_DETILING * float2(seed, floor(seed * 2.6651441f));
return uv;
}
VertexOutput WaterVS(Appdata IN)
{
VertexOutput OUT = (VertexOutput)0;
float3 posWorld = IN.Position.xyz * WorldMatrixArray[0].w + WorldMatrixArray[0].xyz;
float3 normalWorld = IN.Normal.xyz * (1.0 / 127.0) - 1.0;
#if defined(GLSLES) && !defined(GL3) // iPad2 workaround
float3 weights = abs(IN.Position.www - float3(0, 1, 2)) < 0.1;
#else
float3 weights = IN.Position.www == float3(0, 1, 2);
#endif
float waveFactor = dot(weights, IN.Material0.xyz) * (1.0 / 255.0);
#ifdef PIN_HQ
float fade = saturate0(1 - dot(posWorld - G(CameraPosition), -G(ViewDir).xyz) * G(FadeDistance_GlowFactor).y);
posWorld = displacePosition(posWorld, waveFactor * fade);
#endif
OUT.HPosition = mul(G(ViewProjection), float4(posWorld, 1));
OUT.LightPosition_Fog = float4(lgridPrepareSample(lgridOffset(posWorld, normalWorld)), (G(FogParams).z - OUT.HPosition.w) * G(FogParams).w);
OUT.Uv0 = getUV(posWorld, IN.Material1.x, IN.Normal.w);
OUT.Uv1 = getUV(posWorld, IN.Material1.y, IN.Material0.w);
OUT.Uv2 = getUV(posWorld, IN.Material1.z, IN.Material1.w);
OUT.Weights_Wave.xyz = weights;
OUT.Weights_Wave.w = waveFactor;
OUT.Normal = normalWorld;
OUT.View_Depth = float4(G(CameraPosition) - posWorld, OUT.HPosition.w);
OUT.Tangents = float3(IN.Material1.xyz) > 7.5; // side vs top
#ifdef PIN_HQ
OUT.PositionScreen = clipToScreen(OUT.HPosition);
#endif
return OUT;
}
TEX_DECLARE2D(NormalMap1, 0);
TEX_DECLARE2D(NormalMap2, 1);
TEX_DECLARECUBE(EnvMap, 2);
LGRID_SAMPLER(LightMap, 3);
TEX_DECLARE2D(LightMapLookup, 4);
TEX_DECLARE2D(GBufferColor, 5);
TEX_DECLARE2D(GBufferDepth, 6);
float fresnel(float ndotv)
{
return saturate(0.78 - 2.5 * abs(ndotv)) + CFG_FRESNEL_OFFSET;
}
float4 sampleMix(float2 uv)
{
#ifdef PIN_HQ
return lerp(tex2D(NormalMap1, uv), tex2D(NormalMap2, uv), WaveParams.w);
#else
return tex2D(NormalMap1, uv);
#endif
}
float3 sampleNormal(float2 uv0, float2 uv1, float2 uv2, float3 w, float3 normal, float3 tsel)
{
return terrainNormal(sampleMix(uv0), sampleMix(uv1), sampleMix(uv2), w, normal, tsel);
}
float3 sampleNormalSimple(float2 uv0, float2 uv1, float2 uv2, float3 w)
{
float4 data = sampleMix(uv0) * w.x + sampleMix(uv1) * w.y + sampleMix(uv2) * w.z;
return nmapUnpack(data).xzy;
}
float unpackDepth(float2 uv)
{
float4 geomTex = tex2D(GBufferDepth, uv);
float d = geomTex.z * (1.0f/256.0f) + geomTex.w;
return d * GBUFFER_MAX_DEPTH;
}
float3 getRefractedColor(float4 cpos, float3 N, float3 waterColor)
{
float2 refruv0 = cpos.xy / cpos.w;
float2 refruv1 = refruv0 + N.xz * CFG_REFRACTION_STRENGTH;
float4 refr0 = tex2D(GBufferColor, refruv0);
refr0.w = unpackDepth(refruv0);
float4 refr1 = tex2D(GBufferColor, refruv1);
refr1.w = unpackDepth(refruv1);
float4 result = lerp(refr0, refr1, saturate(refr1.w - cpos.w));
// Estimate water absorption by a scaled depth difference
float depthfade = saturate((result.w - cpos.w) * WaterParams.x + WaterParams.y);
// Since GBuffer depth is clamped we tone the refraction down after half of the range for a smooth fadeout
float gbuffade = saturate(cpos.w * (2.f / GBUFFER_MAX_DEPTH) - 1);
float fade = saturate(depthfade + gbuffade);
return lerp(result.rgb, waterColor, fade);
}
float3 getReflectedColor(float4 cpos, float3 wpos, float3 R)
{
float3 result = 0;
float inside = 0;
float distance = CFG_SSR_START_DISTANCE;
float diff = 0;
float diffclamp = cpos.w * CFG_SSR_STEP_CLAMP;
float4 Pproj = cpos;
float4 Rproj = clipToScreen(mul(G(ViewProjection), float4(R, 0)));
#ifndef GLSL
[unroll]
#endif
for (int i = 0; i < CFG_SSR_STEPS; ++i)
{
distance += clamp(diff, -diffclamp, diffclamp);
float4 cposi = Pproj + Rproj * distance;
float2 uv = cposi.xy / cposi.w;
float depth = unpackDepth(uv);
diff = depth - cposi.w;
}
float4 cposi = Pproj + Rproj * distance;
float2 uv = cposi.xy / cposi.w;
// Ray hit has to be inside the screen bounds
float ufade = abs(uv.x - 0.5) < 0.5;
float vfade = abs(uv.y - 0.5) < 0.5;
// Fade reflections out with distance; use max(ray hit, original depth) to discard hits that are too far
// 4 - depth * 4 would give us fade out from 0.75 to 1; 3.9 makes sure reflections go to 0 slightly before GBUFFER_MAX_DEPTH
float wfade = saturate((4 - 0.1) - max(cpos.w, cposi.w) * (4.f / GBUFFER_MAX_DEPTH));
// Ray hit has to be reasonably close to where we started
float dfade = abs(diff) < CFG_SSR_DEPTH_CUTOFF;
// Avoid back-projection
float Vfade = Rproj.w > 0;
float fade = ufade * vfade * wfade * dfade * Vfade;
return lerp(texCUBE(EnvMap, R).rgb, tex2D(GBufferColor, uv).rgb, fade);
}
float4 WaterPS(VertexOutput IN): COLOR0
{
float4 light = lgridSample(TEXTURE(LightMap), TEXTURE(LightMapLookup), IN.LightPosition_Fog.xyz);
float shadow = light.a;
float3 w = IN.Weights_Wave.xyz;
// Use simplified normal reconstruction for LQ mobile (assumes flat water surface)
#if defined(GLSLES) && !defined(PIN_HQ)
float3 normal = sampleNormalSimple(IN.Uv0, IN.Uv1, IN.Uv2, w);
#else
float3 normal = sampleNormal(IN.Uv0, IN.Uv1, IN.Uv2, w, IN.Normal, IN.Tangents);
#endif
// Flatten the normal for Fresnel and for reflections to make them less chaotic
float3 flatNormal = lerp(IN.Normal, normal, CFG_NORMAL_STRENGTH);
float3 waterColor = WaterColor.rgb;
#ifdef PIN_HQ
float fade = saturate0(1 - IN.View_Depth.w * G(FadeDistance_GlowFactor).y);
float3 view = normalize(IN.View_Depth.xyz);
float fre = fresnel(dot(flatNormal, view)) * IN.Weights_Wave.w;
float3 position = G(CameraPosition) - IN.View_Depth.xyz;
#ifdef PIN_GBUFFER
float3 refr = getRefractedColor(IN.PositionScreen, normal, waterColor);
float3 refl = getReflectedColor(IN.PositionScreen, position, reflect(-view, flatNormal));
#else
float3 refr = waterColor;
float3 refl = texCUBE(EnvMap, reflect(-view, flatNormal)).rgb;
#endif
float specularIntensity = CFG_SPECULAR * fade;
float specularPower = CFG_GLOSS;
float3 specular = G(Lamp0Color) * (specularIntensity * shadow * (float)(half)pow(saturate(dot(normal, normalize(-G(Lamp0Dir) + view))), specularPower));
#else
float3 view = normalize(IN.View_Depth.xyz);
float fre = fresnel(dot(flatNormal, view));
float3 refr = waterColor;
float3 refl = texCUBE(EnvMap, reflect(-IN.View_Depth.xyz, flatNormal)).rgb;
float3 specular = 0;
#endif
// Combine
float4 result;
result.rgb = lerp(refr, refl, fre) * (G(AmbientColor).rgb + G(Lamp0Color).rgb * shadow + light.rgb) + specular;
result.a = 1;
float fogAlpha = saturate(IN.LightPosition_Fog.w);
result.rgb = lerp(G(FogColor), result.rgb, fogAlpha);
return result;
}