Water Shader Update
This commit is contained in:
@ -96,41 +96,81 @@ vec3 sampleWaterNormals(vec2 hitXY, float speed, out vec2 tcnor0, out vec2 tcnor
|
|||||||
return normalize(n0 + n1 - 1.0);
|
return normalize(n0 + n1 - 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float waveHeight(vec2 xy, float speed) {
|
||||||
|
return (sin(xy.x * 10.0 / waterDisplace + speed) * cos(xy.y * 10.0 / waterDisplace + speed) + sin(xy.x * 20.0 / waterDisplace + speed * 1.3) * cos(xy.y * 20.0 / waterDisplace + speed * 1.3) * 0.5) / 50.0 * waterDisplace;
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
float gdepth = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0;
|
float gdepth = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0;
|
||||||
vec3 vray = normalize(viewRay);
|
vec3 vray = normalize(viewRay);
|
||||||
float speed = time * 2.0 * waterSpeed;
|
float speed = time * 2.0 * waterSpeed;
|
||||||
bool isSky = (gdepth == 1.0);
|
float farPlane = cameraProj.y / (1.0 - cameraProj.x);
|
||||||
|
float cosAng = dot(eyeLook, vray);
|
||||||
|
float domeR = farPlane * 0.94;
|
||||||
|
float tDome = -3.5 * vray.z + sqrt(max(domeR * domeR - 12.25 * (1.0 - vray.z * vray.z), 0.0));
|
||||||
|
float domeDepth01 = cameraProj.x + cameraProj.y / (tDome * cosAng);
|
||||||
|
bool isSky = (gdepth * 0.5 + 0.5) >= domeDepth01;
|
||||||
|
|
||||||
// Ray-plane intersection with water surface (z = waterLevel)
|
// Ray-plane intersection with water surface (z = waterLevel)
|
||||||
float denom = dot(vray, vec3(0.0, 0.0, 1.0));
|
float denom = vray.z;
|
||||||
float tWater = (waterLevel - eye.z) / denom;
|
float tWater = (waterLevel - eye.z) / denom;
|
||||||
bool hasWaterHit = (abs(denom) > 0.0001) && (tWater > 0.0);
|
bool hasWaterHit = (abs(denom) > 0.0001) && (tWater > 0.0);
|
||||||
|
|
||||||
if (eye.z < waterLevel) {
|
if (eye.z < waterLevel) {
|
||||||
vec2 tc = texCoord;
|
vec2 tc = texCoord;
|
||||||
float fogFactor;
|
float depthThick = 1.0 + (waterLevel - eye.z);
|
||||||
|
vec3 p = vec3(0.0);
|
||||||
|
float geomDist = farPlane;
|
||||||
|
if (!isSky) {
|
||||||
|
p = getPos(eye, eyeLook, vray, gdepth, cameraProj);
|
||||||
|
geomDist = length(p - eye);
|
||||||
|
}
|
||||||
|
bool upRay = hasWaterHit && denom > 0.0;
|
||||||
|
float waterDist = upRay ? min(tWater, geomDist) : geomDist;
|
||||||
|
|
||||||
if (hasWaterHit && denom > 0.0) {
|
vec2 wxy;
|
||||||
// Looking up at water surface - apply normal distortion
|
if (upRay && tWater < geomDist) {
|
||||||
vec3 hit = eye + tWater * vray;
|
wxy = (eye + tWater * vray).xy;
|
||||||
vec2 tc0, tc1;
|
} else if (!isSky) {
|
||||||
vec3 n2 = sampleWaterNormals(hit.xy * waterFreq, speed, tc0, tc1);
|
wxy = p.xy;
|
||||||
tc = texCoord + (n2.xy * n2.z) / 30.0 * waterRefract;
|
|
||||||
fogFactor = clamp(tWater * waterDensity, 0.0, 0.95);
|
|
||||||
} else {
|
} else {
|
||||||
// Looking forward/down - distort via water surface above fragment
|
wxy = eye.xy + vray.xy * 50.0;
|
||||||
vec3 p = getPos(eye, eyeLook, vray, gdepth, cameraProj);
|
}
|
||||||
vec2 wxy = isSky ? (eye.xy + vray.xy * 50.0) : p.xy;
|
|
||||||
vec2 tc0, tc1;
|
vec2 tc0, tc1;
|
||||||
vec3 n2 = sampleWaterNormals(wxy * waterFreq, speed, tc0, tc1);
|
vec3 n2 = sampleWaterNormals(wxy * waterFreq, speed, tc0, tc1);
|
||||||
tc = texCoord + (n2.xy * n2.z) / 30.0 * waterRefract;
|
tc = texCoord + (n2.xy * n2.z) / 30.0 * waterRefract;
|
||||||
float waterDist = isSky ? 50.0 : length(p - eye);
|
float ddepth2 = textureLod(gbufferD, tc, 0.0).r * 2.0 - 1.0;
|
||||||
fogFactor = clamp(waterDist * waterDensity, 0.0, 0.95);
|
float dist2 = cameraProj.y / ((ddepth2 * 0.5 + 0.5) - cameraProj.x) / cosAng;
|
||||||
}
|
if (dist2 > geomDist + 1.0) tc = texCoord;
|
||||||
|
|
||||||
vec3 refracted = textureLod(tex, tc, 0.0).rgb;
|
vec3 refracted = textureLod(tex, tc, 0.0).rgb;
|
||||||
fragColor.rgb = mix(refracted, waterColor, fogFactor);
|
vec3 outCol = refracted;
|
||||||
|
|
||||||
|
if (upRay && tWater < geomDist) {
|
||||||
|
outCol *= max(1.0 - length(n2.xy) * 1.5, 0.0);
|
||||||
|
vec3 h2 = normalize(-vray + ld);
|
||||||
|
outCol += pow(max(dot(n2, h2), 0.0), 80.0) * 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isSky) {
|
||||||
|
float fdw = abs(p.z - (waterLevel + waveHeight(p.xy, speed)));
|
||||||
|
float bandw = 0.1 + waterDist * 0.003;
|
||||||
|
if (fdw < bandw) {
|
||||||
|
float foamLod = clamp(waterDist / 15.0, 0.0, 5.0);
|
||||||
|
vec3 fm0 = textureLod(sfoam, tc0 * 3.0 + vec2(speed / 30.0, speed / 50.0), foamLod).rgb;
|
||||||
|
vec3 fm1 = textureLod(sfoam, tc1 * 4.0 + vec2(-speed / 35.0, speed / 45.0), foamLod).rgb;
|
||||||
|
float fstr = clamp(1.0 - fm0.r * 0.5 - fm1.b * 0.5, 0.0, 1.0);
|
||||||
|
float ffac = clamp(1.0 - fdw / bandw, 0.0, 1.0);
|
||||||
|
float foamFade = clamp(1.0 - (waterLevel - eye.z) * 0.4, 0.0, 1.0);
|
||||||
|
vec3 foamCol = mix(waterColor, vec3(1.0), 0.6);
|
||||||
|
outCol = mix(outCol, foamCol, ffac * fstr * 0.5 * foamFade);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float fogFactor = 1.0 - exp(-waterDist * waterDensity * 0.006 * depthThick);
|
||||||
|
outCol = mix(outCol, waterColor, clamp(fogFactor, 0.0, 1.0));
|
||||||
|
|
||||||
|
fragColor.rgb = outCol;
|
||||||
fragColor.a = 1.0;
|
fragColor.a = 1.0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -144,21 +184,19 @@ void main() {
|
|||||||
if (isSky) tWater = min(tWater, 100.0); // Clamp to prevent aliasing at horizon
|
if (isSky) tWater = min(tWater, 100.0); // Clamp to prevent aliasing at horizon
|
||||||
vec3 p = isSky ? (eye + tWater * vray) : getPos(eye, eyeLook, vray, gdepth, cameraProj);
|
vec3 p = isSky ? (eye + tWater * vray) : getPos(eye, eyeLook, vray, gdepth, cameraProj);
|
||||||
|
|
||||||
float horizonFactor = clamp(1.0 - tWater / 60.0, 0.0, 1.0);
|
if (!isSky && p.z > waterLevel - 0.01) {
|
||||||
if (!isSky && p.z > waterLevel) {
|
fragColor = textureLod(tex, texCoord, 0.0);
|
||||||
fragColor = vec4(0.0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
float horizonFactor = clamp(1.0 - tWater / 60.0, 0.0, 1.0);
|
||||||
|
|
||||||
// Displace surface
|
// Displace surface
|
||||||
float geomZ = p.z; // undisplaced depth for foam/fog tests
|
float geomZ = p.z; // undisplaced depth for foam/fog tests
|
||||||
p.z += (sin(p.x * 10.0 / waterDisplace + speed) * cos(p.y * 10.0 / waterDisplace + speed)
|
p.z += waveHeight(p.xy, speed);
|
||||||
+ sin(p.x * 20.0 / waterDisplace + speed * 1.3) * cos(p.y * 20.0 / waterDisplace + speed * 1.3) * 0.5)
|
|
||||||
/ 50.0 * waterDisplace;
|
|
||||||
|
|
||||||
// Hit plane to determine uvs
|
// Hit plane to determine uvs
|
||||||
vec3 v = normalize(eye - p);
|
vec3 v = normalize(eye - p);
|
||||||
float t = (waterLevel - eye.z) / dot(v, vec3(0.0, 0.0, 1.0));
|
float t = (waterLevel - eye.z) / v.z;
|
||||||
vec3 hit = eye + t * v;
|
vec3 hit = eye + t * v;
|
||||||
|
|
||||||
// Sample normal maps
|
// Sample normal maps
|
||||||
@ -171,8 +209,8 @@ void main() {
|
|||||||
tc = texCoord + (n2.xy * n2.z) / 30.0 * waterRefract;
|
tc = texCoord + (n2.xy * n2.z) / 30.0 * waterRefract;
|
||||||
} else {
|
} else {
|
||||||
float ddepth = textureLod(gbufferD, texCoord + (n2.xy * n2.z) / 40.0, 0.0).r * 2.0 - 1.0;
|
float ddepth = textureLod(gbufferD, texCoord + (n2.xy * n2.z) / 40.0, 0.0).r * 2.0 - 1.0;
|
||||||
vec3 p2 = getPos(eye, eyeLook, vray, ddepth, cameraProj);
|
float dist2 = cameraProj.y / ((ddepth * 0.5 + 0.5) - cameraProj.x) / cosAng;
|
||||||
tc = p2.z > waterLevel ? texCoord : texCoord + (n2.xy * n2.z) / 30.0 * waterRefract;
|
tc = (eye.z + vray.z * dist2) > waterLevel ? texCoord : texCoord + (n2.xy * n2.z) / 30.0 * waterRefract;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Light
|
// Light
|
||||||
@ -222,26 +260,25 @@ void main() {
|
|||||||
#else
|
#else
|
||||||
fragColor.rgb = mix(refracted, reflectedEnv, waterReflect * fresnel);
|
fragColor.rgb = mix(refracted, reflectedEnv, waterReflect * fresnel);
|
||||||
#endif
|
#endif
|
||||||
// Water color tint - blend rather than multiply to preserve brightness
|
|
||||||
float colorMix = isSky ? 0.7 : 0.5;
|
float colorMix = isSky ? 0.7 : 0.5;
|
||||||
fragColor.rgb = mix(fragColor.rgb, fragColor.rgb * waterColor, colorMix * horizonFactor);
|
fragColor.rgb = mix(fragColor.rgb, waterColor, colorMix);
|
||||||
// Blinn-Phong specular using half-vector, faded at horizon
|
// Blinn-Phong specular using half-vector, faded at horizon
|
||||||
vec3 h = normalize(v + ld);
|
vec3 h = normalize(v + ld);
|
||||||
float specAmount = pow(max(dot(n2, h), 0.0), 200.0) * (200.0 + 8.0) / (PI * 8.0);
|
float specAmount = pow(max(dot(n2, h), 0.0), 200.0) * (200.0 + 8.0) / (PI * 8.0);
|
||||||
fragColor.rgb += specAmount * (isSky ? 0.3 : 1.0);
|
fragColor.rgb += specAmount * (isSky ? 0.3 : 1.0) * horizonFactor;
|
||||||
// Depth fog - blend toward waterColor with depth, faded at horizon
|
float waterColumn = isSky ? 1.0 : max(length(p - eye) - tWater, 0.0);
|
||||||
float depthFog = clamp(-(geomZ - waterLevel) * waterDensity, 0.0, 0.9);
|
float depthFog = 1.0 - exp(-waterColumn * waterDensity * 0.006);
|
||||||
fragColor.rgb = mix(fragColor.rgb, waterColor, depthFog * horizonFactor);
|
vec3 surfaceCol = mix(waterColor, reflectedEnv, waterReflect * fresnel);
|
||||||
|
fragColor.rgb = mix(fragColor.rgb, surfaceCol, min(depthFog, 0.9));
|
||||||
// Alpha fades smoothly at horizon instead of hard cut
|
// Alpha fades smoothly at horizon instead of hard cut
|
||||||
// Skydome sits 3.5 below the camera (_skydomeMatrix), so its horizon
|
// Skydome sits 3.5 below the camera (_skydomeMatrix), so its horizon
|
||||||
// appears where the ray z reaches -3.5 / domeRadius
|
// appears where the ray z reaches -3.5 / domeRadius
|
||||||
float farPlane = cameraProj.y / (1.0 - cameraProj.x);
|
|
||||||
float horizonFade = clamp((-3.5 / (farPlane * 0.95) - vray.z) * 20.0, 0.0, 1.0);
|
float horizonFade = clamp((-3.5 / (farPlane * 0.95) - vray.z) * 20.0, 0.0, 1.0);
|
||||||
fragColor.a = isSky ? horizonFade : clamp(abs(geomZ - waterLevel) * 5.0, 0.0, 1.0);
|
fragColor.a = isSky ? horizonFade : clamp(abs(geomZ - waterLevel) * 5.0 + 0.1, 0.0, 1.0);
|
||||||
|
float waveH = p.z - geomZ; // displacement already applied above
|
||||||
// Foam - based on actual geometry depth below water surface
|
float band = 0.1 + tWater * 0.003;
|
||||||
float fd = isSky ? 1.0 : abs(geomZ - waterLevel);
|
float fd = isSky ? 1.0 : abs(geomZ - (waterLevel + waveH));
|
||||||
if (fd < 0.1) {
|
if (fd < band) {
|
||||||
// Based on foam by Owen Deery
|
// Based on foam by Owen Deery
|
||||||
// http://fire-face.com/personal/water
|
// http://fire-face.com/personal/water
|
||||||
// Distance-based LOD blurs foam at range to reduce noise
|
// Distance-based LOD blurs foam at range to reduce noise
|
||||||
@ -250,8 +287,10 @@ void main() {
|
|||||||
vec2 foamUV1 = tcnor1 * 4.0 + vec2(-speed / 35.0, speed / 45.0);
|
vec2 foamUV1 = tcnor1 * 4.0 + vec2(-speed / 35.0, speed / 45.0);
|
||||||
vec3 foamMask0 = textureLod(sfoam, foamUV0, foamLod).rgb;
|
vec3 foamMask0 = textureLod(sfoam, foamUV0, foamLod).rgb;
|
||||||
vec3 foamMask1 = textureLod(sfoam, foamUV1, foamLod).rgb;
|
vec3 foamMask1 = textureLod(sfoam, foamUV1, foamLod).rgb;
|
||||||
float foamStrength = clamp(1.0 - foamMask0.r * 0.5 - foamMask1.b * 0.5, 0.0, 1.0);
|
float foamMask = clamp(1.0 - foamMask0.r * 0.5 - foamMask1.b * 0.5, 0.0, 1.0);
|
||||||
float fac = (1.0 - (fd * (1.0 / 0.1))) * horizonFactor;
|
float fac = clamp(1.0 - fd / band, 0.0, 1.0);
|
||||||
fragColor.rgb = mix(fragColor.rgb, mix(fragColor.rgb, waterColor + 0.2, foamStrength), clamp(fac, 0.0, 1.0) * 0.5);
|
vec3 foamCol = mix(waterColor, vec3(1.0), 0.6);
|
||||||
|
fragColor.rgb = mix(fragColor.rgb, foamCol, fac * foamMask * 0.8);
|
||||||
|
fragColor.a = max(fragColor.a, fac * foamMask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user