Files
aya/client/common/shaders/bgfx_source/water.sc
2025-12-17 16:47:48 +00:00

182 lines
4.8 KiB
Scala

$input a_position, a_normal
$output v_pos, v_texcoord0, v_worldPos, v_normal, v_lightpos_fog, v_color0
#include "common.sh"
#include "globals.sh"
//
// Water shader.
// Big, fat and ugly.
//
// All (most) things considered, I have converged to this particular way of rendering water:
//
// Vertex waves
// No transparency. Solid color for deep water.
// Fresnel law, reflects environment.
// Phong speculars.
// Ripples via animated normal map. Adjustable intensity, speed and scale. Affect reflection and speculars.
uniform vec4 u_nmAnimLerp; // ratio between normal map frames
uniform vec4 u_waveParams; // .x = frequency .y = phase .z = height
uniform vec4 u_waterColor; // deep water color
SAMPLER2D(s_normalMap1, 0);
SAMPLER2D(s_normalMap2, 1);
SAMPLERCUBE(s_envMap, 2);
SAMPLER2D(s_lightMap, 3);
SAMPLER2D(s_lightMapLookup, 4);
#ifdef PIN_HQ
# define WATER_LOD 1
#else
# define WATER_LOD 2
#endif
#define LODBIAS (-1.0)
float fadeFactor(vec3 wspos)
{
return saturate0(-0.4 + 1.4 * length(u_cameraPosition - wspos.xyz) * u_fadeDistance_GlowFactor.y);
}
float wave(vec4 wspos)
{
float x = sin((wspos.z - wspos.x - u_waveParams.y) * u_waveParams.x);
float z = sin((wspos.z + wspos.x + u_waveParams.y) * u_waveParams.x);
float p = (x + z) * u_waveParams.z;
return p - p * fadeFactor(wspos.xyz);
}
// perturbs the water mesh and vertex normals
void makeWaves(inout vec4 wspos, inout vec3 wsnrm)
{
#if WATER_LOD == 0
float gridSize = 4.0;
vec4 wspos1 = wspos;
vec4 wspos2 = wspos;
wspos1.x += gridSize;
wspos2.z += gridSize;
wspos.y += wave(wspos);
wspos1.y += wave(wspos1);
wspos2.y += wave(wspos2);
wsnrm = normalize(cross(wspos2.xyz - wspos.xyz, wspos1.xyz - wspos.xyz));
#elif WATER_LOD == 1
wspos.y += wave(wspos);
#else /* do nothing */
#endif
}
void water_vs()
{
// Decode vertex data
vec3 normal = (a_normal.xyz - 127.0) / 127.0;
normal = normalize(normal);
vec4 wspos = mul(u_worldMatrix, vec4(a_position.xyz, 1.0));
vec3 wsnrm = normal;
wspos.y -= 2.0 * u_waveParams.z;
makeWaves(wspos, wsnrm);
v_worldPos = wspos;
v_normal = wsnrm;
if (normal.y < 0.01) v_normal = normal;
// box mapping
vec2 tcselect;
vec3 wspostc = vec3(wspos.x, -wspos.y, wspos.z);
tcselect.x = dot(abs(normal.yxz), wspostc.xzx);
tcselect.y = dot(abs(normal.yxz), wspostc.zyy);
v_pos = mul(u_viewProjection, wspos);
v_texcoord0.xy = tcselect * 0.05;
v_texcoord0.z = saturate0((u_fogParams.z - v_pos.w) * u_fogParams.w);
v_texcoord0.w = LODBIAS;
v_lightpos_fog.xyz = lgridPrepareSample(lgridOffset(wspos.xyz, wsnrm.xyz));
v_color0.x = fadeFactor(wspos.xyz);
v_color0.y = (1.0 - v_color0.x) * saturate0(dot(wsnrm, -u_lamp0Dir)) * 100.0;
v_color0.z = 1.0 - 0.9 * saturate1(exp(-0.005 * length(u_cameraPosition - wspos.xyz)));
gl_Position = v_pos;
}
vec3 pixelNormal(vec4 tc0)
{
vec4 nm1 = texture2DLod(s_normalMap1, tc0.xy, tc0.w);
#if WATER_LOD <= 1
vec4 nm2 = texture2DLod(s_normalMap2, tc0.xy, tc0.w);
vec4 nm3 = mix(nm1, nm2, u_nmAnimLerp.xxxx);
#else
vec4 nm3 = nm1;
#endif
return nmapUnpack(nm3);
}
// Fresnel approximation. N1 and N2 are refractive indices.
// for above water, use n1 = 1, n2 = 1.3, for underwater use n1 = 1.3, n2 = 1
float fresnel(vec3 N, vec3 V, float n1, float n2, float p, float fade)
{
#if WATER_LOD == 0
float r0 = (n1 - n2) / (n1 + n2);
r0 *= r0;
return r0 + (1.0 - r0) * pow(1.0 - abs(dot(normalize(N), V)), p);
#else
return 0.1 + saturate0(-1.9 * abs(dot(N, V)) + 0.8); // HAXX!
#endif
}
vec4 envColor(vec3 N, vec3 V, float fade)
{
vec3 dir = reflect(V, N);
return textureCube(s_envMap, dir) * 0.91;
}
vec4 deepWaterColor(vec4 light)
{
vec4 tint = 0.8 * vec4(118.0, 143.0, 153.0, 255.0) / 255.0;
return (light + textureCubeLod(s_envMap, vec3(0.0, 1.0, 0.0), 10.0)) * tint;
}
void water_ps()
{
vec3 N2 = v_normal;
vec3 N1 = pixelNormal(v_texcoord0).xzy;
vec3 N3 = 0.5 * (N2 + N1);
N3 = mix(N3, N2, v_color0.z);
vec3 L = -u_lamp0Dir.xyz;
vec3 E = normalize(u_cameraPosition - v_worldPos.xyz);
vec4 light = lgridSample(s_lightMap, s_lightMapLookup, v_lightpos_fog.xyz);
float fre = fresnel(N3, E, 1.0, 1.3, 5.0, v_color0.x);
vec3 diffuse = deepWaterColor(light).rgb;
vec3 env = envColor(N3, -E, v_color0.x).rgb;
vec3 R = reflect(-L, N1);
#if WATER_LOD <= 1
float specular = pow(saturate0(dot(R, E)), 1600.0) * L.y * 100.0; // baseline
# ifndef GLSLES
specular = 0.65 * saturate1(specular * saturate0(light.a - 0.4));
# endif
#else
float specular = 0.0;
#endif
vec3 result = mix(diffuse, env, fre) + vec3(specular, specular, specular);
result = mix(u_fogColor.rgb, result, v_texcoord0.z);
gl_FragColor = vec4(result, 1.0);
}