1
0
Fork 0

HDR: Significant update

- New atmosphering rendering technique based on my own work.
- Attempt to fix some remaining transparency issues.
- Use a luminance histogram for auto exposure.
- Add support for clustered shading.
- Add WS 2.0 shaders.
- Add 3D cloud shaders.
- Add orthoscenery support.
This commit is contained in:
Fernando García Liñán 2023-02-19 16:47:55 +01:00
parent 85771b2863
commit c4d19877cf
52 changed files with 1765 additions and 669 deletions

View file

@ -83,18 +83,7 @@
<type>2d</type>
<width>256</width>
<height>64</height>
<format>rgb16f</format>
<min-filter>linear</min-filter>
<mag-filter>linear</mag-filter>
<wrap-s>clamp-to-edge</wrap-s>
<wrap-t>clamp-to-edge</wrap-t>
</buffer>
<buffer>
<name>multiple-scattering</name>
<type>2d</type>
<width>32</width>
<height>32</height>
<format>rgb16f</format>
<format>rgba16f</format>
<min-filter>linear</min-filter>
<mag-filter>linear</mag-filter>
<wrap-s>clamp-to-edge</wrap-s>
@ -104,8 +93,8 @@
<name>sky-view</name>
<type>2d</type>
<width>256</width>
<height>128</height>
<format>rgb16f</format>
<height>256</height>
<format>rgba16f</format>
<min-filter>linear</min-filter>
<mag-filter>linear</mag-filter>
<wrap-s>repeat</wrap-s>
@ -164,19 +153,27 @@
<shadow-comparison>true</shadow-comparison>
</buffer>
<!-- Average luminance -->
<!-- Histogram for automatic exposure -->
<buffer>
<name>luminance</name>
<name>histogram-partial</name>
<type>2d</type>
<width>1024</width>
<height>1024</height>
<format>r32f</format>
<min-filter>linear-mipmap-nearest</min-filter>
<width>screen</width>
<height>256</height>
<format>r32ui</format>
<min-filter>nearest</min-filter>
<mag-filter>nearest</mag-filter>
<mipmap-levels>10</mipmap-levels>
</buffer>
<buffer>
<name>prev-luminance</name>
<name>histogram</name>
<type>2d</type>
<width>256</width>
<height>1</height>
<format>r32ui</format>
<min-filter>nearest</min-filter>
<mag-filter>nearest</mag-filter>
</buffer>
<buffer>
<name>histogram-luminance</name>
<type>2d</type>
<width>1</width>
<height>1</height>
@ -185,7 +182,7 @@
<mag-filter>nearest</mag-filter>
</buffer>
<buffer>
<name>adapted-luminance</name>
<name>prev-luminance</name>
<type>2d</type>
<width>1</width>
<height>1</height>
@ -296,14 +293,7 @@
</pass>
<!--
Generate the atmospheric scattering related LUTs
This is an implementation of Sébastien Hillaire's "A Scalable and
Production Ready Sky and Atmosphere Rendering Technique".
As a small summary, the sky-view and aerial perspective LUTs are used by
later passes to apply atmospheric scattering to the skydome and
opaque/transparent objects respectively. These two LUTs also rely on
another pair of LUTs (transmittance and multiple scattering) to speed up
the computations.
Atmospheric scattering.
-->
<pass>
<name>atmos-transmittance</name>
@ -314,19 +304,6 @@
<buffer>transmittance</buffer>
</attachment>
</pass>
<pass>
<name>atmos-multiple-scattering</name>
<type>quad</type>
<effect>Effects/HDR/atmos-multiple-scattering</effect>
<binding>
<unit>0</unit>
<buffer>transmittance</buffer>
</binding>
<attachment>
<component>color0</component>
<buffer>multiple-scattering</buffer>
</attachment>
</pass>
<pass>
<name>atmos-sky-view</name>
<type>quad</type>
@ -335,10 +312,6 @@
<unit>0</unit>
<buffer>transmittance</buffer>
</binding>
<binding>
<unit>1</unit>
<buffer>multiple-scattering</buffer>
</binding>
<attachment>
<component>color0</component>
<buffer>sky-view</buffer>
@ -352,10 +325,6 @@
<unit>0</unit>
<buffer>transmittance</buffer>
</binding>
<binding>
<unit>1</unit>
<buffer>multiple-scattering</buffer>
</binding>
<attachment>
<component>color0</component>
<buffer>aerial-perspective</buffer>
@ -650,6 +619,15 @@
<clear-mask>depth stencil</clear-mask>
<clear-depth>0.0</clear-depth>
<cull-mask>0xfffff7ff</cull-mask>
<clustered-shading>
<tile-size>128</tile-size>
<depth-slices>1</depth-slices>
<num-threads>1</num-threads>
<max-pointlights>1024</max-pointlights>
<max-spotlights>1024</max-spotlights>
<pbr-lights>true</pbr-lights>
<expose-uniforms>false</expose-uniforms>
</clustered-shading>
<attachment>
<component>color0</component>
<buffer>gbuffer0</buffer>
@ -771,6 +749,13 @@
<use-shadow-pass>csm1</use-shadow-pass>
<use-shadow-pass>csm2</use-shadow-pass>
<use-shadow-pass>csm3</use-shadow-pass>
<use-clustered-uniforms>
<pass>geometry</pass>
<clusters-bind-unit>5</clusters-bind-unit>
<indices-bind-unit>6</indices-bind-unit>
<pointlights-bind-unit>14</pointlights-bind-unit>
<spotlights-bind-unit>15</spotlights-bind-unit>
</use-clustered-uniforms>
<binding>
<unit>0</unit>
<buffer>gbuffer0</buffer>
@ -918,45 +903,62 @@
</pass>
<!--
Average luminance calculation
1. Convert the RGB values to luminance values and store them on a fixed
size texture.
2. Generate mipmaps for this texture until we are left with a 1x1 texture
at the highest mipmap level. This downsampling process effectively
calculates the geometric mean of luminance.
3. Simulate eye adaptation by smoothing out the transition between
different exposures.
4. Update the previous frame average luminance value for next frame.
Auto exposure using a luminance histogram
Based on the article by Alex Tardif:
https://www.alextardif.com/HistogramLuminance.html
Instead of using a compute shader, we run two fragment shaders. The first
calculates a partial histogram of the rows of the input HDR texture, and
the second aggregates them together to obtain a single histogram.
The last step, which calculates the average scene luminance from the
histogram, is identical even though it is implemented as a fragment shader.
This technique will find the average luminance after multiplication by the
albedo. This is not correct, but the alternative (keeping a luminance
texture) is too expensive and not worth the trouble.
-->
<pass>
<name>luminance</name>
<name>histogram-column</name>
<type>quad</type>
<effect>Effects/HDR/luminance</effect>
<effect>Effects/HDR/histogram-column</effect>
<binding>
<unit>0</unit>
<buffer>hdr-result</buffer>
</binding>
<attachment>
<component>color0</component>
<buffer>luminance</buffer>
<mipmap-generation>true</mipmap-generation>
<buffer>histogram-partial</buffer>
</attachment>
</pass>
<pass>
<name>adapt-luminance</name>
<name>histogram-aggregate</name>
<type>quad</type>
<effect>Effects/HDR/adapt-luminance</effect>
<effect>Effects/HDR/histogram-aggregate</effect>
<binding>
<unit>0</unit>
<buffer>prev-luminance</buffer>
</binding>
<binding>
<unit>1</unit>
<buffer>luminance</buffer>
<buffer>histogram-partial</buffer>
</binding>
<attachment>
<component>color0</component>
<buffer>adapted-luminance</buffer>
<buffer>histogram</buffer>
</attachment>
</pass>
<pass>
<name>histogram-luminance</name>
<type>quad</type>
<effect>Effects/HDR/histogram-luminance</effect>
<binding>
<unit>0</unit>
<buffer>histogram</buffer>
</binding>
<binding>
<unit>1</unit>
<buffer>prev-luminance</buffer>
</binding>
<attachment>
<component>color0</component>
<buffer>histogram-luminance</buffer>
</attachment>
</pass>
<pass>
@ -965,7 +967,7 @@
<effect>Effects/HDR/copy-prev-luminance</effect>
<binding>
<unit>0</unit>
<buffer>adapted-luminance</buffer>
<buffer>histogram-luminance</buffer>
</binding>
<attachment>
<component>color0</component>
@ -990,7 +992,7 @@
</binding>
<binding>
<unit>1</unit>
<buffer>adapted-luminance</buffer>
<buffer>histogram-luminance</buffer>
</binding>
<attachment>
<component>color0</component>
@ -1152,7 +1154,7 @@
</binding>
<binding>
<unit>1</unit>
<buffer>adapted-luminance</buffer>
<buffer>histogram-luminance</buffer>
</binding>
<binding>
<unit>4</unit>

