forked from aya/aya
182 lines
4.8 KiB
Scala
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);
|
|
}
|