From 9983a0c68c90c5488b0065e7f8e607e9ac93d1b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Garc=C3=ADa=20Li=C3=B1=C3=A1n?= Date: Wed, 25 Aug 2021 04:17:09 +0200 Subject: [PATCH] HDR: Water rendering --- Compositor/HDR/hdr.xml | 38 ++++++ Effects/HDR/water-lighting.eff | 49 ++++++++ Effects/water.eff | 131 ++++++++++++++++++- Effects/ws30water.eff | 49 +------- Shaders/HDR/geometry-water.frag | 214 ++++++++++++++++++++++++++++++++ Shaders/HDR/geometry-water.vert | 104 ++++++++++++++++ Shaders/HDR/water-lighting.frag | 80 ++++++++++++ 7 files changed, 611 insertions(+), 54 deletions(-) create mode 100644 Effects/HDR/water-lighting.eff create mode 100644 Shaders/HDR/geometry-water.frag create mode 100644 Shaders/HDR/geometry-water.vert create mode 100644 Shaders/HDR/water-lighting.frag diff --git a/Compositor/HDR/hdr.xml b/Compositor/HDR/hdr.xml index 51390c204..d5b0d7e38 100644 --- a/Compositor/HDR/hdr.xml +++ b/Compositor/HDR/hdr.xml @@ -761,6 +761,44 @@ + + water + quad + Effects/HDR/water-lighting + + 0 + gbuffer0 + + + 1 + gbuffer1 + + + 3 + depth-stencil + + + 9 + prefiltered-envmap + + + 11 + aerial-perspective + + + 12 + transmittance + + + color0 + hdr-result + + + stencil + depth-stencil + + + + + aerial_perspective_lut + sampler-2d + 11 + + + transmittance_lut + sampler-2d + 12 + + + + diff --git a/Effects/water.eff b/Effects/water.eff index 05a1c944f..b6648efe1 100644 --- a/Effects/water.eff +++ b/Effects/water.eff @@ -207,12 +207,6 @@ - - 6 - 7 - - - @@ -1449,4 +1443,129 @@ + + + hdr-geometry + + + + gequal + 1.0 + 0.0 + + + always + 3 + replace + + 0 + back + + render-bin/bin-number + render-bin/bin-name + + + 0 + texture[2]/image + texture[2]/type + texture[2]/filter + texture[2]/wrap-s + texture[2]/wrap-t + texture[2]/internal-format + + + 1 + texture[3]/image + texture[3]/type + texture[3]/filter + texture[3]/wrap-s + texture[3]/wrap-t + texture[3]/internal-format + + + 2 + texture[6]/image + texture[6]/type + texture[6]/filter + texture[6]/wrap-s + texture[6]/wrap-t + texture[6]/internal-format + + + 3 + texture[8]/image + texture[8]/type + texture[8]/filter + texture[8]/wrap-s + texture[8]/wrap-t + texture[8]/internal-format + + + Shaders/HDR/geometry-water.vert + Shaders/HDR/geometry-water.frag + Shaders/HDR/gbuffer-include.frag + + + water_normalmap + sampler-2d + 0 + + + water_dudvmap + sampler-2d + 1 + + + perlin_normalmap + sampler-2d + 2 + + + water_colormap + sampler-2d + 3 + + + WindE + float + windE + + + WindN + float + windN + + + WaveFreq + float + WaveFreq + + + WaveAmp + float + WaveAmp + + + WaveSharp + float + WaveSharp + + + WaveAngle + float + WaveAngle + + + WaveFactor + float + WaveFactor + + + WaveDAngle + float + WaveDAngle + + + + diff --git a/Effects/ws30water.eff b/Effects/ws30water.eff index 9b2f34291..d65b23dde 100644 --- a/Effects/ws30water.eff +++ b/Effects/ws30water.eff @@ -1,7 +1,7 @@ Effects/ws30water - Effects/water + Effects/water @@ -114,51 +114,4 @@ - - - hdr-geometry - - - - gequal - 1.0 - 0.0 - - - always - 3 - replace - - - transparent - transparent - smooth - back - - render-bin/bin-number - render-bin/bin-name - - - 0 - 2d - Textures/Terrain/water.png - linear-mipmap-linear - repeat - repeat - - modulate - - - - Shaders/HDR/geometry.vert - Shaders/HDR/geometry.frag - Shaders/HDR/gbuffer-include.frag - - - color_tex - sampler-2d - 0 - - - diff --git a/Shaders/HDR/geometry-water.frag b/Shaders/HDR/geometry-water.frag new file mode 100644 index 000000000..b21cd5133 --- /dev/null +++ b/Shaders/HDR/geometry-water.frag @@ -0,0 +1,214 @@ +#version 330 core + +layout(location = 0) out vec4 gbuffer0; +layout(location = 1) out vec2 gbuffer1; + +in vec4 waterTex1; +in vec4 waterTex2; +in mat3 TBN; +in vec3 ecPosition; +in vec2 TopoUV; + +uniform sampler2D perlin_normalmap; +uniform sampler2D water_dudvmap; +uniform sampler2D water_normalmap; +uniform sampler2D water_colormap; + +uniform float WindE; +uniform float WindN; +uniform float WaveFreq; +uniform float WaveAmp; +uniform float WaveSharp; +uniform float WaveAngle; +uniform float WaveFactor; +uniform float WaveDAngle; + +uniform float osg_SimulationTime; +uniform vec3 fg_SunDirection; + +vec2 encodeNormal(vec3 n); +vec3 decodeSRGB(vec3 screenRGB); + +void rotationmatrix(float angle, out mat4 rotmat) +{ + rotmat = mat4( cos( angle ), -sin( angle ), 0.0, 0.0, + sin( angle ), cos( angle ), 0.0, 0.0, + 0.0 , 0.0 , 1.0, 0.0, + 0.0 , 0.0 , 0.0, 1.0 ); +} + +// wave functions /////////////////////// + +struct Wave { + float freq; // 2*PI / wavelength + float amp; // amplitude + float phase; // speed * 2*PI / wavelength + vec2 dir; +}; + +Wave wave0 = Wave(1.0, 1.0, 0.5, vec2(0.97, 0.25)); +Wave wave1 = Wave(2.0, 0.5, 1.3, vec2(0.97, -0.25)); +Wave wave2 = Wave(1.0, 1.0, 0.6, vec2(0.95, -0.3)); +Wave wave3 = Wave(2.0, 0.5, 1.4, vec2(0.99, 0.1)); + +float evaluateWave(in Wave w, vec2 pos, float t) +{ + return w.amp * sin( dot(w.dir, pos) * w.freq + t * w.phase); +} + +// derivative of wave function +float evaluateWaveDeriv(Wave w, vec2 pos, float t) +{ + return w.freq * w.amp * cos( dot(w.dir, pos)*w.freq + t*w.phase); +} + +// sharp wave functions +float evaluateWaveSharp(Wave w, vec2 pos, float t, float k) +{ + return w.amp * pow(sin( dot(w.dir, pos)*w.freq + t*w.phase)* 0.5 + 0.5 , k); +} + +float evaluateWaveDerivSharp(Wave w, vec2 pos, float t, float k) +{ + return k*w.freq*w.amp * pow(sin( dot(w.dir, pos)*w.freq + t*w.phase)* 0.5 + 0.5 , k - 1) * cos( dot(w.dir, pos)*w.freq + t*w.phase); +} + +void sumWaves(float angle, float dangle, float windScale, float factor, out float ddx, float ddy) +{ + mat4 RotationMatrix; + float deriv; + vec4 P = waterTex1 * 1024; + + rotationmatrix(radians(angle + dangle * windScale + 0.6 * sin(P.x * factor)), RotationMatrix); + P *= RotationMatrix; + + P.y += evaluateWave(wave0, P.xz, osg_SimulationTime); + deriv = evaluateWaveDeriv(wave0, P.xz, osg_SimulationTime ); + ddx = deriv * wave0.dir.x; + ddy = deriv * wave0.dir.y; + + P.y += evaluateWave(wave1, P.xz, osg_SimulationTime); + deriv = evaluateWaveDeriv(wave1, P.xz, osg_SimulationTime); + ddx += deriv * wave1.dir.x; + ddy += deriv * wave1.dir.y; + + P.y += evaluateWaveSharp(wave2, P.xz, osg_SimulationTime, WaveSharp); + deriv = evaluateWaveDerivSharp(wave2, P.xz, osg_SimulationTime, WaveSharp); + ddx += deriv * wave2.dir.x; + ddy += deriv * wave2.dir.y; + + P.y += evaluateWaveSharp(wave3, P.xz, osg_SimulationTime, WaveSharp); + deriv = evaluateWaveDerivSharp(wave3, P.xz, osg_SimulationTime, WaveSharp); + ddx += deriv * wave3.dir.x; + ddy += deriv * wave3.dir.y; +} + +void main() +{ + const vec4 sca = vec4(0.005, 0.005, 0.005, 0.005); + const vec4 sca2 = vec4(0.02, 0.02, 0.02, 0.02); + const vec4 tscale = vec4(0.25, 0.25, 0.25, 0.25); + + mat4 RotationMatrix; + + float windEffect = sqrt(WindE*WindE + WindN*WindN) * 0.6; + float windScale = 15.0/(3.0 + windEffect); + float windEffect_low = 0.3 + 0.7 * smoothstep(0.0, 5.0, windEffect); + float waveRoughness = 0.01 + smoothstep(0.0, 40.0, windEffect); + + float mixFactor = 0.2 + 0.02 * smoothstep(0.0, 50.0, windEffect); + mixFactor = clamp(mixFactor, 0.3, 0.8); + + // sine waves + float ddx, ddx1, ddx2, ddx3, ddy, ddy1, ddy2, ddy3; + float angle; + ddx = 0.0, ddy = 0.0; + ddx1 = 0.0, ddy1 = 0.0; + ddx2 = 0.0, ddy2 = 0.0; + ddx3 = 0.0, ddy3 = 0.0; + + // there's no need to do wave patterns or foam for pixels which are so + // far away that we can't actually see them + // we only need detail in the near zone or where the sun reflection is + float dist = length(ecPosition); + bool detailed = (dist < 15000.0) + || (dot(fg_SunDirection, normalize(ecPosition)) >= 0.7); + if (detailed) { + angle = 0.0; + wave0.freq = WaveFreq ; + wave0.amp = WaveAmp; + wave0.dir = vec2 (0.0, 1.0); //vec2(cos(radians(angle)), sin(radians(angle))); + + angle -= 45; + wave1.freq = WaveFreq * 2.0 ; + wave1.amp = WaveAmp * 1.25; + wave1.dir = vec2(0.70710, -0.7071); //vec2(cos(radians(angle)), sin(radians(angle))); + + angle += 30; + wave2.freq = WaveFreq * 3.5; + wave2.amp = WaveAmp * 0.75; + wave2.dir = vec2(0.96592, -0.2588);// vec2(cos(radians(angle)), sin(radians(angle))); + + angle -= 50; + wave3.freq = WaveFreq * 3.0 ; + wave3.amp = WaveAmp * 0.75; + wave3.dir = vec2(0.42261, -0.9063); //vec2(cos(radians(angle)), sin(radians(angle))); + + sumWaves(WaveAngle, -1.5, windScale, WaveFactor, ddx, ddy); + sumWaves(WaveAngle, 1.5, windScale, WaveFactor, ddx1, ddy1); + + //reset the waves + angle = 0.0; + float waveamp = WaveAmp * 0.75; + + wave0.freq = WaveFreq ; + wave0.amp = waveamp; + wave0.dir = vec2 (0.0, 1.0); //vec2(cos(radians(angle)), sin(radians(angle))); + + angle -= 20; + wave1.freq = WaveFreq * 2.0 ; + wave1.amp = waveamp * 1.25; + wave1.dir = vec2(0.93969, -0.34202);// vec2(cos(radians(angle)), sin(radians(angle))); + + angle += 35; + wave2.freq = WaveFreq * 3.5; + wave2.amp = waveamp * 0.75; + wave2.dir = vec2(0.965925, 0.25881); //vec2(cos(radians(angle)), sin(radians(angle))); + + angle -= 45; + wave3.freq = WaveFreq * 3.0 ; + wave3.amp = waveamp * 0.75; + wave3.dir = vec2(0.866025, -0.5); //vec2(cos(radians(angle)), sin(radians(angle))); + + sumWaves(WaveAngle + WaveDAngle, -1.5, windScale, WaveFactor, ddx2, ddy2); + sumWaves(WaveAngle + WaveDAngle, 1.5, windScale, WaveFactor, ddx3, ddy3); + } + + vec4 disdis = texture(water_dudvmap, vec2(waterTex2 * tscale)* windScale) * 2.0 - 1.0; + + vec3 N0 = vec3(texture(water_normalmap, vec2(waterTex1 + disdis * sca2) * windScale) * 2.0 - 1.0); + vec3 N1 = vec3(texture(perlin_normalmap, vec2(waterTex1 + disdis * sca) * windScale) * 2.0 - 1.0); + + N0 += vec3(texture(water_normalmap, vec2(waterTex1 * tscale) * windScale) * 2.0 - 1.0); + N1 += vec3(texture(perlin_normalmap, vec2(waterTex2 * tscale) * windScale) * 2.0 - 1.0); + + rotationmatrix(radians(2.0 * sin(osg_SimulationTime * 0.005)), RotationMatrix); + N0 += vec3(texture(water_normalmap, vec2(waterTex2 * RotationMatrix * (tscale + sca2)) * windScale) * 2.0 - 1.0); + N1 += vec3(texture(perlin_normalmap, vec2(waterTex2 * RotationMatrix * (tscale + sca2)) * windScale) * 2.0 - 1.0); + + rotationmatrix(radians(-4.0 * sin(osg_SimulationTime * 0.003)), RotationMatrix); + N0 += vec3(texture(water_normalmap, vec2(waterTex1 * RotationMatrix + disdis * sca2) * windScale) * 2.0 - 1.0); + N1 += vec3(texture(perlin_normalmap, vec2(waterTex1 * RotationMatrix + disdis * sca) * windScale) * 2.0 - 1.0); + + N0 *= windEffect_low; + N1 *= windEffect_low; + + N0.r += (ddx + ddx1 + ddx2 + ddx3); + N0.g += (ddy + ddy1 + ddy2 + ddy3); + + vec3 N = normalize(mix(N0, N1, mixFactor) * waveRoughness); + gbuffer1 = encodeNormal(TBN * N); + + vec3 floorColor = decodeSRGB(texture(water_colormap, TopoUV).rgb); + gbuffer0.rgb = floorColor; +} diff --git a/Shaders/HDR/geometry-water.vert b/Shaders/HDR/geometry-water.vert new file mode 100644 index 000000000..8a6078a76 --- /dev/null +++ b/Shaders/HDR/geometry-water.vert @@ -0,0 +1,104 @@ +#version 330 core + +layout(location = 0) in vec4 pos; +layout(location = 3) in vec4 multiTexCoord0; + +out vec4 waterTex1; +out vec4 waterTex2; +out mat3 TBN; +out vec3 ecPosition; +out vec2 TopoUV; + +uniform float WindE, WindN; + +uniform float osg_SimulationTime; +uniform mat4 osg_ModelViewMatrix; +uniform mat4 osg_ModelViewProjectionMatrix; +uniform mat4 osg_ViewMatrixInverse; +uniform mat3 osg_NormalMatrix; + +// constants for the cartesian to geodetic conversion. +const float a = 6378137.0; //float a = equRad; +const float squash = 0.9966471893352525192801545; +const float latAdjust = 0.9999074159800018; //geotiff source for the depth map +const float lonAdjust = 0.9999537058469516; //actual extents: +-180.008333333333326/+-90.008333333333340 + +void rotationmatrix(float angle, out mat4 rotmat) +{ + rotmat = mat4( cos( angle ), -sin( angle ), 0.0, 0.0, + sin( angle ), cos( angle ), 0.0, 0.0, + 0.0 , 0.0 , 1.0, 0.0, + 0.0 , 0.0 , 0.0, 1.0 ); +} + +void main() +{ + gl_Position = osg_ModelViewProjectionMatrix * pos; + ecPosition = (osg_ModelViewMatrix * pos).xyz; + + // Using precalculated vectors + // vec3 T = normalize(osg_NormalMatrix * tangent); + // vec3 B = normalize(osg_NormalMatrix * binormal); + // vec3 N = normalize(osg_NormalMatrix * normal); + + vec3 T = normalize(osg_NormalMatrix * vec3(0.0, -1.0, 0.0)); + vec3 B = normalize(osg_NormalMatrix * vec3(1.0, 0.0, 0.0)); + vec3 N = normalize(osg_NormalMatrix * vec3(0.0, 0.0, 1.0)); + TBN = mat3(T, B, N); + + mat4 RotationMatrix; + + vec4 t1 = vec4(0.0, osg_SimulationTime * 0.005217, 0.0, 0.0); + vec4 t2 = vec4(0.0, osg_SimulationTime * -0.0012, 0.0, 0.0); + + float Angle; + float windFactor = sqrt(WindE * WindE + WindN * WindN) * 0.05; + + if (WindN == 0.0 && WindE == 0.0) { + Angle = 0.0; + }else{ + Angle = atan(-WindN, WindE) - atan(1.0); + } + + rotationmatrix(Angle, RotationMatrix); + waterTex1 = multiTexCoord0 * RotationMatrix - t1 * windFactor; + + rotationmatrix(Angle, RotationMatrix); + waterTex2 = multiTexCoord0 * RotationMatrix - t2 * windFactor; + + // Geodesy lookup for depth map + vec3 rawPos = (osg_ViewMatrixInverse * vec4(ecPosition, 1.0)).xyz; + float e2 = abs(1.0 - squash * squash); + float ra2 = 1.0/(a * a); + float e4 = e2 * e2; + float XXpYY = rawPos.x * rawPos.x + rawPos.y * rawPos.y; + float Z = rawPos.z; + float sqrtXXpYY = sqrt(XXpYY); + float p = XXpYY * ra2; + float q = Z*Z*(1.0-e2)*ra2; + float r = 1.0/6.0*(p + q - e4); + float s = e4 * p * q/(4.0*r*r*r); + if ( s >= 2.0 && s <= 0.0) + s = 0.0; + float t = pow(1.0+s+sqrt(s*2.0+s*s), 1.0/3.0); + float u = r + r*t + r/t; + float v = sqrt(u*u + e4*q); + float w = (e2*u+ e2*v-e2*q)/(2.0*v); + float k = sqrt(u+v+w*w)-w; + float D = k*sqrtXXpYY/(k+e2); + + vec2 NormPosXY = normalize(rawPos.xy); + vec2 NormPosXZ = normalize(vec2(D, rawPos.z)); + float signS = sign(rawPos.y); + if (-0.00015 <= rawPos.y && rawPos.y<=.00015) + signS = 1.0; + float signT = sign(rawPos.z); + if (-0.0002 <= rawPos.z && rawPos.z<=.0002) + signT = 1.0; + float cosLon = dot(NormPosXY, vec2(1.0,0.0)); + float cosLat = dot(abs(NormPosXZ), vec2(1.0,0.0)); + TopoUV.s = signS * lonAdjust * degrees(acos(cosLon))/180.; + TopoUV.t = signT * latAdjust * degrees(acos(cosLat))/90.; + TopoUV.s = TopoUV.s * 0.5 + 0.5; + TopoUV.t = TopoUV.t * 0.5 + 0.5; +} diff --git a/Shaders/HDR/water-lighting.frag b/Shaders/HDR/water-lighting.frag new file mode 100644 index 000000000..6daf36453 --- /dev/null +++ b/Shaders/HDR/water-lighting.frag @@ -0,0 +1,80 @@ +#version 330 core + +out vec3 fragColor; + +in vec2 texCoord; + +uniform sampler2D gbuffer0_tex; +uniform sampler2D gbuffer1_tex; +uniform sampler2D depth_tex; +uniform samplerCube prefiltered_envmap; +uniform sampler2D transmittance_lut; + +uniform mat4 fg_ViewMatrixInverse; +uniform vec3 fg_SunDirection; +uniform float fg_SunZenithCosTheta; + +const float PI = 3.14159265359; +const float RECIPROCAL_PI = 0.31830988618; +const float MAX_PREFILTERED_LOD = 4.0; +const vec3 EXTRATERRESTRIAL_SOLAR_ILLUMINANCE = vec3(128.0); + +vec3 decodeNormal(vec2 enc); +vec3 positionFromDepth(vec2 pos, float depth); +vec3 addAerialPerspective(vec3 color, vec2 coord, float depth); + +float F_Schlick(float VdotH, float F0) +{ + return F0 + (1.0 - F0) * pow(clamp(1.0 - VdotH, 0.0, 1.0), 5.0); +} + +float D_GGX(float NdotH, float a2) +{ + float f = (NdotH * a2 - NdotH) * NdotH + 1.0; + return a2 / (PI * f * f); +} + +void main() +{ + float depth = texture(depth_tex, texCoord).r; + vec3 pos = positionFromDepth(texCoord, depth); + + vec3 v = normalize(-pos); + vec3 n = decodeNormal(texture(gbuffer1_tex, texCoord).rg); + vec3 l = fg_SunDirection; + + vec3 reflected = reflect(-v, n); + vec3 worldNormal = (fg_ViewMatrixInverse * vec4(n, 0.0)).xyz; + vec3 worldReflected = (fg_ViewMatrixInverse * vec4(reflected, 0.0)).xyz; + + float NdotL = clamp(dot(n, l), 0.0, 1.0); + float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0); + vec3 h = normalize(v + l); + float NdotH = clamp(dot(n, h), 0.0, 1.0); + + // Get transmittance from Sun to the sea surface (assume the water is + // always at sea level, i.e. normalizedAltitude = 0) + vec3 transmittance = texture(transmittance_lut, + vec2(fg_SunZenithCosTheta * 0.5 + 0.5, 0.0)).rgb; + vec3 sunIntensity = EXTRATERRESTRIAL_SOLAR_ILLUMINANCE * transmittance; + + const float f0 = 0.02; // For IOR=1.33 + float fresnel = F_Schlick(NdotV, f0); + + // Refracted light + vec3 seaColor = texture(gbuffer0_tex, texCoord).rgb; + vec3 Esky = textureLod(prefiltered_envmap, worldNormal, MAX_PREFILTERED_LOD).rgb; + vec3 refracted = seaColor * Esky * RECIPROCAL_PI; + + // Reflected sky light + vec3 reflection = textureLod(prefiltered_envmap, worldReflected, 1.0).rgb; + + vec3 color = mix(refracted, reflection, fresnel); + + // Add reflected Sun light + color += RECIPROCAL_PI * fresnel * D_GGX(NdotH, 0.001) * sunIntensity * NdotL; + + color = addAerialPerspective(color, texCoord, length(pos)); + + fragColor = color; +}