View file

@ -14,11 +14,6 @@
<type>sampler-2d</type>
<value type="int">0</value>
</uniform>
<uniform>
<name>multiscattering_lut</name>
<type>sampler-2d</type>
<value type="int">1</value>
</uniform>
</pass>
</technique>
</PropertyList>

View file

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<PropertyList>
<name>Effects/HDR/atmos-multiple-scattering</name>
<technique n="1">
<pass>
<program>
<vertex-shader>Shaders/HDR/trivial.vert</vertex-shader>
<fragment-shader>Shaders/HDR/atmos-multiple-scattering.frag</fragment-shader>
<fragment-shader>Shaders/HDR/atmos-include.frag</fragment-shader>
</program>
<uniform>
<name>transmittance_lut</name>
<type>sampler-2d</type>
<value type="int">0</value>
</uniform>
</pass>
</technique>
</PropertyList>

View file

@ -13,11 +13,6 @@
<type>sampler-2d</type>
<value type="int">0</value>
</uniform>
<uniform>
<name>multiscattering_lut</name>
<type>sampler-2d</type>
<value type="int">1</value>
</uniform>
</pass>
</technique>
</PropertyList>

View file

@ -8,7 +8,7 @@
<fragment-shader>Shaders/HDR/copy-prev-luminance.frag</fragment-shader>
</program>
<uniform>
<name>lum_tex</name>
<name>tex</name>
<type>sampler-2d</type>
<value type="int">0</value>
</uniform>

