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
+
+ 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
+
+ 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
+
+ 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
+
+ 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
+
+
+
+ detail_range
+ float
+
+
+
+
+ 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
+
+
+
+
+
+
+ 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
@@ -87,6 +92,9 @@
true
+
+
+
@@ -103,11 +111,10 @@
-
-
+
+
+
+
@@ -408,6 +415,9 @@
true
+
+
+
@@ -448,9 +458,10 @@
-
-
-
+
+
+
+
@@ -572,6 +583,9 @@
true
+
+
+
@@ -612,9 +626,10 @@
-
-
-
+
+
+
+
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
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+ 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
-
+ 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
+