diff --git a/Compositor/HDR/hdr.xml b/Compositor/HDR/hdr.xml index 11bf2972a..4ed349986 100644 --- a/Compositor/HDR/hdr.xml +++ b/Compositor/HDR/hdr.xml @@ -83,18 +83,7 @@ 2d 256 64 - rgb16f - linear - linear - clamp-to-edge - clamp-to-edge - - - multiple-scattering - 2d - 32 - 32 - rgb16f + rgba16f linear linear clamp-to-edge @@ -104,8 +93,8 @@ sky-view 2d 256 - 128 - rgb16f + 256 + rgba16f linear linear repeat @@ -164,19 +153,27 @@ true - + - luminance + histogram-partial 2d - 1024 - 1024 - r32f - linear-mipmap-nearest + screen + 256 + r32ui + nearest nearest - 10 - prev-luminance + histogram + 2d + 256 + 1 + r32ui + nearest + nearest + + + histogram-luminance 2d 1 1 @@ -185,7 +182,7 @@ nearest - adapted-luminance + prev-luminance 2d 1 1 @@ -296,14 +293,7 @@ atmos-transmittance @@ -314,19 +304,6 @@ transmittance - - atmos-multiple-scattering - quad - Effects/HDR/atmos-multiple-scattering - - 0 - transmittance - - - color0 - multiple-scattering - - atmos-sky-view quad @@ -335,10 +312,6 @@ 0 transmittance - - 1 - multiple-scattering - color0 sky-view @@ -352,10 +325,6 @@ 0 transmittance - - 1 - multiple-scattering - color0 aerial-perspective @@ -650,6 +619,15 @@ depth stencil 0.0 0xfffff7ff + + 128 + 1 + 1 + 1024 + 1024 + true + false + color0 gbuffer0 @@ -771,6 +749,13 @@ csm1 csm2 csm3 + + geometry + 5 + 6 + 14 + 15 + 0 gbuffer0 @@ -918,45 +903,62 @@ - luminance + histogram-column quad - Effects/HDR/luminance + Effects/HDR/histogram-column 0 hdr-result color0 - luminance - true + histogram-partial - adapt-luminance + histogram-aggregate quad - Effects/HDR/adapt-luminance + Effects/HDR/histogram-aggregate 0 - prev-luminance - - - 1 - luminance + histogram-partial color0 - adapted-luminance + histogram + + + + histogram-luminance + quad + Effects/HDR/histogram-luminance + + 0 + histogram + + + 1 + prev-luminance + + + color0 + histogram-luminance @@ -965,7 +967,7 @@ Effects/HDR/copy-prev-luminance 0 - adapted-luminance + histogram-luminance color0 @@ -990,7 +992,7 @@ 1 - adapted-luminance + histogram-luminance color0 @@ -1152,7 +1154,7 @@ 1 - adapted-luminance + histogram-luminance 4 diff --git a/Effects/HDR/atmos-aerial-perspective.eff b/Effects/HDR/atmos-aerial-perspective.eff index e07dd4495..96c90aefd 100644 --- a/Effects/HDR/atmos-aerial-perspective.eff +++ b/Effects/HDR/atmos-aerial-perspective.eff @@ -14,11 +14,6 @@ sampler-2d 0 - - multiscattering_lut - sampler-2d - 1 - diff --git a/Effects/HDR/atmos-multiple-scattering.eff b/Effects/HDR/atmos-multiple-scattering.eff deleted file mode 100644 index 8f55d189c..000000000 --- a/Effects/HDR/atmos-multiple-scattering.eff +++ /dev/null @@ -1,18 +0,0 @@ - - - Effects/HDR/atmos-multiple-scattering - - - - Shaders/HDR/trivial.vert - Shaders/HDR/atmos-multiple-scattering.frag - Shaders/HDR/atmos-include.frag - - - transmittance_lut - sampler-2d - 0 - - - - diff --git a/Effects/HDR/atmos-sky-view.eff b/Effects/HDR/atmos-sky-view.eff index 0c9cb3ae5..93e427c4b 100644 --- a/Effects/HDR/atmos-sky-view.eff +++ b/Effects/HDR/atmos-sky-view.eff @@ -13,11 +13,6 @@ sampler-2d 0 - - multiscattering_lut - sampler-2d - 1 - diff --git a/Effects/HDR/copy-prev-luminance.eff b/Effects/HDR/copy-prev-luminance.eff index cb30811cd..8230fcd87 100644 --- a/Effects/HDR/copy-prev-luminance.eff +++ b/Effects/HDR/copy-prev-luminance.eff @@ -8,7 +8,7 @@ Shaders/HDR/copy-prev-luminance.frag - lum_tex + tex sampler-2d 0 diff --git a/Effects/HDR/luminance.eff b/Effects/HDR/histogram-aggregate.eff similarity index 51% rename from Effects/HDR/luminance.eff rename to Effects/HDR/histogram-aggregate.eff index 1bbbf51e7..d86b26961 100644 --- a/Effects/HDR/luminance.eff +++ b/Effects/HDR/histogram-aggregate.eff @@ -1,14 +1,14 @@ - Effects/HDR/luminance + Effects/HDR/histogram-aggregate - Shaders/HDR/trivial.vert - Shaders/HDR/luminance.frag + Shaders/HDR/trivial-notexcoord.vert + Shaders/HDR/histogram-aggregate.frag - hdr_tex + partial_histogram_tex sampler-2d 0 diff --git a/Effects/HDR/histogram-column.eff b/Effects/HDR/histogram-column.eff new file mode 100644 index 000000000..85668ab9a --- /dev/null +++ b/Effects/HDR/histogram-column.eff @@ -0,0 +1,18 @@ + + + Effects/HDR/histogram-column + + + + Shaders/HDR/trivial-notexcoord.vert + Shaders/HDR/histogram-column.frag + Shaders/HDR/histogram-include.frag + + + hdr_tex + sampler-2d + 0 + + + + diff --git a/Effects/HDR/adapt-luminance.eff b/Effects/HDR/histogram-luminance.eff similarity index 66% rename from Effects/HDR/adapt-luminance.eff rename to Effects/HDR/histogram-luminance.eff index e1ecc952e..68824c187 100644 --- a/Effects/HDR/adapt-luminance.eff +++ b/Effects/HDR/histogram-luminance.eff @@ -1,19 +1,20 @@ - Effects/HDR/adapt-luminance + Effects/HDR/histogram-luminance Shaders/HDR/trivial-notexcoord.vert - Shaders/HDR/adapt-luminance.frag + Shaders/HDR/histogram-luminance.frag + Shaders/HDR/histogram-include.frag - prev_lum_tex + histogram_tex sampler-2d 0 - current_lum_tex + prev_lum_tex sampler-2d 1 diff --git a/Effects/HDR/lighting.eff b/Effects/HDR/lighting.eff index a60b9d152..e93a4b24b 100644 --- a/Effects/HDR/lighting.eff +++ b/Effects/HDR/lighting.eff @@ -41,6 +41,7 @@ Shaders/HDR/shadows-include.frag Shaders/HDR/lighting-include.frag Shaders/HDR/aerial-perspective-include.frag + Shaders/HDR/clustered-include.frag gbuffer0_tex diff --git a/Effects/cloud-impostor.eff b/Effects/cloud-impostor.eff index 2f6a36f54..37b526d98 100644 --- a/Effects/cloud-impostor.eff +++ b/Effects/cloud-impostor.eff @@ -234,4 +234,50 @@ true + + + hdr-geometry + + + hdr-forward + + + + gequal + 1.0 + 0.0 + false + + + 0 + 2d + texture[0]/image + clamp-to-border + clamp-to-border + + 1 + transparent + + Shaders/HDR/cloud-static.vert + Shaders/HDR/aerial-perspective-include.frag + Shaders/HDR/3dcloud.frag + + + baseTexture + sampler-2d + 0 + + + + aerial_perspective_lut + sampler-2d + 11 + + + transmittance_lut + sampler-2d + 12 + + + diff --git a/Effects/cloud-noctilucent.eff b/Effects/cloud-noctilucent.eff index 116135a0e..ab049370f 100644 --- a/Effects/cloud-noctilucent.eff +++ b/Effects/cloud-noctilucent.eff @@ -154,4 +154,50 @@ true + + + hdr-geometry + + + hdr-forward + + + + gequal + 1.0 + 0.0 + false + + + 0 + 2d + texture[0]/image + clamp-to-border + clamp-to-border + + 1 + transparent + + Shaders/HDR/cloud-static.vert + Shaders/HDR/aerial-perspective-include.frag + Shaders/HDR/3dcloud.frag + + + baseTexture + sampler-2d + 0 + + + + aerial_perspective_lut + sampler-2d + 11 + + + transmittance_lut + sampler-2d + 12 + + + diff --git a/Effects/cloud-static.eff b/Effects/cloud-static.eff index 32fc81409..fcd85ec50 100644 --- a/Effects/cloud-static.eff +++ b/Effects/cloud-static.eff @@ -362,4 +362,50 @@ true + + + hdr-geometry + + + hdr-forward + + + + gequal + 1.0 + 0.0 + false + + + 0 + 2d + texture[0]/image + clamp-to-border + clamp-to-border + + 1 + transparent + + Shaders/HDR/cloud-static.vert + Shaders/HDR/aerial-perspective-include.frag + Shaders/HDR/3dcloud.frag + + + baseTexture + sampler-2d + 0 + + + + aerial_perspective_lut + sampler-2d + 11 + + + transmittance_lut + sampler-2d + 12 + + + diff --git a/Effects/cloud.eff b/Effects/cloud.eff index 33c0842dc..1cd17492e 100644 --- a/Effects/cloud.eff +++ b/Effects/cloud.eff @@ -475,4 +475,68 @@ + + + hdr-geometry + + + hdr-forward + + + + gequal + 1.0 + 0.0 + false + + + 0 + 2d + texture[0]/image + clamp-to-border + clamp-to-border + + 1 + transparent + + Shaders/HDR/3dcloud.vert + Shaders/HDR/aerial-perspective-include.frag + Shaders/HDR/3dcloud.frag + + usrAttr1 + 10 + + + usrAttr2 + 11 + + + + baseTexture + sampler-2d + 0 + + + range + float + range + + + detail_range + float + detail + + + + aerial_perspective_lut + sampler-2d + 11 + + + transmittance_lut + sampler-2d + 12 + + + diff --git a/Effects/lfeat.eff b/Effects/lfeat.eff index 375ef7daf..39655579e 100644 --- a/Effects/lfeat.eff +++ b/Effects/lfeat.eff @@ -93,4 +93,46 @@ + + + hdr-geometry + + + + gequal + 1.0 + 0.0 + + + always + 9 + replace + + + 0 + texture[0]/type + texture[0]/image + texture[0]/filter + texture[0]/wrap-s + texture[0]/wrap-t + + 0 + opaque + back + + Shaders/HDR/geometry-lfeat.vert + Shaders/HDR/geometry-lfeat.frag + Shaders/HDR/gbuffer-include.frag + + + color_tex + sampler-2d + 0 + + + 2.0 + 1.0 + + + diff --git a/Effects/model-combined-transparent.eff b/Effects/model-combined-transparent.eff index cde96d51f..06d55d2d3 100644 --- a/Effects/model-combined-transparent.eff +++ b/Effects/model-combined-transparent.eff @@ -5,5 +5,5 @@ It's kept for backwards compatibility and should not be used on new projects. --> Effects/model-combined-transparent - Effects/model-combined + Effects/model-transparent diff --git a/Effects/model-default.eff b/Effects/model-default.eff index f93f37d0c..c32927a31 100644 --- a/Effects/model-default.eff +++ b/Effects/model-default.eff @@ -12,6 +12,11 @@ 0 + + 1 + RenderBin + + true /environment/ground-visibility-m /environment/visibility-m @@ -87,6 +92,9 @@ true + + write-depth + material/active material/ambient @@ -103,11 +111,10 @@ shade-model cull-face - - rendering-hint + + render-bin/bin-number + render-bin/bin-name + @@ -408,6 +415,9 @@ true + + write-depth + material/active @@ -448,9 +458,10 @@ cull-face - - rendering-hint - + + render-bin/bin-number + render-bin/bin-name + @@ -572,6 +583,9 @@ true + + write-depth + material/active @@ -612,9 +626,10 @@ cull-face - - rendering-hint - + + render-bin/bin-number + render-bin/bin-name + texture[0]/active diff --git a/Effects/model-transparent.eff b/Effects/model-transparent.eff index 29e12d1a5..0ba8554be 100644 --- a/Effects/model-transparent.eff +++ b/Effects/model-transparent.eff @@ -4,6 +4,12 @@ Effects/model-default + + 111 + DepthSortedBin + + false + Textures/PBR/dfg_lut.dds 2d diff --git a/Effects/runway.eff b/Effects/runway.eff index 81a60659e..a655b384b 100644 --- a/Effects/runway.eff +++ b/Effects/runway.eff @@ -886,4 +886,64 @@ + + + hdr-geometry + + + + gequal + 1.0 + 0.0 + + + always + 9 + replace + + + 0 + texture[0]/type + texture[0]/image + texture[0]/filter + texture[0]/wrap-s + texture[0]/wrap-t + + + 1 + texture[4]/type + texture[4]/image + texture[4]/filter + texture[4]/wrap-s + texture[4]/wrap-t + + + 2 + noise + + 0 + opaque + back + + Shaders/HDR/geometry-runway.vert + Shaders/HDR/geometry-runway.frag + Shaders/HDR/gbuffer-include.frag + + + color_tex + sampler-2d + 0 + + + normal_tex + sampler-2d + 1 + + + noise_tex + sampler-3d + 2 + + + diff --git a/Effects/terrain-default.eff b/Effects/terrain-default.eff index e9faf666c..5b4354c18 100644 --- a/Effects/terrain-default.eff +++ b/Effects/terrain-default.eff @@ -1669,7 +1669,6 @@ - hdr-geometry @@ -1696,8 +1695,8 @@ opaque back - Shaders/HDR/geometry.vert - Shaders/HDR/geometry.frag + Shaders/HDR/geometry-terrain.vert + Shaders/HDR/geometry-terrain.frag Shaders/HDR/gbuffer-include.frag @@ -1705,16 +1704,11 @@ sampler-2d 0 + - color_mode - int - 2 - - - - material_diffuse - float-vec4 - material/diffuse + orthophoto_tex + sampler-2d + 15 diff --git a/Shaders/HDR/3dcloud.frag b/Shaders/HDR/3dcloud.frag new file mode 100644 index 000000000..d3998b735 --- /dev/null +++ b/Shaders/HDR/3dcloud.frag @@ -0,0 +1,61 @@ +#version 330 core + +out vec4 fragColor; + +in vec2 texCoord; +in vec4 cloudColor; + +uniform sampler2D baseTexture; + +uniform mat4 osg_ProjectionMatrix; +uniform vec4 fg_Viewport; +uniform vec3 fg_SunDirection; + +const int STEPS = 8; + +uniform float density = 30.0; +uniform float max_sample_dist = 0.05; + +void main() +{ + vec4 base = texture(baseTexture, texCoord); + + // Directly discard fragments below a threshold + if (base.a < 0.02) + discard; + + // Pixel position in screen space [-1, 1] + vec2 screen_uv = ((gl_FragCoord.xy - fg_Viewport.xy) / fg_Viewport.zw) * 2.0 - 1.0; + + // XXX: Sun's screen-space position. This should be passed as an uniform + vec4 sun_dir_screen = osg_ProjectionMatrix * vec4(fg_SunDirection, 0.0); + sun_dir_screen.xyz /= sun_dir_screen.w; + sun_dir_screen.xyz = normalize(sun_dir_screen.xyz); + + // Direction from pixel to Sun in screen space + vec2 sun_dir = screen_uv - sun_dir_screen.xy; + // Flip the x axis + sun_dir.x = -sun_dir.x; + + float dt = max_sample_dist / STEPS; + + // 2D ray march along the Sun's direction to estimate the transmittance + float T = 1.0; + for (int i = 0; i < STEPS; ++i) { + float t = (float(i) + 0.5) * dt; + vec2 uv_t = texCoord - sun_dir * t; + vec4 texel = texture(baseTexture, uv_t); + // Beer-Lambert's law + T *= exp(-texel.a * dt * density); + } + + // When the camera is facing perpendicularly to the Sun, the Sun's + // screen-space location can tend toward infinity. Fade the effect toward + // the perpendicular. + float fade = smoothstep(0.1, 0.5, dot(vec3(0.0, 0.0, -1.0), fg_SunDirection)); + + vec4 color = base * cloudColor; + color.rgb *= base.a * mix(1.0, T, fade); + + fragColor = color; +} diff --git a/Shaders/HDR/3dcloud.vert b/Shaders/HDR/3dcloud.vert new file mode 100644 index 000000000..82bc0a8b2 --- /dev/null +++ b/Shaders/HDR/3dcloud.vert @@ -0,0 +1,116 @@ +#version 330 core + +layout(location = 0) in vec4 pos; +layout(location = 2) in vec4 vertexColor; +layout(location = 3) in vec4 multiTexCoord0; +layout(location = 10) in vec4 usrAttr1; +layout(location = 11) in vec4 usrAttr2; + +out vec2 texCoord; +out vec4 cloudColor; + +uniform float range; +uniform float detail_range; + +uniform mat4 osg_ModelViewMatrix; +uniform mat4 osg_ModelViewProjectionMatrix; +uniform mat4 osg_ViewMatrixInverse; +uniform vec3 fg_SunDirectionWorld; + +// aerial-perspective-include.frag +vec3 add_aerial_perspective(vec3 color, vec2 coord, float depth); +vec3 get_sun_radiance(vec3 p); + +void main() +{ + float alpha_factor = usrAttr1.r; + float shade_factor = usrAttr1.g; + float cloud_height = usrAttr1.b; + float bottom_factor = usrAttr2.r; + float middle_factor = usrAttr2.g; + float top_factor = usrAttr2.b; + + texCoord = multiTexCoord0.st; + + // XXX: Should be sent as an uniform + mat4 inverseModelViewMatrix = inverse(osg_ModelViewMatrix); + + vec4 ep = inverseModelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0); + vec4 l = inverseModelViewMatrix * vec4(0.0, 0.0, 1.0, 1.0); + vec3 u = normalize(ep.xyz - l.xyz); + + // Find a rotation matrix that rotates 1,0,0 into u. u, r and w are + // the columns of that matrix. + vec3 absu = abs(u); + vec3 r = normalize(vec3(-u.y, u.x, 0.0)); + vec3 w = cross(u, r); + + // Do the matrix multiplication by [ u r w pos]. Assume no + // scaling in the homogeneous component of pos. + vec4 final_pos = vec4(0.0, 0.0, 0.0, 1.0); + final_pos.xyz = pos.x * u; + final_pos.xyz += pos.y * r; + final_pos.xyz += pos.z * w; + // Apply Z scaling to allow sprites to be squashed in the z-axis + final_pos.z = final_pos.z * vertexColor.w; + + // Now shift the sprite to the correct position in the cloud. + final_pos.xyz += vertexColor.xyz; + + // Determine the position - used for fog and shading calculations + float fogCoord = length(vec3(osg_ModelViewMatrix * vec4(vertexColor.xyz, 1.0))); + float center_dist = length(vec3(osg_ModelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0))); + + if ((fogCoord > detail_range) && (fogCoord > center_dist) && (shade_factor < 0.7)) { + // More than detail_range away, so discard all sprites on opposite side of + // cloud center by shifting them beyond the view fustrum + gl_Position = vec4(0.0, 0.0, 10.0, 1.0); + cloudColor = vec4(0.0); + } else { + gl_Position = osg_ModelViewProjectionMatrix * final_pos; + + vec4 final_view_pos = osg_ModelViewMatrix * final_pos; + vec4 final_world_pos = osg_ViewMatrixInverse * final_view_pos; + + // Determine a lighting normal based on the vertex position from the + // center of the cloud, so that sprite on the opposite side of the cloud + // to the sun are darker. + vec3 n = normalize(vec3(osg_ViewMatrixInverse * + osg_ModelViewMatrix * vec4(-final_pos.xyz, 0.0))); + float NdotL = dot(-fg_SunDirectionWorld, n); + + // Determine the shading of the vertex. We shade it based on it's position + // in the cloud relative to the sun, and it's vertical position in the cloud. + float shade = mix(shade_factor, top_factor, smoothstep(-0.3, 0.3, NdotL)); + + if (final_pos.z < 0.5 * cloud_height) { + shade = min(shade, mix(bottom_factor, middle_factor, + final_pos.z * 2.0 / cloud_height)); + } else { + shade = min(shade, mix(middle_factor, top_factor, + final_pos.z * 2.0 / cloud_height - 1.0)); + } + + cloudColor.rgb = shade * get_sun_radiance(final_world_pos.xyz); + + // Perspective division and scale to [0, 1] to get the screen position + // of the vertex. + vec2 coord = (gl_Position.xy / gl_Position.w) * 0.5 + 0.5; + cloudColor.rgb = add_aerial_perspective( + cloudColor.rgb, coord, length(final_view_pos)); + + if ((fogCoord > (0.9 * detail_range)) + && (fogCoord > center_dist) + && (shade_factor < 0.7)) { + // cloudlet is almost at the detail range, so fade it out. + cloudColor.a = 1.0 - smoothstep(0.9 * detail_range, detail_range, fogCoord); + } else { + // As we get within 100m of the sprite, it is faded out. + // Equally at large distances it also fades out. + cloudColor.a = min(smoothstep(10.0, 100.0, fogCoord), + 1.0 - smoothstep(0.9 * range, range, fogCoord)); + } + + cloudColor.a *= alpha_factor; + } +} diff --git a/Shaders/HDR/adapt-luminance.frag b/Shaders/HDR/adapt-luminance.frag deleted file mode 100644 index e7cfa438b..000000000 --- a/Shaders/HDR/adapt-luminance.frag +++ /dev/null @@ -1,19 +0,0 @@ -#version 330 core - -out float adaptedLum; - -uniform sampler2D prev_lum_tex; -uniform sampler2D current_lum_tex; - -uniform float osg_DeltaFrameTime; - -// Higher values give faster eye adaptation times -const float TAU = 4.0; - -void main() -{ - float prevLum = texelFetch(prev_lum_tex, ivec2(0), 0).r; - float currentLum = exp(textureLod(current_lum_tex, vec2(0.5), 10.0).r); - adaptedLum = prevLum + (currentLum - prevLum) * - (1.0 - exp(-osg_DeltaFrameTime * TAU)); -} diff --git a/Shaders/HDR/aerial-perspective-include.frag b/Shaders/HDR/aerial-perspective-include.frag index 13062e63d..a56a0983f 100644 --- a/Shaders/HDR/aerial-perspective-include.frag +++ b/Shaders/HDR/aerial-perspective-include.frag @@ -3,71 +3,91 @@ uniform sampler2D aerial_perspective_lut; uniform sampler2D transmittance_lut; -uniform float fg_SunZenithCosTheta; +uniform vec3 fg_SunDirectionWorld; uniform float fg_CameraDistanceToEarthCenter; +uniform float fg_SunZenithCosTheta; uniform float fg_EarthRadius; -const float AERIAL_SLICES = 32.0; -const float AERIAL_LUT_TILE_SIZE = 1.0 / AERIAL_SLICES; -const float AERIAL_LUT_TEXEL_SIZE = 1.0 / 1024.0; -const float AERIAL_MAX_DEPTH = 128000.0; -const vec3 EXTRATERRESTRIAL_SOLAR_ILLUMINANCE = vec3(128.0); +const float AP_SLICE_COUNT = 32.0; +const float AP_MAX_DEPTH = 128000.0; +const float AP_SLICE_WIDTH_PIXELS = 32.0; +const float AP_SLICE_SIZE = 1.0 / AP_SLICE_COUNT; +const float AP_TEXEL_WIDTH = 1.0 / (AP_SLICE_COUNT * AP_SLICE_WIDTH_PIXELS); const float ATMOSPHERE_RADIUS = 6471e3; -vec4 sampleAerialPerspectiveSlice(vec2 coord, int slice) +//-- BEGIN spectral include + +// Extraterrestial Solar Irradiance Spectra, units W * m^-2 * nm^-1 +// https://www.nrel.gov/grid/solar-resource/spectra.html +const vec4 sun_spectral_irradiance = vec4(1.679, 1.828, 1.986, 1.307); + +const mat4x3 M = mat4x3( + 137.672389239975, -8.632904716299537, -1.7181567391931372, + 32.549094028629234, 91.29801417199785, -12.005406444382531, + -38.91428392614275, 34.31665471469816, 29.89044807197628, + 8.572844237945445, -11.103384660054624, 117.47585277566478 + ); + +vec3 linear_srgb_from_spectral_samples(vec4 L) { - // Sample at the pixel center - float offset = slice * AERIAL_LUT_TILE_SIZE + AERIAL_LUT_TEXEL_SIZE * 0.5; - float x = coord.x * (AERIAL_LUT_TILE_SIZE - AERIAL_LUT_TEXEL_SIZE) + offset; - return texture(aerial_perspective_lut, vec2(x, coord.y)); + return M * L; } -vec4 sampleAerialPerspective(vec2 coord, float depth) +//-- END spectral include + +vec4 sample_aerial_perspective_slice(sampler2D lut, vec2 coord, float slice) +{ + // Sample at the pixel center + float offset = slice * AP_SLICE_SIZE + AP_TEXEL_WIDTH * 0.5; + float x = coord.x * (AP_SLICE_SIZE - AP_TEXEL_WIDTH) + offset; + return texture(lut, vec2(x, coord.y)); +} + +vec4 sample_aerial_perspective(sampler2D lut, vec2 coord, float depth) { vec4 color; - // Map to [0,1] - float w = depth / AERIAL_MAX_DEPTH; - // Squared distribution - w = sqrt(clamp(w, 0.0, 1.0)); - w *= AERIAL_SLICES; - if (w <= 1.0) { + float w = sqrt(clamp(depth / AP_MAX_DEPTH, 0.0, 1.0)); + float x = w * AP_SLICE_COUNT; + if (x <= 1.0) { // Handle special case of fragments behind the first slice color = mix(vec4(0.0, 0.0, 0.0, 1.0), - sampleAerialPerspectiveSlice(coord, 0), - w); + sample_aerial_perspective_slice(lut, coord, 0), + x); } else { - w -= 1.0; // Manually interpolate between slices - color = mix(sampleAerialPerspectiveSlice(coord, int(floor(w))), - sampleAerialPerspectiveSlice(coord, int(ceil(w))), - sqrt(fract(w))); + x -= 1.0; + color = mix(sample_aerial_perspective_slice(lut, coord, floor(x)), + sample_aerial_perspective_slice(lut, coord, ceil(x)), + fract(x)); } return color; } -vec3 addAerialPerspective(vec3 color, vec2 coord, float depth) +vec3 add_aerial_perspective(vec3 color, vec2 coord, float depth) { - vec4 aerialPerspective = sampleAerialPerspective(coord, depth); - return color * aerialPerspective.a + aerialPerspective.rgb - * EXTRATERRESTRIAL_SOLAR_ILLUMINANCE; + vec4 ap = sample_aerial_perspective(aerial_perspective_lut, coord, depth); + return color * ap.a + ap.rgb; } -/** - * Get the illuminance of the Sun for a surface perpendicular to the Sun - * direction. The illuminance is calculated at the altitude of the viewer, - * which might or might not be correct in certain circumstances. If the object - * being illuminated is not too far from the viewer it's a good enough - * approximation. +/* + * Get the Sun radiance at a point 'p' in world space. + * We cannot use the Sun extraterrestial irradiance directly because it will be + * attenuated by the transmittance of the atmospheric medium. */ -vec3 getSunIntensity() +vec3 get_sun_radiance(vec3 p) { - float normalizedHeight = (fg_CameraDistanceToEarthCenter - fg_EarthRadius) + float distance_to_earth_center = length(p); + float normalized_altitude = (distance_to_earth_center - fg_EarthRadius) / (ATMOSPHERE_RADIUS - fg_EarthRadius); - vec2 coord = vec2(fg_SunZenithCosTheta * 0.5 + 0.5, - clamp(normalizedHeight, 0.0, 1.0)); - vec3 transmittance = texture(transmittance_lut, coord).rgb; + vec3 zenith_dir = p / distance_to_earth_center; + float sun_cos_theta = dot(zenith_dir, fg_SunDirectionWorld); - return EXTRATERRESTRIAL_SOLAR_ILLUMINANCE * transmittance; + float u = sun_cos_theta * 0.5 + 0.5; + float v = clamp(normalized_altitude, 0.0, 1.0); + vec4 transmittance = texture(transmittance_lut, vec2(u, v)); + + vec4 L = sun_spectral_irradiance * transmittance; + return linear_srgb_from_spectral_samples(L); } diff --git a/Shaders/HDR/atmos-aerial-perspective.frag b/Shaders/HDR/atmos-aerial-perspective.frag index 0b0bf283a..93f7d3427 100644 --- a/Shaders/HDR/atmos-aerial-perspective.frag +++ b/Shaders/HDR/atmos-aerial-perspective.frag @@ -1,14 +1,11 @@ -// An implementation of Sébastien Hillaire's "A Scalable and Production Ready -// Sky and Atmosphere Rendering Technique". +// Render the aerial perspective LUT, similar to +// "A Scalable and Production Ready Sky and Atmosphere Rendering Technique" +// by Sébastien Hillaire (2020). // -// This shader generates the aerial perspective LUT. This LUT is used by opaque -// and transparent objects to apply atmospheric scattering. In-scattering is -// stored in the RGB channels, while transmittance is stored in the alpha -// channel. // Unlike the paper, we are using a tiled 2D texture instead of a true 3D -// texture. For some reason the overhead of rendering to a texture a lot of -// times (the depth of the 3D texture) seems to be too high, probably because -// OSG is not sharing state between those passes. +// texture. For some reason the overhead of rendering to a texture many times +// (the depth of the 3D texture) seems to be too high, probably because OSG is +// not sharing state between those passes. #version 330 core @@ -17,112 +14,73 @@ out vec4 fragColor; in vec2 texCoord; uniform sampler2D transmittance_lut; -uniform sampler2D multiscattering_lut; uniform mat4 fg_ViewMatrixInverse; uniform vec3 fg_CameraPositionCart; uniform vec3 fg_SunDirectionWorld; -uniform float fg_SunZenithCosTheta; -uniform float fg_CameraDistanceToEarthCenter; -uniform float fg_EarthRadius; -const float PI = 3.141592653; -const float ATMOSPHERE_RADIUS = 6471e3; -const float TOTAL_SLICES = 32.0; -const float DEPTH_RANGE = 128000.0; -const int AERIAL_PERSPECTIVE_SAMPLES = 20; -const vec3 ONE_OVER_THREE = vec3(1.0 / 3.0); +const float AP_SLICE_COUNT = 32.0; +const float AP_MAX_DEPTH = 128000.0; +const int AERIAL_PERSPECTIVE_STEPS = 20; +// gbuffer-include.frag vec3 positionFromDepth(vec2 pos, float depth); -float raySphereIntersection(vec3 ro, vec3 rd, float radius); -vec3 sampleMedium(in float height, - out float mieScattering, out float mieAbsorption, - out vec3 rayleighScattering, out vec3 ozoneAbsorption); -float miePhaseFunction(float cosTheta); -float rayleighPhaseFunction(float cosTheta); -vec3 getValueFromLUT(sampler2D lut, float sunCosTheta, float normalizedHeight); +// atmos-include.frag +vec4 compute_inscattering(in vec3 ray_origin, + in vec3 ray_dir, + in float t_max, + in vec3 sun_dir, + in int steps, + in sampler2D transmittance_lut, + out vec4 transmittance); + +//-- BEGIN spectral include + +// Extraterrestial Solar Irradiance Spectra, units W * m^-2 * nm^-1 +// https://www.nrel.gov/grid/solar-resource/spectra.html +const vec4 sun_spectral_irradiance = vec4(1.679, 1.828, 1.986, 1.307); + +const mat4x3 M = mat4x3( + 137.672389239975, -8.632904716299537, -1.7181567391931372, + 32.549094028629234, 91.29801417199785, -12.005406444382531, + -38.91428392614275, 34.31665471469816, 29.89044807197628, + 8.572844237945445, -11.103384660054624, 117.47585277566478 + ); + +vec3 linear_srgb_from_spectral_samples(vec4 L) +{ + return M * L; +} + +//-- END spectral include + void main() { - // Account for the depth layer we are currently in - float x = texCoord.x * TOTAL_SLICES; + // Account for the depth slice we are currently in. Depth goes from 0 to + // DEPTH_RANGE in a squared distribution. The first slice is not 0 since + // that would waste a slice. + float x = texCoord.x * AP_SLICE_COUNT; + float slice = ceil(x); + float w = slice / AP_SLICE_COUNT; // [0,1] + float depth = w*w * AP_MAX_DEPTH; + vec2 coord = vec2(fract(x), texCoord.y); - // Depth goes from the 0 to DEPTH_RANGE in a squared distribution. - // The first slice is not at 0 since that would waste a slice. - float w = ceil(x) / TOTAL_SLICES; - w *= w; - float depth = w * DEPTH_RANGE; - vec3 fragPos = positionFromDepth(coord, 1.0); - vec3 rayDir = vec4(fg_ViewMatrixInverse * vec4(normalize(fragPos), 0.0)).xyz; + vec3 frag_pos = positionFromDepth(coord, 1.0); + vec3 ray_dir = vec4(fg_ViewMatrixInverse * vec4(normalize(frag_pos), 0.0)).xyz; - vec3 rayOrigin = fg_CameraPositionCart; - - // Handle the camera being underground - float earthRadius = min(fg_EarthRadius, fg_CameraDistanceToEarthCenter); - - float atmosDist = raySphereIntersection(rayOrigin, rayDir, ATMOSPHERE_RADIUS); - float groundDist = raySphereIntersection(rayOrigin, rayDir, earthRadius); - - float tmax; - if (fg_CameraDistanceToEarthCenter < ATMOSPHERE_RADIUS) { - // We are inside the atmosphere - if (groundDist < 0.0) { - // No ground collision, use the distance to the outer atmosphere - tmax = atmosDist; - } else { - // Use the distance to the ground - tmax = groundDist; - } - } else { - // We are in outer space, skip - fragColor = vec4(0.0, 0.0, 0.0, 1.0); - return; - } - // Clip the max distance to the depth of this slice - tmax = min(tmax, depth); - - float cosTheta = dot(rayDir, fg_SunDirectionWorld); - float miePhase = miePhaseFunction(cosTheta); - float rayleighPhase = rayleighPhaseFunction(cosTheta); - - vec3 L = vec3(0.0); - vec3 throughput = vec3(1.0); - float t = 0.0; - - for (int i = 0; i < AERIAL_PERSPECTIVE_SAMPLES; ++i) { - float newT = ((float(i) + 0.3) / AERIAL_PERSPECTIVE_SAMPLES) * tmax; - float dt = newT - t; - t = newT; - - vec3 samplePos = rayOrigin + rayDir * t; - float height = length(samplePos) - fg_EarthRadius; - float normalizedHeight = height / (ATMOSPHERE_RADIUS - fg_EarthRadius); - - float mieScattering, mieAbsorption; - vec3 rayleighScattering, ozoneAbsorption; - vec3 extinction = sampleMedium(height, mieScattering, mieAbsorption, - rayleighScattering, ozoneAbsorption); - - vec3 sampleTransmittance = exp(-dt*extinction); - - vec3 sunTransmittance = getValueFromLUT( - transmittance_lut, fg_SunZenithCosTheta, normalizedHeight); - vec3 multiscattering = getValueFromLUT( - multiscattering_lut, fg_SunZenithCosTheta, normalizedHeight); - - vec3 S = - rayleighScattering * (rayleighPhase * sunTransmittance + multiscattering) + - mieScattering * (miePhase * sunTransmittance + multiscattering); - - vec3 Sint = (S - S * sampleTransmittance) / extinction; - L += throughput * Sint; - throughput *= sampleTransmittance; - } - - // Instead of storing an entire vec3, store the mean of its components - float transmittance = dot(throughput, ONE_OVER_THREE); - - fragColor = vec4(L, transmittance); + vec4 transmittance; + vec4 L = compute_inscattering(fg_CameraPositionCart, + ray_dir, + depth, + fg_SunDirectionWorld, + AERIAL_PERSPECTIVE_STEPS, + transmittance_lut, + transmittance); + // In-scattering + fragColor.rgb = linear_srgb_from_spectral_samples(L * sun_spectral_irradiance); + // Transmittance + fragColor.a = dot(transmittance, vec4(0.25)); } diff --git a/Shaders/HDR/atmos-include.frag b/Shaders/HDR/atmos-include.frag index d8c435543..991f9a4fe 100644 --- a/Shaders/HDR/atmos-include.frag +++ b/Shaders/HDR/atmos-include.frag @@ -1,28 +1,99 @@ -// An implementation of Sébastien Hillaire's "A Scalable and Production Ready -// Sky and Atmosphere Rendering Technique". - #version 330 core -const float PI = 3.141592653; - -// Atmosphere parameters -// Section 2.1 of [Bruneton08], units are inverse meters -const float mie_density_height_scale = 8.33333e-4; // Hm=1.2km -const float rayleigh_density_height_scale = 1.25e-4; // Hr=8km -const float mie_scattering = 3.996e-6; -const float mie_absorption = 4.4e-6; -const vec3 rayleigh_scattering = vec3(5.802, 13.558, 33.1) * vec3(1e-6); -const vec3 ozone_absorption = vec3(0.650, 1.881, 0.085) * vec3(1e-6); - +const float PI = 3.14159265358979323846; +const float INV_PI = 0.31830988618379067154; +const float INV_4PI = 0.25 * INV_PI; +const float PHASE_ISOTROPIC = INV_4PI; +const float RAYLEIGH_PHASE_SCALE = (3.0 / 16.0) * INV_PI; const float g = 0.8; const float gg = g*g; -const float mie_phase_scale = 3.0/(8.0*PI); -const float rayleigh_phase_scale = 3.0/(16.0*PI); -// Returns the distance between ro and the first intersection with the sphere -// or -1.0 if there is no intersection. -// -1.0 is also returned if the ray is pointing away from the sphere. -float raySphereIntersection(vec3 ro, vec3 rd, float radius) +const float ATMOSPHERE_RADIUS = 6471e3; + +// Rayleigh scattering coefficient at sea level, units m^-1 +// "Rayleigh-scattering calculations for the terrestrial atmosphere" +// by Anthony Bucholtz (1995). +const vec4 molecular_scattering_coefficient_base = + vec4(6.605e-6, 1.067e-5, 1.842e-5, 3.156e-5); + +// Ozone absorption cross section, units m^2 / molecules +// "High spectral resolution ozone absorption cross-sections" +// by V. Gorshelev et al. (2014). +const vec4 ozone_cross_section = + vec4(3.472e-21, 3.914e-21, 1.349e-21, 11.03e-23) * 1e-4f; + +const float ozone_mean_monthly_dobson[] = float[]( + 347.0, // January + 370.0, // February + 381.0, // March + 384.0, // April + 372.0, // May + 352.0, // June + 333.0, // July + 317.0, // August + 298.0, // September + 285.0, // October + 290.0, // November + 315.0 // December + ); +const float ozone_height_distribution[] = float[]( + 9.0 / 210.0, + 14.0 / 210.0, + 111.0 / 210.0, + 64.0 / 210.0, + 6.0 / 210.0, + 6.0 / 210.0, + 0.0 + ); + +/* + * Every aerosol type expects 5 parameters: + * - Scattering cross section + * - Absorption cross section + * - Base density (km^-3) + * - Background density (km^-3) + * - Height scaling parameter + * These parameters can be sent as uniforms. + * + * This model for aerosols and their corresponding parameters come from + * "A Physically-Based Spatio-Temporal Sky Model" + * by Guimera et al. (2018). + */ +// Urban +uniform vec4 aerosol_absorption_cross_section = + vec4(2.8722e-24, 4.6168e-24, 7.9706e-24, 1.3578e-23); +uniform vec4 aerosol_scattering_cross_section = + vec4(1.5908e-22, 1.7711e-22, 2.0942e-22, 2.4033e-22); +uniform float aerosol_base_density = 1.3681e20; +uniform float aerosol_relative_background_density = 2e6 / 1.3681e20; +uniform float aerosol_height_scale = 0.73; + +uniform float aerosol_turbidity = 1.0; + +uniform int month_of_the_year = 0; +uniform vec4 ground_albedo = vec4(0.3); + +uniform float fg_EarthRadius; + +//------------------------------------------------------------------------------ + +/* + * Helper function to obtain the transmittance to the top of the atmosphere + * from Buffer A. + */ +vec4 transmittance_from_lut(sampler2D lut, float cos_theta, float normalized_altitude) +{ + float u = clamp(cos_theta * 0.5 + 0.5, 0.0, 1.0); + float v = clamp(normalized_altitude, 0.0, 1.0); + return texture(lut, vec2(u, v)); +} + +/* + * Returns the distance between ro and the first intersection with the sphere + * or -1.0 if there is no intersection. The sphere's origin is (0,0,0). + * -1.0 is also returned if the ray is pointing away from the sphere. + */ +float ray_sphere_intersection(vec3 ro, vec3 rd, float radius) { float b = dot(ro, rd); float c = dot(ro, ro) - radius*radius; @@ -33,46 +104,205 @@ float raySphereIntersection(vec3 ro, vec3 rd, float radius) return (-b-sqrt(d)); } -// Sample the sky medium properties at a height in meters, 0 being the ground -// and ~100km being the top of the atmosphere. -// Returns the total atmospheric extinction. -vec3 sampleMedium(in float height, - out float mieScattering, out float mieAbsorption, - out vec3 rayleighScattering, out vec3 ozoneAbsorption) +/* + * Rayleigh phase function. + */ +float molecular_phase_function(float cos_theta) { - float densityMie = exp(-mie_density_height_scale * height); - float densityRayleigh = exp(-rayleigh_density_height_scale * height); - float densityOzone = max(0.0, 1.0 - abs(height-25.0e3)/15.0e3); - - mieScattering = mie_scattering * densityMie; - mieAbsorption = mie_absorption * densityMie; - - rayleighScattering = rayleigh_scattering * densityRayleigh; - - ozoneAbsorption = ozone_absorption * densityOzone; - - return mieScattering + mieAbsorption + rayleighScattering + ozoneAbsorption; + return RAYLEIGH_PHASE_SCALE * (1.0 + cos_theta*cos_theta); } -// Approximation of the Mie phase function with the Cornette-Shanks phase function -float miePhaseFunction(float cosTheta) +/* + * Henyey-Greenstrein phase function. + */ +float aerosol_phase_function(float cos_theta) { - float num = (1.0 - gg) * (1.0 + cosTheta*cosTheta); - float den = (2.0 + gg) * pow((1.0 + gg - 2.0 * g * cosTheta), 1.5); - return mie_phase_scale * num / den; + float den = 1.0 + gg + 2.0 * g * cos_theta; + return INV_4PI * (1.0 - gg) / (den * sqrt(den)); } -float rayleighPhaseFunction(float cosTheta) +/* + * Get the approximated multiple scattering contribution for a given point + * within the atmosphere. + */ +vec4 get_multiple_scattering(sampler2D transmittance_lut, + float cos_theta, + float normalized_height, + float d) { - return rayleigh_phase_scale * (1.0 + cosTheta*cosTheta); + // Solid angle subtended by the planet from a point at d distance + // from the planet center. + float omega = 2.0 * PI * (1.0 - sqrt(d*d - fg_EarthRadius*fg_EarthRadius) / d); + omega = max(0.0, omega); + + vec4 T_to_ground = transmittance_from_lut(transmittance_lut, cos_theta, 0.0); + + vec4 T_ground_to_sample = + transmittance_from_lut(transmittance_lut, 1.0, 0.0) / + transmittance_from_lut(transmittance_lut, 1.0, normalized_height); + + // 2nd order scattering from the ground + vec4 L_ground = PHASE_ISOTROPIC * omega * (ground_albedo * INV_PI) + * T_to_ground * T_ground_to_sample * max(0.0, cos_theta); + + // Fit of Earth's multiple scattering coming from other points in the atmosphere + vec4 L_ms = 0.02 * vec4(0.217, 0.347, 0.594, 1.0) + * (1.0 / (1.0 + 5.0 * exp(-17.92 * cos_theta))); + + return L_ms + L_ground; } -// Sample one of the LUTs (transmittance or multiple scattering) for a given -// normalized height inside the atmosphere [0,1] and the cosine of the Sun -// zenith angle. -vec3 getValueFromLUT(sampler2D lut, float sunCosTheta, float normHeight) +/* + * Return the molecular volume scattering coefficient (m^-1) for a given altitude + * in kilometers. + */ +vec4 get_molecular_scattering_coefficient(float h) { - float x = clamp(sunCosTheta * 0.5 + 0.5, 0.0, 1.0); - float y = clamp(normHeight, 0.0, 1.0); - return texture(lut, vec2(x, y)).rgb; + return molecular_scattering_coefficient_base + * exp(-0.07771971 * pow(h, 1.16364243)); +} + +/* + * Return the molecular volume absorption coefficient (km^-1) for a given altitude + * in kilometers. + */ +vec4 get_molecular_absorption_coefficient(float h) +{ + int i = int(clamp(h / 9.0, 0.0, 6.0)); + float density = ozone_height_distribution[i] * + ozone_mean_monthly_dobson[month_of_the_year] * 2.6867e20f; // molecules / m^2 + density /= 9e3; // m^-3 + return ozone_cross_section * density; // m^-1 +} + +/* + * Return the aerosol density for a given altitude in kilometers. + */ +float get_aerosol_density(float h) +{ + return aerosol_base_density * (exp(-h / aerosol_height_scale) + + aerosol_relative_background_density); +} + +/* + * Get the collision coefficients (scattering and absorption) of the + * atmospheric medium for a given point at an altitude h. + */ +void get_atmosphere_collision_coefficients(in float h, + out vec4 aerosol_absorption, + out vec4 aerosol_scattering, + out vec4 molecular_absorption, + out vec4 molecular_scattering, + out vec4 extinction) +{ + h = max(h, 0.0); // In case height is negative + float aerosol_density = get_aerosol_density(h * 1e-3) * aerosol_turbidity; + aerosol_absorption = aerosol_absorption_cross_section * aerosol_density * 1e-3; + aerosol_scattering = aerosol_scattering_cross_section * aerosol_density * 1e-3; + molecular_absorption = get_molecular_absorption_coefficient(h * 1e-3); + molecular_scattering = get_molecular_scattering_coefficient(h * 1e-3); + extinction = + aerosol_absorption + aerosol_scattering + + molecular_absorption + molecular_scattering; +} + +/* + * Compute the in-scattering integral of the volume rendering equation (VRE) + * + * The integral is solved numerically by ray marching. The final in-scattering + * returned by this function is a 4D vector of the spectral radiance sampled for + * the 4 wavelengths at the top of this file. To obtain an RGB triplet, the + * spectral radiance must be multiplied by the spectral irradiance of the Sun + * and converted to sRGB. + */ +vec4 compute_inscattering(in vec3 ray_origin, + in vec3 ray_dir, + in float t_max, + in vec3 sun_dir, + in int steps, + in sampler2D transmittance_lut, + out vec4 transmittance) +{ + // Any given ray inside the atmospheric medium can end in one of 3 places: + // 1. The Earth's surface. + // 2. Outer space. We define the boundary between space and the atmosphere + // at the Kármán line. + // 3. Any object within the atmosphere. + float ray_altitude = length(ray_origin); + // Handle the camera being underground + float earth_radius = min(ray_altitude, fg_EarthRadius); + float atmos_dist = ray_sphere_intersection(ray_origin, ray_dir, ATMOSPHERE_RADIUS); + float ground_dist = ray_sphere_intersection(ray_origin, ray_dir, earth_radius); + float t_d; + if (ray_altitude < ATMOSPHERE_RADIUS) { + // We are inside the atmosphere + if (ground_dist < 0.0) { + // No ground collision, use the distance to the outer atmosphere + t_d = atmos_dist; + } else { + // We have a collision with the ground, use the distance to it + t_d = ground_dist; + } + } else { + // We are in outer space + // XXX: For now this is a flight simulator, not a space simulator + transmittance = vec4(1.0); + return vec4(0.0); + } + + // Clip by the maximum distance + t_d = min(t_d, t_max); + + float cos_theta = dot(-ray_dir, sun_dir); + + float molecular_phase = molecular_phase_function(cos_theta); + float aerosol_phase = aerosol_phase_function(cos_theta); + + float dt = t_d / float(steps); + + vec4 L_inscattering = vec4(0.0); + transmittance = vec4(1.0); + + for (int i = 0; i < steps; ++i) { + float t = (float(i) + 0.5) * dt; + vec3 x_t = ray_origin + ray_dir * t; + + float distance_to_earth_center = length(x_t); + vec3 zenith_dir = x_t / distance_to_earth_center; + float altitude = distance_to_earth_center - fg_EarthRadius; + float normalized_altitude = altitude / (ATMOSPHERE_RADIUS - fg_EarthRadius); + + float sample_cos_theta = dot(zenith_dir, sun_dir); + + vec4 aerosol_absorption, aerosol_scattering; + vec4 molecular_absorption, molecular_scattering; + vec4 extinction; + get_atmosphere_collision_coefficients( + altitude, + aerosol_absorption, aerosol_scattering, + molecular_absorption, molecular_scattering, + extinction); + + vec4 transmittance_to_sun = transmittance_from_lut( + transmittance_lut, sample_cos_theta, normalized_altitude); + + vec4 ms = get_multiple_scattering( + transmittance_lut, sample_cos_theta, normalized_altitude, + distance_to_earth_center); + + vec4 S = + molecular_scattering * (molecular_phase * transmittance_to_sun + ms) + + aerosol_scattering * (aerosol_phase * transmittance_to_sun + ms); + + vec4 step_transmittance = exp(-dt * extinction); + + // Energy-conserving analytical integration + // "Physically Based Sky, Atmosphere and Cloud Rendering in Frostbite" + // by Sébastien Hillaire + vec4 S_int = (S - S * step_transmittance) / max(extinction, 1e-7); + L_inscattering += transmittance * S_int; + transmittance *= step_transmittance; + } + + return L_inscattering; } diff --git a/Shaders/HDR/atmos-multiple-scattering.frag b/Shaders/HDR/atmos-multiple-scattering.frag deleted file mode 100644 index 315e3554d..000000000 --- a/Shaders/HDR/atmos-multiple-scattering.frag +++ /dev/null @@ -1,133 +0,0 @@ -// An implementation of Sébastien Hillaire's "A Scalable and Production Ready -// Sky and Atmosphere Rendering Technique". -// -// This shader generates the multiple scattering LUT. - -#version 330 core - -out vec3 fragColor; - -in vec2 texCoord; - -uniform sampler2D transmittance_lut; - -uniform float fg_EarthRadius; - -const float PI = 3.141592653; -const float ATMOSPHERE_RADIUS = 6471e3; -const int SQRT_SAMPLES = 4; -const float INV_SAMPLES = 1.0 / float(SQRT_SAMPLES*SQRT_SAMPLES); -const int MULTIPLE_SCATTERING_SAMPLES = 20; - -const vec3 ground_albedo = vec3(0.3); - -float raySphereIntersection(vec3 ro, vec3 rd, float radius); -vec3 sampleMedium(in float height, - out float mieScattering, out float mieAbsorption, - out vec3 rayleighScattering, out vec3 ozoneAbsorption); -float miePhaseFunction(float cosTheta); -float rayleighPhaseFunction(float cosTheta); -vec3 getValueFromLUT(sampler2D lut, float sunCosTheta, float normalizedHeight); - -vec3 generateRayDir(float theta, float phi) -{ - float cosPhi = cos(phi); - float sinPhi = sin(phi); - float cosTheta = cos(theta); - float sinTheta = sin(theta); - return vec3(cosTheta * sinPhi, sinTheta * sinPhi, cosPhi); -} - -void main() -{ - float sunCosTheta = texCoord.x * 2.0 - 1.0; - vec3 sunDir = vec3(-sqrt(1.0 - sunCosTheta*sunCosTheta), 0.0, sunCosTheta); - - float altitude = mix(fg_EarthRadius, ATMOSPHERE_RADIUS, texCoord.y); - vec3 rayOrigin = vec3(0.0, 0.0, altitude); - - vec3 Ltotal = vec3(0.0); - vec3 LMStotal = vec3(0.0); - - for (int i = 0; i < SQRT_SAMPLES; ++i) { - for (int j = 0; j < SQRT_SAMPLES; ++j) { - float theta = 2.0 * PI * (float(i) + 0.5) / float(SQRT_SAMPLES); - float phi = PI * (float(j) + 0.5) / float(SQRT_SAMPLES); - vec3 rayDir = generateRayDir(theta, phi); - - float atmosDist = raySphereIntersection(rayOrigin, rayDir, ATMOSPHERE_RADIUS); - float groundDist = raySphereIntersection(rayOrigin, rayDir, fg_EarthRadius); - - float tmax; - if (groundDist < 0.0) { - // No ground collision, use the distance to the outer atmosphere - tmax = atmosDist; - } else { - // Use the distance to the ground - tmax = groundDist; - } - - float cosTheta = dot(rayDir, sunDir); - float miePhase = miePhaseFunction(cosTheta); - float rayleighPhase = rayleighPhaseFunction(-cosTheta); - - vec3 L = vec3(0.0); - vec3 LMS = vec3(0.0); - vec3 throughput = vec3(1.0); - float t = 0.0; - - for (int k = 0; k < MULTIPLE_SCATTERING_SAMPLES; ++k) { - float newT = ((float(k) + 0.3) / MULTIPLE_SCATTERING_SAMPLES) * tmax; - float dt = newT - t; - t = newT; - - vec3 samplePos = rayOrigin + rayDir * t; - float height = length(samplePos) - fg_EarthRadius; - float normalizedHeight = height / (ATMOSPHERE_RADIUS - fg_EarthRadius); - - float mieScattering, mieAbsorption; - vec3 rayleighScattering, ozoneAbsorption; - vec3 extinction = sampleMedium(height, mieScattering, mieAbsorption, - rayleighScattering, ozoneAbsorption); - - vec3 sampleTransmittance = exp(-dt*extinction); - - vec3 sunTransmittance = getValueFromLUT( - transmittance_lut, sunCosTheta, normalizedHeight); - - vec3 S = (rayleighScattering * rayleighPhase + - mieScattering * miePhase) * sunTransmittance; - - // Not using the power serie - vec3 MS = mieScattering + rayleighScattering; - vec3 MSint = (MS - MS * sampleTransmittance) / extinction; - LMS += throughput * MSint; - - vec3 Sint = (S - S * sampleTransmittance) / extinction; - L += throughput * Sint; - throughput *= sampleTransmittance; - } - - if (groundDist >= 0.0) { - // Account for bounced light off the Earth - vec3 p = rayOrigin + rayDir * groundDist; - float pHeight = length(p); - vec3 up = p / pHeight; - - float normHeight = (pHeight - fg_EarthRadius) - / (ATMOSPHERE_RADIUS - fg_EarthRadius); - float sunZenithCosTheta = dot(sunDir, up); - - vec3 transmittanceFromGround = getValueFromLUT( - transmittance_lut, sunZenithCosTheta, normHeight); - L += transmittanceFromGround * throughput - * clamp(sunZenithCosTheta, 0.0, 1.0) * ground_albedo / PI; - } - - Ltotal += L * INV_SAMPLES; - LMStotal += LMS * INV_SAMPLES; - } - } - - fragColor = Ltotal / (1.0 - LMStotal); -} diff --git a/Shaders/HDR/atmos-sky-view.frag b/Shaders/HDR/atmos-sky-view.frag index eaf13eb6e..a6d2b1d2b 100644 --- a/Shaders/HDR/atmos-sky-view.frag +++ b/Shaders/HDR/atmos-sky-view.frag @@ -1,114 +1,54 @@ -// An implementation of Sébastien Hillaire's "A Scalable and Production Ready -// Sky and Atmosphere Rendering Technique". -// -// This shader generates the sky-view texture. Since the sky generally has low -// frequency detail, it's possible to pre-compute it on a small texture and -// sample it later when rendering the skydome. This effectively bypasses the -// need for raymarching on screen-sized textures, which is specially costly on -// larger resolutions like 4K. - #version 330 core -out vec3 fragColor; +out vec4 fragColor; in vec2 texCoord; uniform sampler2D transmittance_lut; -uniform sampler2D multiscattering_lut; - uniform float fg_SunZenithCosTheta; uniform float fg_CameraDistanceToEarthCenter; -uniform float fg_EarthRadius; -const float PI = 3.141592653; +const float PI = 3.14159265358979323846; +const int SKY_STEPS = 32; -const float ATMOSPHERE_RADIUS = 6471e3; -const int SCATTERING_SAMPLES = 32; - -float raySphereIntersection(vec3 ro, vec3 rd, float radius); -vec3 sampleMedium(in float height, - out float mieScattering, out float mieAbsorption, - out vec3 rayleighScattering, out vec3 ozoneAbsorption); -float miePhaseFunction(float cosTheta); -float rayleighPhaseFunction(float cosTheta); -vec3 getValueFromLUT(sampler2D lut, float sunCosTheta, float normalizedHeight); +// atmos-include.frag +vec4 compute_inscattering(in vec3 ray_origin, + in vec3 ray_dir, + in float t_max, + in vec3 sun_dir, + in int steps, + in sampler2D transmittance_lut, + out vec4 transmittance); void main() { - // Always leave the sun right in the middle of the texture as the skydome - // model is already being rotated. - vec3 sunDir = vec3(-sqrt(1.0 - fg_SunZenithCosTheta*fg_SunZenithCosTheta), - 0.0, - fg_SunZenithCosTheta); + // Always leave the Sun right in the middle of the sky texture. + // The skydome model implemented in SimGear already takes care of rotating + // the Sun for us. + vec3 sun_dir = vec3( + -sqrt(1.0 - fg_SunZenithCosTheta*fg_SunZenithCosTheta), + 0.0, + fg_SunZenithCosTheta); float azimuth = 2.0 * PI * texCoord.x; // [0, 2pi] // Apply a non-linear transformation to the elevation to dedicate more - // texels to the horizon, which is where having more detail matters. + // texels to the horizon, where having more detail matters. float l = texCoord.y * 2.0 - 1.0; float elev = l*l * sign(l) * PI * 0.5; // [-pi/2, pi/2] - vec3 rayDir = vec3(cos(elev) * cos(azimuth), cos(elev) * sin(azimuth), sin(elev)); - vec3 rayOrigin = vec3(0.0, 0.0, fg_CameraDistanceToEarthCenter); + vec3 ray_dir = vec3(cos(elev) * cos(azimuth), + cos(elev) * sin(azimuth), + sin(elev)); - // Handle the camera being underground - float earthRadius = min(fg_EarthRadius, fg_CameraDistanceToEarthCenter); - - float atmosDist = raySphereIntersection(rayOrigin, rayDir, ATMOSPHERE_RADIUS); - float groundDist = raySphereIntersection(rayOrigin, rayDir, earthRadius); - - float tmax; - if (fg_CameraDistanceToEarthCenter < ATMOSPHERE_RADIUS) { - // We are inside the atmosphere - if (groundDist < 0.0) { - // No ground collision, use the distance to the outer atmosphere - tmax = atmosDist; - } else { - // Use the distance to the ground - tmax = groundDist; - } - } else { - // We are in outer space, skip - fragColor = vec3(0.0); - return; - } - - float cosTheta = dot(rayDir, sunDir); - float miePhase = miePhaseFunction(cosTheta); - float rayleighPhase = rayleighPhaseFunction(-cosTheta); - - vec3 L = vec3(0.0); - vec3 throughput = vec3(1.0); - float t = 0.0; - - for (int i = 0; i < SCATTERING_SAMPLES; ++i) { - float newT = ((float(i) + 0.3) / SCATTERING_SAMPLES) * tmax; - float dt = newT - t; - t = newT; - - vec3 samplePos = rayOrigin + rayDir * t; - float height = length(samplePos) - fg_EarthRadius; - float normalizedHeight = height / (ATMOSPHERE_RADIUS - fg_EarthRadius); - - float mieScattering, mieAbsorption; - vec3 rayleighScattering, ozoneAbsorption; - vec3 extinction = sampleMedium(height, mieScattering, mieAbsorption, - rayleighScattering, ozoneAbsorption); - - vec3 sampleTransmittance = exp(-dt*extinction); - - vec3 sunTransmittance = getValueFromLUT( - transmittance_lut, fg_SunZenithCosTheta, normalizedHeight); - vec3 multiscattering = getValueFromLUT( - multiscattering_lut, fg_SunZenithCosTheta, normalizedHeight); - - vec3 S = - rayleighScattering * (rayleighPhase * sunTransmittance + multiscattering) + - mieScattering * (miePhase * sunTransmittance + multiscattering); - - vec3 Sint = (S - S * sampleTransmittance) / extinction; - L += throughput * Sint; - throughput *= sampleTransmittance; - } + vec3 ray_origin = vec3(0.0, 0.0, fg_CameraDistanceToEarthCenter); + vec4 transmittance; + vec4 L = compute_inscattering(ray_origin, + ray_dir, + 1e7, + sun_dir, + SKY_STEPS, + transmittance_lut, + transmittance); fragColor = L; } diff --git a/Shaders/HDR/atmos-transmittance.frag b/Shaders/HDR/atmos-transmittance.frag index 335209e44..dee2b22a1 100644 --- a/Shaders/HDR/atmos-transmittance.frag +++ b/Shaders/HDR/atmos-transmittance.frag @@ -1,13 +1,6 @@ -// An implementation of Sébastien Hillaire's "A Scalable and Production Ready -// Sky and Atmosphere Rendering Technique". -// -// This shader generates the transmittance LUT. It stores the transmittance to -// the Sun through the atmosphere for a given Sun zenith angle and a height -// inside the atmosphere (0 being the ground). - #version 330 core -out vec3 fragColor; +out vec4 fragColor; in vec2 texCoord; @@ -16,38 +9,46 @@ uniform float fg_EarthRadius; const float ATMOSPHERE_RADIUS = 6471e3; const int TRANSMITTANCE_STEPS = 40; -float raySphereIntersection(vec3 ro, vec3 rd, float radius); -vec3 sampleMedium(in float height, - out float mieScattering, out float mieAbsorption, - out vec3 rayleighScattering, out vec3 ozoneAbsorption); +// atmos-include.frag +float ray_sphere_intersection(vec3 ro, vec3 rd, float radius); +void get_atmosphere_collision_coefficients(in float h, + out vec4 aerosol_absorption, + out vec4 aerosol_scattering, + out vec4 molecular_absorption, + out vec4 molecular_scattering, + out vec4 extinction); void main() { - float sunCosTheta = texCoord.x * 2.0 - 1.0; - vec3 sunDir = vec3(-sqrt(1.0 - sunCosTheta*sunCosTheta), 0.0, sunCosTheta); + float sun_cos_theta = texCoord.x * 2.0 - 1.0; + vec3 sun_dir = vec3(-sqrt(1.0 - sun_cos_theta*sun_cos_theta), 0.0, sun_cos_theta); - float altitude = mix(fg_EarthRadius, ATMOSPHERE_RADIUS, texCoord.y); - vec3 rayOrigin = vec3(0.0, 0.0, altitude); + float distance_to_earth_center = mix(fg_EarthRadius, ATMOSPHERE_RADIUS, texCoord.y); + vec3 ray_origin = vec3(0.0, 0.0, distance_to_earth_center); - float dist = raySphereIntersection(rayOrigin, sunDir, ATMOSPHERE_RADIUS); - float t = 0.0; - vec3 transmittance = vec3(1.0); + float t_d = ray_sphere_intersection(ray_origin, sun_dir, ATMOSPHERE_RADIUS); + float dt = t_d / float(TRANSMITTANCE_STEPS); + + vec4 result = vec4(0.0); for (int i = 0; i < TRANSMITTANCE_STEPS; ++i) { - float newT = ((float(i) + 0.3) / TRANSMITTANCE_STEPS) * dist; - float dt = newT - t; - t = newT; + float t = (float(i) + 0.5) * dt; + vec3 x_t = ray_origin + sun_dir * t; - vec3 samplePos = rayOrigin + sunDir * t; - float height = length(samplePos) - fg_EarthRadius; + float altitude = length(x_t) - fg_EarthRadius; - float mieScattering, mieAbsorption; - vec3 rayleighScattering, ozoneAbsorption; - vec3 extinction = sampleMedium(height, mieScattering, mieAbsorption, - rayleighScattering, ozoneAbsorption); + vec4 aerosol_absorption, aerosol_scattering; + vec4 molecular_absorption, molecular_scattering; + vec4 extinction; + get_atmosphere_collision_coefficients( + altitude, + aerosol_absorption, aerosol_scattering, + molecular_absorption, molecular_scattering, + extinction); - transmittance *= exp(-dt * extinction); + result += extinction * dt; } + vec4 transmittance = exp(-result); fragColor = transmittance; } diff --git a/Shaders/HDR/cloud-static.vert b/Shaders/HDR/cloud-static.vert new file mode 100644 index 000000000..80a14e857 --- /dev/null +++ b/Shaders/HDR/cloud-static.vert @@ -0,0 +1,74 @@ +#version 330 core + +layout(location = 0) in vec4 pos; +layout(location = 2) in vec4 vertexColor; +layout(location = 3) in vec4 multiTexCoord0; + +out vec2 texCoord; +out vec4 cloudColor; + +uniform mat4 osg_ModelViewMatrix; +uniform mat4 osg_ModelViewProjectionMatrix; +uniform mat4 osg_ViewMatrixInverse; +uniform vec3 fg_SunDirectionWorld; + +const float shade = 0.8; +const float cloud_height = 1000.0; + +// aerial-perspective-include.frag +vec3 add_aerial_perspective(vec3 color, vec2 coord, float depth); +vec3 get_sun_radiance(vec3 p); + +void main() +{ + texCoord = multiTexCoord0.st; + + // XXX: Should be sent as an uniform + mat4 inverseModelViewMatrix = inverse(osg_ModelViewMatrix); + + vec4 ep = inverseModelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0); + vec4 l = inverseModelViewMatrix * vec4(0.0, 0.0, 1.0, 1.0); + vec3 u = normalize(ep.xyz - l.xyz); + + vec4 final_pos = vec4(0.0, 0.0, 0.0, 1.0); + final_pos.x = pos.x; + final_pos.y = pos.y; + final_pos.z = pos.z; + final_pos.xyz += vertexColor.xyz; + + gl_Position = osg_ModelViewProjectionMatrix * final_pos; + + // Determine a lighting normal based on the vertex position from the + // center of the cloud, so that sprite on the opposite side of the cloud + // to the sun are darker. + vec3 n = normalize(vec3(osg_ViewMatrixInverse * + osg_ModelViewMatrix * vec4(-final_pos.xyz, 0.0))); + float NdotL = dot(-fg_SunDirectionWorld, n); + + vec4 final_view_pos = osg_ModelViewMatrix * final_pos; + vec4 final_world_pos = osg_ViewMatrixInverse * final_view_pos; + + float fogCoord = abs(final_view_pos.z); + float fract = smoothstep(0.0, cloud_height, final_pos.z + cloud_height); + + vec3 sun_radiance = get_sun_radiance(final_world_pos.xyz); + + // Determine the shading of the sprite based on its vertical position and + // position relative to the sun. + NdotL = min(smoothstep(-0.5, 0.0, NdotL), fract); + // Determine the shading based on a mixture from the backlight to the front + vec3 backlight = shade * sun_radiance; + + cloudColor.rgb = mix(backlight, sun_radiance, NdotL); + + // Perspective division and scale to [0, 1] to get the screen position + // of the vertex. + vec2 coord = (gl_Position.xy / gl_Position.w) * 0.5 + 0.5; + cloudColor.rgb = add_aerial_perspective( + cloudColor.rgb, coord, length(final_view_pos)); + + // As we get within 100m of the sprite, it is faded out. Equally at large + // distances it also fades out. + cloudColor.a = min(smoothstep(100.0, 250.0, fogCoord), + 1.0 - smoothstep(70000.0, 75000.0, fogCoord)); +} diff --git a/Shaders/HDR/clustered-include.frag b/Shaders/HDR/clustered-include.frag new file mode 100644 index 000000000..2f778e7e8 --- /dev/null +++ b/Shaders/HDR/clustered-include.frag @@ -0,0 +1,190 @@ +#version 330 core + +uniform sampler3D fg_Clusters; +uniform sampler2D fg_ClusteredIndices; +uniform sampler2D fg_ClusteredPointLights; +uniform sampler2D fg_ClusteredSpotLights; + +uniform int fg_ClusteredMaxPointLights; +uniform int fg_ClusteredMaxSpotLights; +uniform int fg_ClusteredMaxLightIndices; +uniform int fg_ClusteredTileSize; +uniform int fg_ClusteredDepthSlices; +uniform float fg_ClusteredSliceScale; +uniform float fg_ClusteredSliceBias; +uniform int fg_ClusteredHorizontalTiles; +uniform int fg_ClusteredVerticalTiles; + +// lighting-include.frag +vec3 evaluateLight( + vec3 baseColor, + float metallic, + float roughness, + vec3 f0, + vec3 intensity, + float visibility, + vec3 n, + vec3 l, + vec3 v, + float NdotL, + float NdotV); + +struct PointLight { + vec3 position; + vec3 color; + float intensity; + float range; +}; + +struct SpotLight { + vec3 position; + vec3 direction; + vec3 color; + float intensity; + float range; + float cos_cutoff; + float exponent; +}; + +PointLight unpackPointLight(int index) +{ + float v = (float(index) + 0.5) / float(fg_ClusteredMaxPointLights); + PointLight light; + vec4 block; + block = texture(fg_ClusteredPointLights, vec2(0.25, v)); + light.position = block.xyz; + light.range = block.w; + block = texture(fg_ClusteredPointLights, vec2(0.75, v)); + light.color = block.xyz; + light.intensity = block.w; + return light; +} + +SpotLight unpackSpotLight(int index) +{ + float v = (float(index) + 0.5) / float(fg_ClusteredMaxSpotLights); + SpotLight light; + vec4 block; + block = texture(fg_ClusteredSpotLights, vec2(0.125, v)); + light.position = block.xyz; + light.range = block.w; + block = texture(fg_ClusteredSpotLights, vec2(0.375, v)); + light.direction = block.xyz; + light.cos_cutoff = block.w; + block = texture(fg_ClusteredSpotLights, vec2(0.625, v)); + light.color = block.xyz; + light.intensity = block.w; + block = texture(fg_ClusteredSpotLights, vec2(0.875, v)); + light.exponent = block.x; + return light; +} + +int getIndex(int counter) +{ + vec2 coords = vec2(mod(float(counter), float(fg_ClusteredMaxLightIndices)) + 0.5, + float(counter / fg_ClusteredMaxLightIndices) + 0.5); + // Normalize + coords /= vec2(fg_ClusteredMaxLightIndices); + return int(texture(fg_ClusteredIndices, coords).r); +} + +float get_square_falloff_attenuation(vec3 to_light, float inv_range) +{ + float dd = dot(to_light, to_light); + float factor = dd * inv_range * inv_range; + float smooth_factor = max(1.0 - factor * factor, 0.0); + return (smooth_factor * smooth_factor) / max(dd, 0.0001); +} + +float get_spot_angle_attenuation(vec3 l, vec3 light_dir, + float cos_cutoff, float exponent) +{ + float cd = dot(-l, light_dir); + if (cd < cos_cutoff) + return 0.0; + return pow(cd, exponent); +} + +vec3 get_contribution_from_scene_lights( + vec3 p, + vec3 base_color, + float metallic, + float roughness, + vec3 f0, + vec3 n, + vec3 v) +{ + int slice = int(max(log2(-p.z) * fg_ClusteredSliceScale + + fg_ClusteredSliceBias, 0.0)); + vec3 clusterCoords = vec3(floor(gl_FragCoord.xy / fg_ClusteredTileSize), + slice) + vec3(0.5); // Pixel center + // Normalize + clusterCoords /= vec3(fg_ClusteredHorizontalTiles, + fg_ClusteredVerticalTiles, + fg_ClusteredDepthSlices); + + vec3 cluster = texture(fg_Clusters, clusterCoords).rgb; + int lightIndex = int(cluster.r); + int pointCount = int(cluster.g); + int spotCount = int(cluster.b); + + vec3 color = vec3(0.0); + + for (int i = 0; i < pointCount; ++i) { + int index = getIndex(lightIndex++); + PointLight light = unpackPointLight(index); + + vec3 to_light = light.position - p; + vec3 l = normalize(to_light); + + float attenuation = get_square_falloff_attenuation( + to_light, 1.0 / light.range); + if (attenuation <= 0.0) + continue; + + vec3 intensity = light.color * light.intensity * attenuation; + + float NdotL = max(dot(n, l), 0.0); + float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0); + + color += evaluateLight(base_color, + metallic, + roughness, + f0, + intensity, + 1.0, + n, l, v, + NdotL, NdotV); + } + + for (int i = 0; i < spotCount; ++i) { + int index = getIndex(lightIndex++); + SpotLight light = unpackSpotLight(index); + + vec3 to_light = light.position - p; + vec3 l = normalize(to_light); + + float attenuation = get_square_falloff_attenuation( + to_light, 1.0 / light.range); + attenuation *= get_spot_angle_attenuation( + l, light.direction, light.cos_cutoff, light.exponent); + if (attenuation <= 0.0) + continue; + + vec3 intensity = light.color * light.intensity * attenuation; + + float NdotL = max(dot(n, l), 0.0); + float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0); + + color += evaluateLight(base_color, + metallic, + roughness, + f0, + intensity, + 1.0, + n, l, v, + NdotL, NdotV); + } + + return color; +} diff --git a/Shaders/HDR/copy-prev-luminance.frag b/Shaders/HDR/copy-prev-luminance.frag index 44351236f..048e6b4e2 100644 --- a/Shaders/HDR/copy-prev-luminance.frag +++ b/Shaders/HDR/copy-prev-luminance.frag @@ -2,9 +2,9 @@ out float prevLum; -uniform sampler2D lum_tex; +uniform sampler2D tex; void main() { - prevLum = texelFetch(lum_tex, ivec2(0), 0).r; + prevLum = texelFetch(tex, ivec2(0), 0).r; } diff --git a/Shaders/HDR/exposure-include.frag b/Shaders/HDR/exposure-include.frag index 374a0d459..630b5ebd2 100644 --- a/Shaders/HDR/exposure-include.frag +++ b/Shaders/HDR/exposure-include.frag @@ -13,7 +13,7 @@ float log10(float x) // http://resources.mpi-inf.mpg.de/hdr/peffects/krawczyk05sccg.pdf float keyValue(float L) { - return 1.03 - 2.0 / (log10(L + 1.0) + 2.0); + return 1.0 - 2.0 / (log10(L + 1.0) + 2.0); } vec3 applyExposure(vec3 color, float avgLuminance, float threshold) diff --git a/Shaders/HDR/geometry-lfeat.frag b/Shaders/HDR/geometry-lfeat.frag new file mode 100644 index 000000000..3ca633699 --- /dev/null +++ b/Shaders/HDR/geometry-lfeat.frag @@ -0,0 +1,30 @@ +#version 330 core + +layout(location = 0) out vec4 outGBuffer0; +layout(location = 1) out vec4 outGBuffer1; +layout(location = 2) out vec4 outGBuffer2; + +in vec3 normalVS; +in vec2 texCoord; + +uniform sampler2D color_tex; + +vec2 encodeNormal(vec3 n); +vec3 decodeSRGB(vec3 screenRGB); + +void main() +{ + vec4 texel = texture(color_tex, texCoord); + if (texel.a < 0.5) + discard; + + vec3 color = decodeSRGB(texel.rgb); + + outGBuffer0.rg = encodeNormal(normalVS); + outGBuffer0.b = 0.9; + outGBuffer0.a = 1.0; + outGBuffer1.rgb = color; + outGBuffer1.a = 0.0; + outGBuffer2.rgb = vec3(0.0); + outGBuffer2.a = 1.0; +} diff --git a/Shaders/HDR/geometry-lfeat.vert b/Shaders/HDR/geometry-lfeat.vert new file mode 100644 index 000000000..a45fbc704 --- /dev/null +++ b/Shaders/HDR/geometry-lfeat.vert @@ -0,0 +1,21 @@ +#version 330 core + +layout(location = 0) in vec4 pos; +layout(location = 1) in vec3 normal; +layout(location = 2) in vec4 vertexColor; +layout(location = 3) in vec4 multiTexCoord0; + +out vec3 normalVS; +out vec2 texCoord; + +uniform mat4 osg_ModelViewProjectionMatrix; +uniform mat3 osg_NormalMatrix; + +void main() +{ + vec4 raised_pos = pos; + raised_pos.z += 0.05; + gl_Position = osg_ModelViewProjectionMatrix * raised_pos; + normalVS = normalize(osg_NormalMatrix * normal); + texCoord = multiTexCoord0.st; +} diff --git a/Shaders/HDR/geometry-pbr-transparent.frag b/Shaders/HDR/geometry-pbr-transparent.frag index 7668d3ba9..895f6f5f8 100644 --- a/Shaders/HDR/geometry-pbr-transparent.frag +++ b/Shaders/HDR/geometry-pbr-transparent.frag @@ -45,8 +45,8 @@ vec3 evaluateIBL( vec3 nWorldSpace, float NdotV, vec3 reflected); -vec3 addAerialPerspective(vec3 color, vec2 coord, float depth); -vec3 getSunIntensity(); +vec3 add_aerial_perspective(vec3 color, vec2 coord, float depth); +vec3 get_sun_radiance(vec3 p); void main() { @@ -74,14 +74,16 @@ void main() vec3 f0 = getF0Reflectance(baseColor.rgb, metallic); - vec3 sunIlluminance = getSunIntensity() * clamp(NdotL, 0.0, 1.0); + vec3 pos_world = (osg_ViewMatrixInverse * vec4(ecPos, 1.0)).xyz; + vec3 sun_radiance = get_sun_radiance(pos_world); + float shadowFactor = getShadowing(ecPos, n, l, osg_ProjectionMatrix); vec3 color = evaluateLight(baseColor.rgb, metallic, roughness, f0, - sunIlluminance, + sun_radiance, shadowFactor, n, l, v, NdotL, NdotV); @@ -99,7 +101,7 @@ void main() worldReflected); vec2 coord = (gl_FragCoord.xy - fg_Viewport.xy) / fg_Viewport.zw; - color = addAerialPerspective(color, coord, length(ecPos)); + color = add_aerial_perspective(color, coord, length(ecPos)); fragColor = vec4(color, baseColor.a); } diff --git a/Shaders/HDR/geometry-runway.frag b/Shaders/HDR/geometry-runway.frag new file mode 100644 index 000000000..53850669e --- /dev/null +++ b/Shaders/HDR/geometry-runway.frag @@ -0,0 +1,45 @@ +#version 330 core + +layout(location = 0) out vec4 outGBuffer0; +layout(location = 1) out vec4 outGBuffer1; +layout(location = 2) out vec4 outGBuffer2; + +in vec3 rawpos; +in vec2 texCoord; +in mat3 TBN; + +uniform sampler2D color_tex; +uniform sampler2D normal_tex; +uniform sampler3D noise_tex; + +const float NORMAL_MAP_SCALE = 8.0; + +vec2 encodeNormal(vec3 n); +vec3 decodeSRGB(vec3 screenRGB); + +void main() +{ + vec4 texel = texture(color_tex, texCoord); + vec3 color = decodeSRGB(texel.rgb); + + vec3 normal_texel = texture(normal_tex, texCoord * NORMAL_MAP_SCALE).rgb; + vec3 normal = normalize(TBN * (normal_texel * 2.0 - 1.0)); + + vec3 noise_large = texture(noise_tex, rawpos * 0.0045).rgb; + vec3 noise_small = texture(noise_tex, rawpos).rgb; + + float mix_factor = noise_large.r * noise_large.g * noise_large.b * 350.0; + mix_factor = smoothstep(0.0, 1.0, mix_factor); + + color = mix(color, noise_small, 0.15); + + float roughness = mix(0.94, 0.98, mix_factor); + + outGBuffer0.rg = encodeNormal(normal); + outGBuffer0.b = roughness; + outGBuffer0.a = 1.0; + outGBuffer1.rgb = vec3(color); + outGBuffer1.a = 0.0; + outGBuffer2.rgb = vec3(0.0); + outGBuffer2.a = 1.0; +} diff --git a/Shaders/HDR/geometry-runway.vert b/Shaders/HDR/geometry-runway.vert new file mode 100644 index 000000000..b6e7d6bf0 --- /dev/null +++ b/Shaders/HDR/geometry-runway.vert @@ -0,0 +1,27 @@ +#version 330 core + +layout(location = 0) in vec4 pos; +layout(location = 1) in vec3 normal; +layout(location = 3) in vec4 multiTexCoord0; + +out vec3 rawpos; +out vec2 texCoord; +out mat3 TBN; + +uniform mat4 osg_ModelViewProjectionMatrix; +uniform mat3 osg_NormalMatrix; + +void main() +{ + rawpos = pos.xyz / pos.w; + gl_Position = osg_ModelViewProjectionMatrix * pos; + texCoord = multiTexCoord0.st; + + vec3 tangent = cross(normal, vec3(1.0, 0.0, 0.0)); + vec3 binormal = cross(normal, tangent); + + vec3 T = normalize(osg_NormalMatrix * tangent); + vec3 B = normalize(osg_NormalMatrix * binormal); + vec3 N = normalize(osg_NormalMatrix * normal); + TBN = mat3(T, B, N); +} diff --git a/Shaders/HDR/geometry-terrain.frag b/Shaders/HDR/geometry-terrain.frag new file mode 100644 index 000000000..180e2f869 --- /dev/null +++ b/Shaders/HDR/geometry-terrain.frag @@ -0,0 +1,38 @@ +#version 330 core + +layout(location = 0) out vec4 outGBuffer0; +layout(location = 1) out vec4 outGBuffer1; +layout(location = 2) out vec4 outGBuffer2; + +in vec3 normalVS; +in vec2 texCoord; +in vec2 orthophoto_texCoord; + +uniform sampler2D color_tex; +uniform sampler2D orthophoto_tex; + +uniform bool orthophotoAvailable; + +vec2 encodeNormal(vec3 n); +vec3 decodeSRGB(vec3 screenRGB); + +void main() +{ + vec3 texel = texture(color_tex, texCoord).rgb; + if (orthophotoAvailable) { + vec4 sat_texel = texture(orthophoto_tex, orthophoto_texCoord); + if (sat_texel.a > 0.0) { + texel.rgb = sat_texel.rgb; + } + } + + vec3 color = decodeSRGB(texel); + + outGBuffer0.rg = encodeNormal(normalVS); + outGBuffer0.b = 0.95; + outGBuffer0.a = 1.0; + outGBuffer1.rgb = color; + outGBuffer1.a = 0.0; + outGBuffer2.rgb = vec3(0.0); + outGBuffer2.a = 1.0; +} diff --git a/Shaders/HDR/geometry-terrain.vert b/Shaders/HDR/geometry-terrain.vert new file mode 100644 index 000000000..02f3253f1 --- /dev/null +++ b/Shaders/HDR/geometry-terrain.vert @@ -0,0 +1,21 @@ +#version 330 core + +layout(location = 0) in vec4 pos; +layout(location = 1) in vec3 normal; +layout(location = 3) in vec4 multiTexCoord0; +layout(location = 5) in vec4 multiTexCoord2; + +out vec3 normalVS; +out vec2 texCoord; +out vec2 orthophoto_texCoord; + +uniform mat4 osg_ModelViewProjectionMatrix; +uniform mat3 osg_NormalMatrix; + +void main() +{ + gl_Position = osg_ModelViewProjectionMatrix * pos; + normalVS = normalize(osg_NormalMatrix * normal); + texCoord = multiTexCoord0.st; + orthophoto_texCoord = multiTexCoord2.st; +} diff --git a/Shaders/HDR/geometry-transparent.frag b/Shaders/HDR/geometry-transparent.frag index df8e4f97d..ce21dc297 100644 --- a/Shaders/HDR/geometry-transparent.frag +++ b/Shaders/HDR/geometry-transparent.frag @@ -41,8 +41,8 @@ vec3 evaluateIBL( vec3 nWorldSpace, float NdotV, vec3 reflected); -vec3 addAerialPerspective(vec3 color, vec2 coord, float depth); -vec3 getSunIntensity(); +vec3 add_aerial_perspective(vec3 color, vec2 coord, float depth); +vec3 get_sun_radiance(vec3 p); void main() { @@ -59,14 +59,16 @@ void main() vec3 f0 = getF0Reflectance(baseColor.rgb, 0.0); - vec3 sunIlluminance = getSunIntensity() * clamp(NdotL, 0.0, 1.0); + vec3 pos_world = (osg_ViewMatrixInverse * vec4(ecPos, 1.0)).xyz; + vec3 sun_radiance = get_sun_radiance(pos_world); + float shadowFactor = getShadowing(ecPos, n, l, osg_ProjectionMatrix); vec3 color = evaluateLight(baseColor, DEFAULT_TRANSPARENT_METALNESS, DEFAULT_TRANSPARENT_ROUGHNESS, f0, - sunIlluminance, + sun_radiance, shadowFactor, n, l, v, NdotL, NdotV); @@ -84,7 +86,7 @@ void main() worldReflected); vec2 coord = (gl_FragCoord.xy - fg_Viewport.xy) / fg_Viewport.zw; - color = addAerialPerspective(color, coord, length(ecPos)); + color = add_aerial_perspective(color, coord, length(ecPos)); fragColor = vec4(color, alpha); } diff --git a/Shaders/HDR/histogram-aggregate.frag b/Shaders/HDR/histogram-aggregate.frag new file mode 100644 index 000000000..eae557df6 --- /dev/null +++ b/Shaders/HDR/histogram-aggregate.frag @@ -0,0 +1,20 @@ +#version 330 core + +out uint fragHits; + +uniform usampler2D partial_histogram_tex; + +void main() +{ + ivec2 partial_histogram_size = textureSize(partial_histogram_tex, 0); // screen x 256 + uint bin = uint(gl_FragCoord.x); // [0, 255] + + uint hits = 0u; + + for (int column = 0; column < partial_histogram_size.x; ++column) { + uint partial_hits = texelFetch(partial_histogram_tex, ivec2(column, bin), 0).r; + hits += partial_hits; + } + + fragHits = hits; +} diff --git a/Shaders/HDR/histogram-column.frag b/Shaders/HDR/histogram-column.frag new file mode 100644 index 000000000..37aa85d51 --- /dev/null +++ b/Shaders/HDR/histogram-column.frag @@ -0,0 +1,35 @@ +#version 330 core + +out uint fragHits; + +uniform sampler2D hdr_tex; + +uint luminance_to_bin_index(float luminance); + +float srgb_to_luminance(vec3 color) +{ + return dot(color, vec3(0.2125, 0.7154, 0.0721)); +} + +void main() +{ + ivec2 hdr_tex_size = textureSize(hdr_tex, 0); + int column = int(gl_FragCoord.x); + uint target_bin = uint(gl_FragCoord.y); // [0, 255] + + uint hits = 0u; + + for (int row = 0; row < hdr_tex_size.y; ++row) { + vec3 hdr_color = texelFetch(hdr_tex, ivec2(column, row), 0).rgb; + // sRGB to relative luminance + float lum = srgb_to_luminance(hdr_color); + // Get the bin index corresponding to the given pixel luminance + uint pixel_bin = luminance_to_bin_index(lum); + // Check if this pixel should go in the bin + if (pixel_bin == target_bin) { + hits += 1u; + } + } + + fragHits = hits; +} diff --git a/Shaders/HDR/histogram-include.frag b/Shaders/HDR/histogram-include.frag new file mode 100644 index 000000000..205c63408 --- /dev/null +++ b/Shaders/HDR/histogram-include.frag @@ -0,0 +1,24 @@ +#version 330 core + +const float NUM_BINS = 254.0; +const float INV_NUM_BINS = 1.0 / NUM_BINS; + +const float MIN_LOG_LUM = -10.0; +const float LOG_LUM_RANGE = 16.0; +const float INV_LOG_LUM_RANGE = 1.0 / LOG_LUM_RANGE; + +uint luminance_to_bin_index(float luminance) +{ + // Avoid taking the log of zero + if (luminance < 0.005) + return 0u; + float log_lum = (log2(luminance) - MIN_LOG_LUM) * INV_LOG_LUM_RANGE; + log_lum = clamp(log_lum, 0.0, 1.0); + // From [0, 1] to [1, 255]. The 0th bin is handled by the near-zero check + return uint(log_lum * NUM_BINS + 1.0); +} + +float bin_index_to_luminance(float bin) +{ + return exp2(((bin * INV_NUM_BINS) * LOG_LUM_RANGE) + MIN_LOG_LUM); +} diff --git a/Shaders/HDR/histogram-luminance.frag b/Shaders/HDR/histogram-luminance.frag new file mode 100644 index 000000000..3df7034e8 --- /dev/null +++ b/Shaders/HDR/histogram-luminance.frag @@ -0,0 +1,42 @@ +#version 330 core + +out float fragLuminance; + +uniform usampler2D histogram_tex; +uniform sampler2D prev_lum_tex; + +uniform float osg_DeltaFrameTime; + +// Higher values give faster eye adaptation times +const float TAU = 1.1; + +float bin_index_to_luminance(float bin); + +void main() +{ + int num_bins = textureSize(histogram_tex, 0).x; // [0, 255] + + uint sum = 0u; + uint total_pixels = 0u; + + // Calculate the mean of the luminance histogram. + // We start indexing at 1 to ignore the first bin, which contains the + // luminance values that are lower than our threshold. + for (int i = 1; i < num_bins; ++i) { + uint hits = texelFetch(histogram_tex, ivec2(i, 0), 0).r; + sum += uint(i) * hits; + total_pixels += hits; + } + + float mean = float(sum) / max(float(total_pixels), 1.0) - 1.0; + + // Transform the bin index [1, 255] to an actual luminance value + float average_lum = bin_index_to_luminance(mean); + + // Simulate smooth eye adaptation over time + float prev_lum = texelFetch(prev_lum_tex, ivec2(0), 0).r; + float adapted_lum = prev_lum + (average_lum - prev_lum) * + (1.0 - exp(-osg_DeltaFrameTime * TAU)); + + fragLuminance = adapted_lum; +} diff --git a/Shaders/HDR/lighting-include.frag b/Shaders/HDR/lighting-include.frag index f096040d9..f6dc46d8d 100644 --- a/Shaders/HDR/lighting-include.frag +++ b/Shaders/HDR/lighting-include.frag @@ -180,6 +180,6 @@ vec3 evaluateLight( vec3 material = f_diffuse + f_specular; - vec3 color = material * intensity * visibility; + vec3 color = material * intensity * visibility * NdotL; return color; } diff --git a/Shaders/HDR/lighting.frag b/Shaders/HDR/lighting.frag index 9016a30a4..257fa4e74 100644 --- a/Shaders/HDR/lighting.frag +++ b/Shaders/HDR/lighting.frag @@ -43,8 +43,17 @@ vec3 evaluateIBL( vec3 nWorldSpace, float NdotV, vec3 reflected); -vec3 addAerialPerspective(vec3 color, vec2 coord, float depth); -vec3 getSunIntensity(); +vec3 add_aerial_perspective(vec3 color, vec2 coord, float depth); +vec3 get_sun_radiance(vec3 p); + +vec3 get_contribution_from_scene_lights( + vec3 p, + vec3 base_color, + float metallic, + float roughness, + vec3 f0, + vec3 n, + vec3 v); float GTAOMultiBounce(float x, vec3 albedo) { @@ -81,18 +90,26 @@ void main() vec3 f0 = getF0Reflectance(baseColor, metallic); - vec3 sunIlluminance = getSunIntensity() * clamp(NdotL, 0.0, 1.0); + vec3 pos_world = (fg_ViewMatrixInverse * vec4(pos, 1.0)).xyz; + vec3 sun_radiance = get_sun_radiance(pos_world); + float shadowFactor = getShadowing(pos, n, l, fg_ProjectionMatrix); vec3 color = evaluateLight(baseColor, metallic, roughness, f0, - sunIlluminance, + sun_radiance, shadowFactor, n, l, v, NdotL, NdotV); + color += get_contribution_from_scene_lights(pos, + baseColor, + metallic, + roughness, + f0, n, v); + float ao = occlusion; if (ambient_occlusion_enabled) { ao *= GTAOMultiBounce(texture(ao_tex, texCoord).r, baseColor); @@ -110,7 +127,7 @@ void main() NdotV, worldNormal); - color = addAerialPerspective(color, texCoord, length(pos)); + color = add_aerial_perspective(color, texCoord, length(pos)); if (debug_shadow_cascades) color *= debugShadowColor(pos, n, l); diff --git a/Shaders/HDR/luminance.frag b/Shaders/HDR/luminance.frag deleted file mode 100644 index fb69556b7..000000000 --- a/Shaders/HDR/luminance.frag +++ /dev/null @@ -1,13 +0,0 @@ -#version 330 core - -out float luminance; - -in vec2 texCoord; - -uniform sampler2D hdr_tex; - -void main() -{ - vec3 hdrColor = texture(hdr_tex, texCoord).rgb; - luminance = log(max(dot(hdrColor, vec3(0.299, 0.587, 0.114)), 0.0001)); -} diff --git a/Shaders/HDR/postprocess.frag b/Shaders/HDR/postprocess.frag index bc8bee1a6..cd9076bec 100644 --- a/Shaders/HDR/postprocess.frag +++ b/Shaders/HDR/postprocess.frag @@ -74,6 +74,11 @@ vec3 encodeSRGB(vec3 linearRGB) return mix(a, b, c); } +float rand2D(vec2 co) +{ + return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); +} + void main() { vec3 hdrColor = texture(hdr_tex, texCoord).rgb; @@ -95,5 +100,8 @@ void main() vec3 bloom = texture(bloom_tex, texCoord).rgb; color += bloom.rgb * bloom_magnitude; + // Dithering + color += mix(-0.5/255.0, 0.5/255.0, rand2D(texCoord)); + fragColor = vec4(color, 1.0); } diff --git a/Shaders/HDR/skydome.frag b/Shaders/HDR/skydome.frag index d4687ea13..4d298c945 100644 --- a/Shaders/HDR/skydome.frag +++ b/Shaders/HDR/skydome.frag @@ -2,8 +2,8 @@ out vec4 fragColor; -in vec3 vRayDir; -in vec3 vRayDirView; +in vec3 v_ray_dir; +in vec3 v_ray_dir_view; uniform bool sun_disk; uniform sampler2D sky_view_lut; @@ -14,51 +14,73 @@ uniform float fg_CameraDistanceToEarthCenter; uniform float fg_EarthRadius; uniform vec3 fg_CameraViewUp; -const float PI = 3.141592653; -const vec3 EXTRATERRESTRIAL_SOLAR_ILLUMINANCE = vec3(128.0); +const float PI = 3.14159265358979323846; const float ATMOSPHERE_RADIUS = 6471e3; - -const float sun_solid_angle = 0.545*PI/180.0; // ~half a degree +const float sun_solid_angle = radians(0.545); // ~half a degree const float sun_cos_solid_angle = cos(sun_solid_angle); +// Limb darkening constants, sampled for +// 630, 560, 490, 430 nanometers +const vec4 u = vec4(1.0); +const vec4 alpha = vec4(0.429, 0.502, 0.575, 0.643); + +//-- BEGIN spectral include +// Extraterrestial Solar Irradiance Spectra, units W * m^-2 * nm^-1 +// https://www.nrel.gov/grid/solar-resource/spectra.html +const vec4 sun_spectral_irradiance = vec4(1.679, 1.828, 1.986, 1.307); + +const mat4x3 M = mat4x3( + 137.672389239975, -8.632904716299537, -1.7181567391931372, + 32.549094028629234, 91.29801417199785, -12.005406444382531, + -38.91428392614275, 34.31665471469816, 29.89044807197628, + 8.572844237945445, -11.103384660054624, 117.47585277566478); + +vec3 linear_srgb_from_spectral_samples(vec4 L) +{ + return M * L; +} +//-- END spectral include void main() { - vec3 rayDir = normalize(vRayDir); - float azimuth = atan(rayDir.y, rayDir.x) / PI * 0.5 + 0.5; + vec3 ray_dir = normalize(v_ray_dir); + float azimuth = atan(ray_dir.y, ray_dir.x) / PI * 0.5 + 0.5; // Undo the non-linear transformation from the sky-view LUT - float l = asin(rayDir.z); + float l = asin(ray_dir.z); float elev = sqrt(abs(l) / (PI * 0.5)) * sign(l) * 0.5 + 0.5; - vec3 color = texture(sky_view_lut, vec2(azimuth, elev)).rgb; - color *= EXTRATERRESTRIAL_SOLAR_ILLUMINANCE; + vec4 sky_radiance = texture(sky_view_lut, vec2(azimuth, elev)); + // When computing the sky texture we assumed an unitary light source. + // Now multiply by the sun irradiance. + sky_radiance *= sun_spectral_irradiance; if (sun_disk) { // Render the Sun disk - vec3 rayDirView = normalize(vRayDirView); - float cosTheta = dot(rayDirView, fg_SunDirection); + vec3 ray_dir_view = normalize(v_ray_dir_view); + float cos_theta = dot(ray_dir_view, fg_SunDirection); - if (cosTheta >= sun_cos_solid_angle) { - float normalizedHeight = (fg_CameraDistanceToEarthCenter - fg_EarthRadius) + if (cos_theta >= sun_cos_solid_angle) { + float normalized_altitude = + (fg_CameraDistanceToEarthCenter - fg_EarthRadius) / (ATMOSPHERE_RADIUS - fg_EarthRadius); - float sunZenithCosTheta = dot(-rayDirView, fg_CameraViewUp); + float sun_zenith_cos_theta = dot(-ray_dir_view, fg_CameraViewUp); - vec2 coords = vec2(sunZenithCosTheta * 0.5 + 0.5, - clamp(normalizedHeight, 0.0, 1.0)); - vec3 transmittance = texture(transmittance_lut, coords).rgb; + vec2 uv = vec2(sun_zenith_cos_theta * 0.5 + 0.5, + clamp(normalized_altitude, 0.0, 1.0)); + vec4 transmittance = texture(transmittance_lut, uv); // Limb darkening // http://www.physics.hmc.edu/faculty/esin/a101/limbdarkening.pdf - vec3 u = vec3(1.0); - vec3 a = vec3(0.397, 0.503, 0.652); - float centerToEdge = 1.0 - (cosTheta - sun_cos_solid_angle) + float center_to_edge = 1.0 - (cos_theta - sun_cos_solid_angle) / (1.0 - sun_cos_solid_angle); - float mu = sqrt(max(1.0 - centerToEdge * centerToEdge, 0.0)); - vec3 factor = vec3(1.0) - u * (vec3(1.0) - pow(vec3(mu), a)); + float mu = sqrt(max(1.0 - center_to_edge*center_to_edge, 0.0)); + vec4 factor = vec4(1.0) - u * (vec4(1.0) - pow(vec4(mu), alpha)); - color += EXTRATERRESTRIAL_SOLAR_ILLUMINANCE * transmittance * factor; + vec4 sun_radiance = sun_spectral_irradiance * transmittance * factor; + sky_radiance += sun_radiance; } } - fragColor = vec4(color, 1.0); + vec3 sky_color = linear_srgb_from_spectral_samples(sky_radiance); + fragColor = vec4(sky_color, 1.0); } diff --git a/Shaders/HDR/skydome.vert b/Shaders/HDR/skydome.vert index 179547975..6a00f9190 100644 --- a/Shaders/HDR/skydome.vert +++ b/Shaders/HDR/skydome.vert @@ -2,8 +2,8 @@ layout(location = 0) in vec4 pos; -out vec3 vRayDir; -out vec3 vRayDirView; +out vec3 v_ray_dir; +out vec3 v_ray_dir_view; uniform mat4 osg_ModelViewMatrix; uniform mat4 osg_ModelViewProjectionMatrix; @@ -15,6 +15,6 @@ void main() vec4 groundPoint = osg_ModelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0); float altitude = length(groundPoint); // Compensate for the skydome being fixed on the ground - vRayDir = normalize(pos.xyz - vec3(0.0, 0.0, altitude)); - vRayDirView = (osg_ModelViewMatrix * vec4(vRayDir, 0.0)).xyz; + v_ray_dir = normalize(pos.xyz - vec3(0.0, 0.0, altitude)); + v_ray_dir_view = (osg_ModelViewMatrix * vec4(v_ray_dir, 0.0)).xyz; } diff --git a/Shaders/HDR/water-lighting.frag b/Shaders/HDR/water-lighting.frag index 2cd279426..f122f6116 100644 --- a/Shaders/HDR/water-lighting.frag +++ b/Shaders/HDR/water-lighting.frag @@ -21,7 +21,7 @@ const vec3 EXTRATERRESTRIAL_SOLAR_ILLUMINANCE = vec3(128.0); vec3 decodeNormal(vec2 f); vec3 positionFromDepth(vec2 pos, float depth); -vec3 addAerialPerspective(vec3 color, vec2 coord, float depth); +vec3 add_aerial_perspective(vec3 color, vec2 coord, float depth); float F_Schlick(float VdotH, float F0) { @@ -79,7 +79,7 @@ void main() // Add reflected Sun light color += RECIPROCAL_PI * fresnel * D_GGX(NdotH, 0.001) * sunIntensity * NdotL; - color = addAerialPerspective(color, texCoord, length(pos)); + color = add_aerial_perspective(color, texCoord, length(pos)); fragColor = color; } diff --git a/defaults.xml b/defaults.xml index 8aa42a780..f9a41d9b6 100644 --- a/defaults.xml +++ b/defaults.xml @@ -531,6 +531,7 @@ Started September 2000 by David Megginson, david@megginson.com false true + 2 0.0 @@ -546,6 +547,7 @@ Started September 2000 by David Megginson, david@megginson.com false + true @@ -1185,7 +1187,9 @@ Started September 2000 by David Megginson, david@megginson.com 50 30 - + + false +