View file

@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<PropertyList>
<name>Effects/HDR/luminance</name>
<name>Effects/HDR/histogram-aggregate</name>
<technique n="1">
<pass>
<program>
<vertex-shader>Shaders/HDR/trivial.vert</vertex-shader>
<fragment-shader>Shaders/HDR/luminance.frag</fragment-shader>
<vertex-shader>Shaders/HDR/trivial-notexcoord.vert</vertex-shader>
<fragment-shader>Shaders/HDR/histogram-aggregate.frag</fragment-shader>
</program>
<uniform>
<name>hdr_tex</name>
<name>partial_histogram_tex</name>
<type>sampler-2d</type>
<value type="int">0</value>
</uniform>

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<PropertyList>
<name>Effects/HDR/histogram-column</name>
<technique n="1">
<pass>
<program>
<vertex-shader>Shaders/HDR/trivial-notexcoord.vert</vertex-shader>
<fragment-shader>Shaders/HDR/histogram-column.frag</fragment-shader>
<fragment-shader>Shaders/HDR/histogram-include.frag</fragment-shader>
</program>
<uniform>
<name>hdr_tex</name>
<type>sampler-2d</type>
<value type="int">0</value>
</uniform>
</pass>
</technique>
</PropertyList>

View file

@ -1,19 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<PropertyList>
<name>Effects/HDR/adapt-luminance</name>
<name>Effects/HDR/histogram-luminance</name>
<technique n="1">
<pass>
<program>
<vertex-shader>Shaders/HDR/trivial-notexcoord.vert</vertex-shader>
<fragment-shader>Shaders/HDR/adapt-luminance.frag</fragment-shader>
<fragment-shader>Shaders/HDR/histogram-luminance.frag</fragment-shader>
<fragment-shader>Shaders/HDR/histogram-include.frag</fragment-shader>
</program>
<uniform>
<name>prev_lum_tex</name>
<name>histogram_tex</name>
<type>sampler-2d</type>
<value type="int">0</value>
</uniform>
<uniform>
<name>current_lum_tex</name>
<name>prev_lum_tex</name>
<type>sampler-2d</type>
<value type="int">1</value>
</uniform>

