forked from LeenkxTeam/LNXSDK
Update
This commit is contained in:
@ -1,11 +1,11 @@
|
||||
/* Various sky functions
|
||||
* =====================
|
||||
*
|
||||
* Nishita model is based on https://github.com/wwwtyro/glsl-atmosphere (Unlicense License)
|
||||
* Single scattering model is based on https://github.com/wwwtyro/glsl-atmosphere (Unlicense License)
|
||||
*
|
||||
* Changes to the original implementation:
|
||||
* - r and pSun parameters of nishita_atmosphere() are already normalized
|
||||
* - Some original parameters of nishita_atmosphere() are replaced with pre-defined values
|
||||
* - r and pSun parameters of single_scatter_atmosphere() are already normalized
|
||||
* - Some original parameters of single_scatter_atmosphere() are replaced with pre-defined values
|
||||
* - Implemented air, dust and ozone density node parameters (see Blender source)
|
||||
* - Replaced the inner integral calculation with a LUT lookup
|
||||
*
|
||||
@ -22,8 +22,8 @@
|
||||
|
||||
#include "std/math.glsl"
|
||||
|
||||
uniform sampler2D nishitaLUT;
|
||||
uniform vec2 nishitaDensity;
|
||||
uniform sampler2D singleScatterLUT;
|
||||
uniform vec2 skyDensity;
|
||||
|
||||
#ifndef PI
|
||||
#define PI 3.141592
|
||||
@ -32,33 +32,33 @@ uniform vec2 nishitaDensity;
|
||||
#define HALF_PI 1.570796
|
||||
#endif
|
||||
|
||||
#define nishita_iSteps 16
|
||||
#define single_scatter_iSteps 16
|
||||
|
||||
// These values are taken from Cycles code if they
|
||||
// exist there, otherwise they are taken from the example
|
||||
// in the glsl-atmosphere repo
|
||||
#define nishita_sun_intensity 22.0
|
||||
#define nishita_atmo_radius 6420e3
|
||||
#define nishita_rayleigh_scale 8e3
|
||||
#define nishita_rayleigh_coeff vec3(5.5e-6, 13.0e-6, 22.4e-6)
|
||||
#define nishita_mie_scale 1.2e3
|
||||
#define nishita_mie_coeff 2e-5
|
||||
#define nishita_mie_dir 0.76 // Aerosols anisotropy ("direction")
|
||||
#define nishita_mie_dir_sq 0.5776 // Squared aerosols anisotropy
|
||||
#define single_scatter_sun_intensity 22.0
|
||||
#define single_scatter_atmo_radius 6420e3
|
||||
#define single_scatter_rayleigh_scale 8e3
|
||||
#define single_scatter_rayleigh_coeff vec3(5.5e-6, 13.0e-6, 22.4e-6)
|
||||
#define single_scatter_mie_scale 1.2e3
|
||||
#define single_scatter_mie_coeff 2e-5
|
||||
#define single_scatter_mie_dir 0.76 // Aerosols anisotropy ("direction")
|
||||
#define single_scatter_mie_dir_sq 0.5776 // Squared aerosols anisotropy
|
||||
|
||||
// Values from [Hill: 60]
|
||||
#define sun_limb_darkening_col vec3(0.397, 0.503, 0.652)
|
||||
|
||||
vec3 nishita_lookupLUT(const float height, const float sunTheta) {
|
||||
vec3 single_scatter_lookupLUT(const float height, const float sunTheta) {
|
||||
vec2 coords = vec2(
|
||||
sqrt(height * (1 / nishita_atmo_radius)),
|
||||
sqrt(height * (1 / single_scatter_atmo_radius)),
|
||||
0.5 + 0.5 * sign(sunTheta - HALF_PI) * sqrt(abs(sunTheta * (1 / HALF_PI) - 1))
|
||||
);
|
||||
return textureLod(nishitaLUT, coords, 0.0).rgb;
|
||||
return textureLod(singleScatterLUT, coords, 0.0).rgb;
|
||||
}
|
||||
|
||||
/* See raySphereIntersection() in leenkx/Sources/renderpath/Nishita.hx */
|
||||
vec2 nishita_rsi(const vec3 r0, const vec3 rd, const float sr) {
|
||||
/* See raySphereIntersection() in leenkx/Sources/renderpath/Sky.hx */
|
||||
vec2 single_scatter_rsi(const vec3 r0, const vec3 rd, const float sr) {
|
||||
float a = dot(rd, rd);
|
||||
float b = 2.0 * dot(rd, r0);
|
||||
float c = dot(r0, r0) - (sr * sr);
|
||||
@ -74,12 +74,12 @@ vec2 nishita_rsi(const vec3 r0, const vec3 rd, const float sr) {
|
||||
* pSun: normalized sun direction
|
||||
* rPlanet: planet radius
|
||||
*/
|
||||
vec3 nishita_atmosphere(const vec3 r, const vec3 r0, const vec3 pSun, const float rPlanet) {
|
||||
vec3 single_scatter_atmosphere(const vec3 r, const vec3 r0, const vec3 pSun, const float rPlanet) {
|
||||
// Calculate the step size of the primary ray
|
||||
vec2 p = nishita_rsi(r0, r, nishita_atmo_radius);
|
||||
vec2 p = single_scatter_rsi(r0, r, single_scatter_atmo_radius);
|
||||
if (p.x > p.y) return vec3(0.0);
|
||||
p.y = min(p.y, nishita_rsi(r0, r, rPlanet).x);
|
||||
float iStepSize = (p.y - p.x) / float(nishita_iSteps);
|
||||
p.y = min(p.y, single_scatter_rsi(r0, r, rPlanet).x);
|
||||
float iStepSize = (p.y - p.x) / float(single_scatter_iSteps);
|
||||
|
||||
// Primary ray time
|
||||
float iTime = 0.0;
|
||||
@ -96,18 +96,18 @@ vec3 nishita_atmosphere(const vec3 r, const vec3 r0, const vec3 pSun, const floa
|
||||
float mu = dot(r, pSun);
|
||||
float mumu = mu * mu;
|
||||
float pRlh = 3.0 / (16.0 * PI) * (1.0 + mumu);
|
||||
float pMie = 3.0 / (8.0 * PI) * ((1.0 - nishita_mie_dir_sq) * (mumu + 1.0)) / (pow(1.0 + nishita_mie_dir_sq - 2.0 * mu * nishita_mie_dir, 1.5) * (2.0 + nishita_mie_dir_sq));
|
||||
float pMie = 3.0 / (8.0 * PI) * ((1.0 - single_scatter_mie_dir_sq) * (mumu + 1.0)) / (pow(1.0 + single_scatter_mie_dir_sq - 2.0 * mu * single_scatter_mie_dir, 1.5) * (2.0 + single_scatter_mie_dir_sq));
|
||||
|
||||
// Sample the primary ray
|
||||
for (int i = 0; i < nishita_iSteps; i++) {
|
||||
for (int i = 0; i < single_scatter_iSteps; i++) {
|
||||
|
||||
// Calculate the primary ray sample position and height
|
||||
vec3 iPos = r0 + r * (iTime + iStepSize * 0.5);
|
||||
float iHeight = length(iPos) - rPlanet;
|
||||
|
||||
// Calculate the optical depth of the Rayleigh and Mie scattering for this step
|
||||
float odStepRlh = exp(-iHeight / nishita_rayleigh_scale) * nishitaDensity.x * iStepSize;
|
||||
float odStepMie = exp(-iHeight / nishita_mie_scale) * nishitaDensity.y * iStepSize;
|
||||
float odStepRlh = exp(-iHeight / single_scatter_rayleigh_scale) * skyDensity.x * iStepSize;
|
||||
float odStepMie = exp(-iHeight / single_scatter_mie_scale) * skyDensity.y * iStepSize;
|
||||
|
||||
// Accumulate optical depth
|
||||
iOdRlh += odStepRlh;
|
||||
@ -116,12 +116,12 @@ vec3 nishita_atmosphere(const vec3 r, const vec3 r0, const vec3 pSun, const floa
|
||||
// Idea behind this: "Rotate" everything by iPos (-> iPos is the new zenith) and then all calculations for the
|
||||
// inner integral only depend on the sample height (iHeight) and sunTheta (angle between sun and new zenith).
|
||||
float sunTheta = safe_acos(dot(normalize(iPos), normalize(pSun)));
|
||||
vec3 jAttn = nishita_lookupLUT(iHeight, sunTheta);
|
||||
vec3 jAttn = single_scatter_lookupLUT(iHeight, sunTheta);
|
||||
|
||||
// Calculate attenuation
|
||||
vec3 iAttn = exp(-(
|
||||
nishita_mie_coeff * iOdMie
|
||||
+ nishita_rayleigh_coeff * iOdRlh
|
||||
single_scatter_mie_coeff * iOdMie
|
||||
+ single_scatter_rayleigh_coeff * iOdRlh
|
||||
// + 0 for ozone
|
||||
));
|
||||
vec3 attn = iAttn * jAttn;
|
||||
@ -136,7 +136,7 @@ vec3 nishita_atmosphere(const vec3 r, const vec3 r0, const vec3 pSun, const floa
|
||||
iTime += iStepSize;
|
||||
}
|
||||
|
||||
return nishita_sun_intensity * (pRlh * nishita_rayleigh_coeff * totalRlh + pMie * nishita_mie_coeff * totalMie);
|
||||
return single_scatter_sun_intensity * (pRlh * single_scatter_rayleigh_coeff * totalRlh + pMie * single_scatter_mie_coeff * totalMie);
|
||||
}
|
||||
|
||||
vec3 sun_disk(const vec3 n, const vec3 light_dir, const float disk_size, const float intensity) {
|
||||
@ -149,7 +149,76 @@ vec3 sun_disk(const vec3 n, const vec3 light_dir, const float disk_size, const f
|
||||
float mu = sqrt(invDist * invDist);
|
||||
vec3 limb_darkening = 1.0 - (1.0 - pow(vec3(mu), sun_limb_darkening_col));
|
||||
|
||||
return 1 + (1.0 - step(1.0, dist)) * nishita_sun_intensity * intensity * limb_darkening;
|
||||
return 1 + (1.0 - step(1.0, dist)) * single_scatter_sun_intensity * intensity * limb_darkening;
|
||||
}
|
||||
|
||||
uniform sampler2D multiScatterLUT;
|
||||
uniform vec4 multiScatterParams; // x=elevation, y=rotation, z=angular_diameter, w=intensity
|
||||
uniform vec4 multiScatterSunBottom; // xyz=sun_bottom, w=earth_intersection_angle
|
||||
uniform vec3 multiScatterSunTop;
|
||||
|
||||
// XYZ to sRGB/Rec.709 conversion (D65 white point)
|
||||
vec3 xyz_to_rgb(vec3 xyz) {
|
||||
return vec3(
|
||||
3.2406 * xyz.x - 1.5372 * xyz.y - 0.4986 * xyz.z,
|
||||
-0.9689 * xyz.x + 1.8758 * xyz.y + 0.0415 * xyz.z,
|
||||
0.0557 * xyz.x - 0.2040 * xyz.y + 1.0570 * xyz.z
|
||||
);
|
||||
}
|
||||
|
||||
float sky_elevation_to_v(float elevation) {
|
||||
float abs_el = abs(elevation);
|
||||
float l = sign(elevation) * sqrt(abs_el / 1.5707963);
|
||||
float v = (l + 1.0) * 0.5;
|
||||
return clamp(v, 0.0, 1.0);
|
||||
}
|
||||
|
||||
vec3 multi_scatter_sample_lut(vec3 dir, float sun_rotation) {
|
||||
float azimuth = atan(dir.x, dir.y);
|
||||
float elevation = asin(clamp(dir.z, -1.0, 1.0));
|
||||
|
||||
azimuth -= sun_rotation;
|
||||
float u = fract(azimuth / (2.0 * PI));
|
||||
float v = sky_elevation_to_v(elevation);
|
||||
|
||||
return textureLod(multiScatterLUT, vec2(u, v), 0.0).rgb;
|
||||
}
|
||||
|
||||
vec3 multi_scatter_sun_disc(vec3 dir, vec3 sun_dir, float angular_diameter, float intensity) {
|
||||
float dist = distance(dir, sun_dir) / (angular_diameter * 0.5);
|
||||
if (dist > 1.0) return vec3(0.0);
|
||||
|
||||
float invDist = 1.0 - dist;
|
||||
float mu = sqrt(invDist * invDist);
|
||||
vec3 limb_darkening = 1.0 - (1.0 - pow(vec3(mu), sun_limb_darkening_col));
|
||||
|
||||
float sun_elev = multiScatterParams.x;
|
||||
float dir_elev = asin(clamp(dir.z, -1.0, 1.0));
|
||||
float t = clamp((dir_elev - (sun_elev - angular_diameter * 0.5)) / angular_diameter, 0.0, 1.0);
|
||||
vec3 sun_color = mix(multiScatterSunBottom.rgb, multiScatterSunTop, t) * intensity * limb_darkening;
|
||||
|
||||
return xyz_to_rgb(sun_color);
|
||||
}
|
||||
|
||||
vec3 multi_scatter_atmosphere(vec3 dir) {
|
||||
float sun_elevation = multiScatterParams.x;
|
||||
float sun_rotation = multiScatterParams.y;
|
||||
float angular_diameter = multiScatterParams.z;
|
||||
float sun_intensity = multiScatterParams.w;
|
||||
|
||||
vec3 xyz = multi_scatter_sample_lut(dir, sun_rotation);
|
||||
vec3 radiance = xyz_to_rgb(xyz);
|
||||
|
||||
if (sun_intensity > 0.0) {
|
||||
vec3 computed_sun_dir = vec3(
|
||||
sin(sun_rotation) * cos(sun_elevation),
|
||||
cos(sun_rotation) * cos(sun_elevation),
|
||||
sin(sun_elevation)
|
||||
);
|
||||
radiance += multi_scatter_sun_disc(dir, computed_sun_dir, angular_diameter, sun_intensity);
|
||||
}
|
||||
|
||||
return radiance;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -88,58 +88,103 @@ vec4 rayCast(vec3 dir) {
|
||||
}
|
||||
#endif //SSR
|
||||
|
||||
vec3 sampleWaterNormals(vec2 hitXY, float speed, out vec2 tcnor0, out vec2 tcnor1) {
|
||||
tcnor0 = hitXY / 3.0;
|
||||
vec3 n0 = textureLod(sdetail, tcnor0 + vec2(speed / 60.0, speed / 120.0), 0.0).rgb;
|
||||
tcnor1 = hitXY / 6.0 + n0.xy / 20.0;
|
||||
vec3 n1 = textureLod(sbase, tcnor1 + vec2(speed / 40.0, speed / 80.0), 0.0).rgb;
|
||||
return normalize(n0 + n1 - 1.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
float gdepth = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0;
|
||||
if (gdepth == 1.0) {
|
||||
fragColor = vec4(0.0);
|
||||
return;
|
||||
}
|
||||
// Eye below water
|
||||
if (eye.z < waterLevel) {
|
||||
fragColor = vec4(0.0);
|
||||
return;
|
||||
}
|
||||
// Displace surface
|
||||
vec3 vray = normalize(viewRay);
|
||||
vec3 p = getPos(eye, eyeLook, vray, gdepth, cameraProj);
|
||||
float speed = time * 2.0 * waterSpeed;
|
||||
p.z += sin(p.x * 10.0 / waterDisplace + speed) * cos(p.y * 10.0 / waterDisplace + speed) / 50.0 * waterDisplace;
|
||||
bool isSky = (gdepth == 1.0);
|
||||
|
||||
// Ray-plane intersection with water surface (z = waterLevel)
|
||||
float denom = dot(vray, vec3(0.0, 0.0, 1.0));
|
||||
float tWater = (waterLevel - eye.z) / denom;
|
||||
bool hasWaterHit = (abs(denom) > 0.0001) && (tWater > 0.0);
|
||||
|
||||
if (eye.z < waterLevel) {
|
||||
vec2 tc = texCoord;
|
||||
float fogFactor;
|
||||
|
||||
if (hasWaterHit && denom > 0.0) {
|
||||
// Looking up at water surface - apply normal distortion
|
||||
vec3 hit = eye + tWater * vray;
|
||||
vec2 tc0, tc1;
|
||||
vec3 n2 = sampleWaterNormals(hit.xy * waterFreq, speed, tc0, tc1);
|
||||
tc = texCoord + (n2.xy * n2.z) / 30.0 * waterRefract;
|
||||
fogFactor = clamp(tWater * waterDensity, 0.0, 0.95);
|
||||
} else {
|
||||
// Looking forward/down - distort via water surface above fragment
|
||||
vec3 p = getPos(eye, eyeLook, vray, gdepth, cameraProj);
|
||||
vec2 wxy = isSky ? (eye.xy + vray.xy * 50.0) : p.xy;
|
||||
vec2 tc0, tc1;
|
||||
vec3 n2 = sampleWaterNormals(wxy * waterFreq, speed, tc0, tc1);
|
||||
tc = texCoord + (n2.xy * n2.z) / 30.0 * waterRefract;
|
||||
float waterDist = isSky ? 50.0 : length(p - eye);
|
||||
fogFactor = clamp(waterDist * waterDensity, 0.0, 0.95);
|
||||
}
|
||||
|
||||
vec3 refracted = textureLod(tex, tc, 0.0).rgb;
|
||||
fragColor.rgb = mix(refracted, waterColor, fogFactor);
|
||||
fragColor.a = 1.0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Above water
|
||||
if (p.z > waterLevel) {
|
||||
if (!hasWaterHit || denom >= 0.0) {
|
||||
fragColor = vec4(0.0);
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
float horizonFactor = clamp(1.0 - tWater / 60.0, 0.0, 1.0);
|
||||
if (!isSky && p.z > waterLevel) {
|
||||
fragColor = vec4(0.0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Displace surface
|
||||
p.z += (sin(p.x * 10.0 / waterDisplace + speed) * cos(p.y * 10.0 / waterDisplace + 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
|
||||
vec3 v = normalize(eye - p.xyz);
|
||||
float t = -(dot(eye, vec3(0.0, 0.0, 1.0)) - waterLevel) / dot(v, vec3(0.0, 0.0, 1.0));
|
||||
vec3 v = normalize(eye - p);
|
||||
float t = (waterLevel - eye.z) / dot(v, vec3(0.0, 0.0, 1.0));
|
||||
vec3 hit = eye + t * v;
|
||||
hit.xy *= waterFreq;
|
||||
hit.z += waterLevel;
|
||||
|
||||
// Sample normal maps
|
||||
vec2 tcnor0 = hit.xy / 3.0;
|
||||
vec3 n0 = textureLod(sdetail, tcnor0 + vec2(speed / 60.0, speed / 120.0), 0.0).rgb;
|
||||
vec2 tcnor0, tcnor1;
|
||||
vec3 n2 = sampleWaterNormals(hit.xy * waterFreq, speed, tcnor0, tcnor1);
|
||||
|
||||
vec2 tcnor1 = hit.xy / 6.0 + n0.xy / 20.0;
|
||||
vec3 n1 = textureLod(sbase, tcnor1 + vec2(speed / 40.0, speed / 80.0), 0.0).rgb;
|
||||
vec3 n2 = normalize(((n1 + n0) / 2.0) * 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);
|
||||
vec2 tc = p2.z > waterLevel ? texCoord : texCoord + (n2.xy * n2.z) / 30.0 * waterRefract;
|
||||
// Refraction
|
||||
vec2 tc;
|
||||
if (isSky) {
|
||||
tc = texCoord + (n2.xy * n2.z) / 30.0 * waterRefract;
|
||||
} else {
|
||||
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);
|
||||
tc = p2.z > waterLevel ? texCoord : texCoord + (n2.xy * n2.z) / 30.0 * waterRefract;
|
||||
}
|
||||
|
||||
// Light
|
||||
float fresnel = 1.0 - max(dot(n2, v), 0.0);
|
||||
fresnel = pow(fresnel, 30.0) * 0.45;
|
||||
fresnel = 0.02 + 0.98 * pow(fresnel, 5.0);
|
||||
vec3 r = reflect(-v, n2);
|
||||
#ifdef _Rad
|
||||
vec3 reflectedEnv = textureLod(senvmapRadiance, envMapEquirect(r), 0).rgb;
|
||||
vec3 reflectedEnv = textureLod(senvmapRadiance, envMapEquirect(r), 0).rgb;
|
||||
#else
|
||||
const vec3 reflectedEnv = vec3(0.5);
|
||||
#endif
|
||||
vec3 refracted = textureLod(tex, tc, 0.0).rgb;
|
||||
|
||||
|
||||
#ifdef _SSR
|
||||
float roughness = 0.1;//unpackFloat(g0.b).y;
|
||||
//if (roughness == 1.0) { fragColor.rgb = vec3(0.0); return; }
|
||||
@ -147,8 +192,8 @@ void main() {
|
||||
float spec = 0.9;//fract(textureLod(gbuffer1, texCoord, 0.0).a);
|
||||
//if (spec == 0.0) { fragColor.rgb = vec3(0.0); return; }
|
||||
|
||||
vec3 viewNormal = n2;
|
||||
vec3 viewPos = getPosView(viewRay, gdepth, cameraProj);
|
||||
vec3 viewNormal = V3 * n2;
|
||||
vec3 viewPos = isSky ? vec3(0.0) : getPosView(viewRay, gdepth, cameraProj);
|
||||
vec3 reflected = reflect(normalize(viewPos), viewNormal);
|
||||
hitCoord = viewPos;
|
||||
|
||||
@ -158,7 +203,6 @@ void main() {
|
||||
vec3 dir = reflected * (1.0 - rand(texCoord) * ssrJitter * roughness) * 2.0;
|
||||
#endif
|
||||
|
||||
// * max(ssrMinRayStep, -viewPos.z)
|
||||
vec4 coords = rayCast(dir);
|
||||
|
||||
vec2 deltaCoords = abs(vec2(0.5, 0.5) - coords.xy);
|
||||
@ -177,20 +221,32 @@ void main() {
|
||||
#else
|
||||
fragColor.rgb = mix(refracted, reflectedEnv, waterReflect * fresnel);
|
||||
#endif
|
||||
fragColor.rgb *= waterColor;
|
||||
fragColor.rgb += clamp(pow(max(dot(r, ld), 0.0), 200.0) * (200.0 + 8.0) / (PI * 8.0), 0.0, 2.0);
|
||||
fragColor.rgb *= 1.0 - (clamp(-(p.z - waterLevel) * waterDensity, 0.0, 0.9));
|
||||
fragColor.a = clamp(abs(p.z - waterLevel) * 5.0, 0.0, 1.0);
|
||||
// Water color tint - blend rather than multiply to preserve brightness
|
||||
float colorMix = isSky ? 0.7 : 0.5;
|
||||
fragColor.rgb = mix(fragColor.rgb, fragColor.rgb * waterColor, colorMix * horizonFactor);
|
||||
// Blinn-Phong specular using half-vector, faded at horizon
|
||||
vec3 h = normalize(v + ld);
|
||||
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) * horizonFactor;
|
||||
// Depth fog - blend toward waterColor with depth, faded at horizon
|
||||
float depthFog = clamp(-(p.z - waterLevel) * waterDensity, 0.0, 0.9);
|
||||
fragColor.rgb = mix(fragColor.rgb, waterColor, depthFog * horizonFactor);
|
||||
// Alpha fades smoothly at horizon instead of hard cut
|
||||
fragColor.a = isSky ? horizonFactor : clamp(abs(p.z - waterLevel) * 5.0, 0.0, 1.0);
|
||||
|
||||
// Foam
|
||||
float fd = abs(p.z - waterLevel);
|
||||
// Foam - based on actual geometry depth below water surface
|
||||
float fd = isSky ? 1.0 : abs(p.z - waterLevel);
|
||||
if (fd < 0.1) {
|
||||
// Based on foam by Owen Deery
|
||||
// http://fire-face.com/personal/water
|
||||
vec3 foamMask0 = textureLod(sfoam, tcnor0 * 10, 0.0).rgb;
|
||||
vec3 foamMask1 = textureLod(sfoam, tcnor1 * 11, 0.0).rgb;
|
||||
vec3 foam = vec3(1.0) - foamMask0.rrr - foamMask1.bbb;
|
||||
float fac = 1.0 - (fd * (1.0 / 0.1));
|
||||
fragColor.rgb = mix(fragColor.rgb, clamp(foam, 0.0, 1.0), clamp(fac, 0.0, 1.0));
|
||||
// Distance-based LOD blurs foam at range to reduce noise
|
||||
float foamLod = clamp(tWater / 15.0, 0.0, 5.0);
|
||||
vec2 foamUV0 = tcnor0 * 3.0 + vec2(speed / 30.0, speed / 50.0);
|
||||
vec2 foamUV1 = tcnor1 * 4.0 + vec2(-speed / 35.0, speed / 45.0);
|
||||
vec3 foamMask0 = textureLod(sfoam, foamUV0, 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 fac = (1.0 - (fd * (1.0 / 0.1))) * horizonFactor;
|
||||
fragColor.rgb = mix(fragColor.rgb, mix(fragColor.rgb, waterColor + 0.2, foamStrength), clamp(fac, 0.0, 1.0) * 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user