View file

@ -41,6 +41,7 @@
<fragment-shader>Shaders/HDR/shadows-include.frag</fragment-shader>
<fragment-shader>Shaders/HDR/lighting-include.frag</fragment-shader>
<fragment-shader>Shaders/HDR/aerial-perspective-include.frag</fragment-shader>
<fragment-shader>Shaders/HDR/clustered-include.frag</fragment-shader>
</program>
<uniform>
<name>gbuffer0_tex</name>

View file

@ -234,4 +234,50 @@
<vertex-program-two-side>true</vertex-program-two-side>
</pass>
</technique>
<technique n="109">
<scheme>hdr-geometry</scheme>
</technique>
<technique n="129">
<scheme>hdr-forward</scheme>
<pass>
<!-- Reverse floating point depth buffer -->
<depth>
<function>gequal</function>
<near>1.0</near>
<far>0.0</far>
<write-mask>false</write-mask>
</depth>
<texture-unit>
<unit>0</unit>
<type>2d</type>
<image><use>texture[0]/image</use></image>
<wrap-s>clamp-to-border</wrap-s>
<wrap-t>clamp-to-border</wrap-t>
</texture-unit>
<blend>1</blend>
<rendering-hint>transparent</rendering-hint>
<program>
<vertex-shader>Shaders/HDR/cloud-static.vert</vertex-shader>
<vertex-shader>Shaders/HDR/aerial-perspective-include.frag</vertex-shader>
<fragment-shader>Shaders/HDR/3dcloud.frag</fragment-shader>
</program>
<uniform>
<name>baseTexture</name>
<type>sampler-2d</type>
<value type="int">0</value>
</uniform>
<!-- Aerial perspective include -->
<uniform>
<name>aerial_perspective_lut</name>
<type>sampler-2d</type>
<value type="int">11</value>
</uniform>
<uniform>
<name>transmittance_lut</name>
<type>sampler-2d</type>
<value type="int">12</value>
</uniform>
</pass>
</technique>
</PropertyList>

View file

@ -154,4 +154,50 @@
<vertex-program-two-side>true</vertex-program-two-side>
</pass>
</technique>
<technique n="109">
<scheme>hdr-geometry</scheme>
</technique>
<technique n="129">
<scheme>hdr-forward</scheme>
<pass>
<!-- Reverse floating point depth buffer -->
<depth>
<function>gequal</function>
<near>1.0</near>
<far>0.0</far>
<write-mask>false</write-mask>
</depth>
<texture-unit>
<unit>0</unit>
<type>2d</type>
<image><use>texture[0]/image</use></image>
<wrap-s>clamp-to-border</wrap-s>
<wrap-t>clamp-to-border</wrap-t>
</texture-unit>
<blend>1</blend>
<rendering-hint>transparent</rendering-hint>
<program>
<vertex-shader>Shaders/HDR/cloud-static.vert</vertex-shader>
<vertex-shader>Shaders/HDR/aerial-perspective-include.frag</vertex-shader>
<fragment-shader>Shaders/HDR/3dcloud.frag</fragment-shader>
</program>
<uniform>
<name>baseTexture</name>
<type>sampler-2d</type>
<value type="int">0</value>
</uniform>
<!-- Aerial perspective include -->
<uniform>
<name>aerial_perspective_lut</name>
<type>sampler-2d</type>
<value type="int">11</value>
</uniform>
<uniform>
<name>transmittance_lut</name>
<type>sampler-2d</type>
<value type="int">12</value>
</uniform>
</pass>
</technique>
</PropertyList>

View file

@ -362,4 +362,50 @@
<vertex-program-two-side>true</vertex-program-two-side>
</pass>
</technique>
<technique n="109">
<scheme>hdr-geometry</scheme>
</technique>
<technique n="129">
<scheme>hdr-forward</scheme>
<pass>
<!-- Reverse floating point depth buffer -->
<depth>
<function>gequal</function>
<near>1.0</near>
<far>0.0</far>
<write-mask>false</write-mask>
</depth>
<texture-unit>
<unit>0</unit>
<type>2d</type>
<image><use>texture[0]/image</use></image>
<wrap-s>clamp-to-border</wrap-s>
<wrap-t>clamp-to-border</wrap-t>
</texture-unit>
<blend>1</blend>
<rendering-hint>transparent</rendering-hint>
<program>
<vertex-shader>Shaders/HDR/cloud-static.vert</vertex-shader>
<vertex-shader>Shaders/HDR/aerial-perspective-include.frag</vertex-shader>
<fragment-shader>Shaders/HDR/3dcloud.frag</fragment-shader>
</program>
<uniform>
<name>baseTexture</name>
<type>sampler-2d</type>
<value type="int">0</value>
</uniform>
<!-- Aerial perspective include -->
<uniform>
<name>aerial_perspective_lut</name>
<type>sampler-2d</type>
<value type="int">11</value>
</uniform>
<uniform>
<name>transmittance_lut</name>
<type>sampler-2d</type>
<value type="int">12</value>
</uniform>
</pass>
</technique>
</PropertyList>

View file

@ -475,4 +475,68 @@
<!--<vertex-program-two-side>true</vertex-program-two-side>-->
</pass>
</technique>
<technique n="109">
<scheme>hdr-geometry</scheme>
</technique>
<technique n="129">
<scheme>hdr-forward</scheme>
<pass>
<!-- Reverse floating point depth buffer -->
<depth>
<function>gequal</function>
<near>1.0</near>
<far>0.0</far>
<write-mask>false</write-mask>
</depth>
<texture-unit>
<unit>0</unit>
<type>2d</type>
<image><use>texture[0]/image</use></image>
<wrap-s>clamp-to-border</wrap-s>
<wrap-t>clamp-to-border</wrap-t>
</texture-unit>
<blend>1</blend>
<rendering-hint>transparent</rendering-hint>
<program>
<vertex-shader>Shaders/HDR/3dcloud.vert</vertex-shader>
<vertex-shader>Shaders/HDR/aerial-perspective-include.frag</vertex-shader>
<fragment-shader>Shaders/HDR/3dcloud.frag</fragment-shader>
<attribute>
<name>usrAttr1</name>
<index>10</index>
</attribute>
<attribute>
<name>usrAttr2</name>
<index>11</index>
</attribute>
</program>
<uniform>
<name>baseTexture</name>
<type>sampler-2d</type>
<value type="int">0</value>
</uniform>
<uniform>
<name>range</name>
<type>float</type>
<value><use>range</use></value>
</uniform>
<uniform>
<name>detail_range</name>
<type>float</type>
<value><use>detail</use></value>
</uniform>
<!-- Aerial perspective include -->
<uniform>
<name>aerial_perspective_lut</name>
<type>sampler-2d</type>
<value type="int">11</value>
</uniform>
<uniform>
<name>transmittance_lut</name>
<type>sampler-2d</type>
<value type="int">12</value>
</uniform>
</pass>
</technique>
</PropertyList>

View file

@ -93,4 +93,46 @@
</polygon-offset>
</pass>
</technique>
<technique n="107">
<scheme>hdr-geometry</scheme>
<pass>
<!-- Reverse floating point depth buffer -->
<depth>
<function>gequal</function>
<near>1.0</near>
<far>0.0</far>
</depth>
<stencil>
<function>always</function>
<value>9</value>
<pass>replace</pass>
</stencil>
<texture-unit>
<unit>0</unit>
<type><use>texture[0]/type</use></type>
<image><use>texture[0]/image</use></image>
<filter><use>texture[0]/filter</use></filter>
<wrap-s><use>texture[0]/wrap-s</use></wrap-s>
<wrap-t><use>texture[0]/wrap-t</use></wrap-t>
</texture-unit>
<blend>0</blend>
<rendering-hint>opaque</rendering-hint>
<cull-face>back</cull-face>
<program>
<vertex-shader>Shaders/HDR/geometry-lfeat.vert</vertex-shader>
<fragment-shader>Shaders/HDR/geometry-lfeat.frag</fragment-shader>
<fragment-shader>Shaders/HDR/gbuffer-include.frag</fragment-shader>
</program>
<uniform>
<name>color_tex</name>
<type>sampler-2d</type>
<value type="int">0</value>
</uniform>
<polygon-offset>
<factor>2.0</factor>
<units>1.0</units>
</polygon-offset>
</pass>
</technique>
</PropertyList>

View file

@ -5,5 +5,5 @@ It's kept for backwards compatibility and should not be used on new projects.
-->
<PropertyList>
<name>Effects/model-combined-transparent</name>
<inherits-from>Effects/model-combined</inherits-from>
<inherits-from>Effects/model-transparent</inherits-from>
</PropertyList>

View file

@ -12,6 +12,11 @@
<!-- DIFFUSE -->
</material>
<material-id>0</material-id>
<render-bin>
<bin-number>1</bin-number>
<bin-name>RenderBin</bin-name>
</render-bin>
<write-depth type="bool">true</write-depth>
<!-- BEGIN fog include -->
<visibility><use>/environment/ground-visibility-m</use></visibility>
<avisibility><use>/environment/visibility-m</use></avisibility>
@ -87,6 +92,9 @@
</predicate>
<pass>
<lighting>true</lighting>
<depth>
<write-mask><use>write-depth</use></write-mask>
</depth>
<material>
<active><use>material/active</use></active>
<ambient><use>material/ambient</use></ambient>
@ -103,11 +111,10 @@
</blend>
<shade-model><use>shade-model</use></shade-model>
<cull-face><use>cull-face</use></cull-face>
<!--<render-bin>
<bin-number>1</bin-number>
<bin-name>RenderBin</bin-name>
</render-bin>-->
<rendering-hint><use>rendering-hint</use></rendering-hint>
<render-bin>
<bin-number><use>render-bin/bin-number</use></bin-number>
<bin-name><use>render-bin/bin-name</use></bin-name>
</render-bin>
<texture-unit>
<!-- The texture unit is always active because the shaders expect
that. -->
@ -408,6 +415,9 @@
</predicate>
<pass>
<lighting>true</lighting>
<depth>
<write-mask><use>write-depth</use></write-mask>
</depth>
<material>
<active>
<use>material/active</use>
@ -448,9 +458,10 @@
<cull-face>
<use>cull-face</use>
</cull-face>
<rendering-hint>
<use>rendering-hint</use>
</rendering-hint>
<render-bin>
<bin-number><use>render-bin/bin-number</use></bin-number>
<bin-name><use>render-bin/bin-name</use></bin-name>
</render-bin>
<texture-unit>
<!-- The texture unit is always active because the shaders expect
that. -->
@ -572,6 +583,9 @@
<technique n="13">
<pass>
<lighting>true</lighting>
<depth>
<write-mask><use>write-depth</use></write-mask>
</depth>
<material>
<active>
<use>material/active</use>
@ -612,9 +626,10 @@
<cull-face>
<use>cull-face</use>
</cull-face>
<rendering-hint>
<use>rendering-hint</use>
</rendering-hint>
<render-bin>
<bin-number><use>render-bin/bin-number</use></bin-number>
<bin-name><use>render-bin/bin-name</use></bin-name>
</render-bin>
<texture-unit>
<active>
<use>texture[0]/active</use>

View file

@ -4,6 +4,12 @@
<inherits-from>Effects/model-default</inherits-from>
<parameters>
<render-bin>
<bin-number>111</bin-number>
<bin-name>DepthSortedBin</bin-name>
</render-bin>
<write-depth type="bool">false</write-depth>
<texture n="8">
<image>Textures/PBR/dfg_lut.dds</image>
<type>2d</type>

View file

@ -886,4 +886,64 @@
</alpha-test>
</pass>
</technique>
<technique n="108">
<scheme>hdr-geometry</scheme>
<pass>
<!-- Reverse floating point depth buffer -->
<depth>
<function>gequal</function>
<near>1.0</near>
<far>0.0</far>
</depth>
<stencil>
<function>always</function>
<value>9</value>
<pass>replace</pass>
</stencil>
<texture-unit>
<unit>0</unit>
<type><use>texture[0]/type</use></type>
<image><use>texture[0]/image</use></image>
<filter><use>texture[0]/filter</use></filter>
<wrap-s><use>texture[0]/wrap-s</use></wrap-s>
<wrap-t><use>texture[0]/wrap-t</use></wrap-t>
</texture-unit>
<texture-unit>
<unit>1</unit>
<type><use>texture[4]/type</use></type>
<image><use>texture[4]/image</use></image>
<filter><use>texture[4]/filter</use></filter>
<wrap-s><use>texture[4]/wrap-s</use></wrap-s>
<wrap-t><use>texture[4]/wrap-t</use></wrap-t>
</texture-unit>
<texture-unit>
<unit>2</unit>
<type>noise</type>
</texture-unit>
<blend>0</blend>
<rendering-hint>opaque</rendering-hint>
<cull-face>back</cull-face>
<program>
<vertex-shader>Shaders/HDR/geometry-runway.vert</vertex-shader>
<fragment-shader>Shaders/HDR/geometry-runway.frag</fragment-shader>
<fragment-shader>Shaders/HDR/gbuffer-include.frag</fragment-shader>
</program>
<uniform>
<name>color_tex</name>
<type>sampler-2d</type>
<value type="int">0</value>
</uniform>
<uniform>
<name>normal_tex</name>
<type>sampler-2d</type>
<value type="int">1</value>
</uniform>
<uniform>
<name>noise_tex</name>
<type>sampler-3d</type>
<value type="int">2</value>
</uniform>
</pass>
</technique>
</PropertyList>

View file

@ -1669,7 +1669,6 @@
</pass>
</technique>
<!-- Just for ocean tiles. For actual terrain we use ws30.eff. -->
<technique n="109">
<scheme>hdr-geometry</scheme>
<pass>
@ -1696,8 +1695,8 @@
<rendering-hint>opaque</rendering-hint>
<cull-face>back</cull-face>
<program>
<vertex-shader>Shaders/HDR/geometry.vert</vertex-shader>
<fragment-shader>Shaders/HDR/geometry.frag</fragment-shader>
<vertex-shader>Shaders/HDR/geometry-terrain.vert</vertex-shader>
<fragment-shader>Shaders/HDR/geometry-terrain.frag</fragment-shader>
<fragment-shader>Shaders/HDR/gbuffer-include.frag</fragment-shader>
</program>
<uniform>
@ -1705,16 +1704,11 @@
<type>sampler-2d</type>
<value type="int">0</value>
</uniform>
<!-- Orthophoto include -->
<uniform>
<name>color_mode</name>
<type>int</type>
<value>2</value>
<!-- AMBIENT_AND_DIFFUSE -->
</uniform>
<uniform>
<name>material_diffuse</name>
<type>float-vec4</type>
<value><use>material/diffuse</use></value>
<name>orthophoto_tex</name>
<type>sampler-2d</type>
<value type="int">15</value>
</uniform>
</pass>
</technique>

61
Shaders/HDR/3dcloud.frag Normal file
View file

@ -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;
}

116
Shaders/HDR/3dcloud.vert Normal file
View file

@ -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;
}
}

View file

@ -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));
}

View file

@ -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);
}

View file

@ -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));
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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));
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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)

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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));
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -531,6 +531,7 @@ Started September 2000 by David Megginson, david@megginson.com
<available type="bool" userarchive="n">false</available>
<enabled type="bool" userarchive="y">true</enabled>
</pilot-model>
<hdr>
<antialiasing-technique type="int" userarchive="y">2</antialiasing-technique>
<exposure-compensation type="float">0.0</exposure-compensation>
@ -546,6 +547,7 @@ Started September 2000 by David Megginson, david@megginson.com
<show-shadow-cascades type="bool">false</show-shadow-cascades>
</debug>
</hdr>
<composite-viewer-enabled type="bool">true</composite-viewer-enabled>
</rendering>
<vr>
@ -1185,7 +1187,9 @@ Started September 2000 by David Megginson, david@megginson.com
<max-rate-hz type="int">50</max-rate-hz>
<rate-hz type="int">30</rate-hz>
</emexec>
<debug>
<show-light-volumes type="bool">false</show-light-volumes>
</debug>
</sim>
<!-- accelerations -->
<accelerations>