HDR: Add support for glTF transparent objects
This commit is contained in:
parent
0c82a1f59a
commit
212f8fdab9
13 changed files with 863 additions and 430 deletions
|
@ -9,7 +9,7 @@
|
|||
<!-- TODO: Explicitly select the LOD level -->
|
||||
<cull-mask>0x800</cull-mask>
|
||||
<binding>
|
||||
<unit>11</unit>
|
||||
<unit>13</unit>
|
||||
<buffer>sky-view</buffer>
|
||||
</binding>
|
||||
</PropertyList>
|
||||
|
|
|
@ -704,7 +704,7 @@
|
|||
<buffer>depth</buffer>
|
||||
</binding>
|
||||
<binding>
|
||||
<unit>8</unit>
|
||||
<unit>7</unit>
|
||||
<buffer>ao0</buffer>
|
||||
</binding>
|
||||
<binding>
|
||||
|
@ -730,7 +730,7 @@
|
|||
</pass>
|
||||
|
||||
<!--
|
||||
Forward pass
|
||||
Main forward pass
|
||||
Render all objects that couldn't be rendered on the G-Buffer, mainly
|
||||
transparent objects. This is also done in HDR.
|
||||
We reuse the depth buffer from the G-Buffer stage so we can take advantage
|
||||
|
@ -740,17 +740,29 @@
|
|||
<name>forward</name>
|
||||
<type>scene</type>
|
||||
<effect-scheme>hdr-forward</effect-scheme>
|
||||
<use-shadow-pass>csm0</use-shadow-pass>
|
||||
<use-shadow-pass>csm1</use-shadow-pass>
|
||||
<use-shadow-pass>csm2</use-shadow-pass>
|
||||
<use-shadow-pass>csm3</use-shadow-pass>
|
||||
<binding>
|
||||
<unit>9</unit>
|
||||
<buffer>prefiltered-envmap</buffer>
|
||||
</binding>
|
||||
<binding>
|
||||
<unit>10</unit>
|
||||
<buffer>sun-shadowmap-atlas</buffer>
|
||||
</binding>
|
||||
<binding>
|
||||
<unit>11</unit>
|
||||
<buffer>sky-view</buffer>
|
||||
<buffer>aerial-perspective</buffer>
|
||||
</binding>
|
||||
<binding>
|
||||
<unit>12</unit>
|
||||
<buffer>transmittance</buffer>
|
||||
</binding>
|
||||
<binding>
|
||||
<unit>13</unit>
|
||||
<buffer>transmittance</buffer>
|
||||
<buffer>sky-view</buffer>
|
||||
</binding>
|
||||
<attachment>
|
||||
<component>color0</component>
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
<PropertyList>
|
||||
<name>Effects/HDR/lighting</name>
|
||||
<parameters>
|
||||
<texture n="7">
|
||||
<texture n="8">
|
||||
<image>Textures/PBR/dfg_lut.dds</image>
|
||||
<type>2d</type>
|
||||
<type>2d</type>
|
||||
<filter>linear</filter>
|
||||
<mag-filter>linear</mag-filter>
|
||||
<wrap-s>clamp-to-edge</wrap-s>
|
||||
<wrap-t>clamp-to-edge</wrap-t>
|
||||
<internal-format>normalized</internal-format>
|
||||
|
@ -14,18 +15,21 @@
|
|||
<technique n="1">
|
||||
<pass>
|
||||
<texture-unit>
|
||||
<unit>7</unit>
|
||||
<image><use>texture[7]/image</use></image>
|
||||
<type><use>texture[7]/type</use></type>
|
||||
<filter><use>texture[7]/filter</use></filter>
|
||||
<wrap-s><use>texture[7]/wrap-s</use></wrap-s>
|
||||
<wrap-t><use>texture[7]/wrap-t</use></wrap-t>
|
||||
<internal-format><use>texture[7]/internal-format</use></internal-format>
|
||||
<unit>8</unit>
|
||||
<image><use>texture[8]/image</use></image>
|
||||
<type><use>texture[8]/type</use></type>
|
||||
<filter><use>texture[8]/filter</use></filter>
|
||||
<mag-filter><use>texture[8]/mag-filter</use></mag-filter>
|
||||
<wrap-s><use>texture[8]/wrap-s</use></wrap-s>
|
||||
<wrap-t><use>texture[8]/wrap-t</use></wrap-t>
|
||||
<internal-format><use>texture[8]/internal-format</use></internal-format>
|
||||
</texture-unit>
|
||||
<program>
|
||||
<vertex-shader>Shaders/HDR/trivial.vert</vertex-shader>
|
||||
<fragment-shader>Shaders/HDR/lighting.frag</fragment-shader>
|
||||
<fragment-shader>Shaders/HDR/gbuffer-include.frag</fragment-shader>
|
||||
<fragment-shader>Shaders/HDR/lighting-include.frag</fragment-shader>
|
||||
<fragment-shader>Shaders/HDR/aerial-perspective-include.frag</fragment-shader>
|
||||
</program>
|
||||
<uniform>
|
||||
<name>gbuffer0_tex</name>
|
||||
|
@ -48,12 +52,13 @@
|
|||
<value type="int">3</value>
|
||||
</uniform>
|
||||
<uniform>
|
||||
<name>dfg_lut</name>
|
||||
<name>ao_tex</name>
|
||||
<type>sampler-2d</type>
|
||||
<value type="int">7</value>
|
||||
</uniform>
|
||||
<!-- Lighting include -->
|
||||
<uniform>
|
||||
<name>ao_tex</name>
|
||||
<name>dfg_lut</name>
|
||||
<type>sampler-2d</type>
|
||||
<value type="int">8</value>
|
||||
</uniform>
|
||||
|
@ -67,6 +72,7 @@
|
|||
<type>sampler-2d-shadow</type>
|
||||
<value type="int">10</value>
|
||||
</uniform>
|
||||
<!-- Aerial perspective include -->
|
||||
<uniform>
|
||||
<name>aerial_perspective_lut</name>
|
||||
<type>sampler-2d</type>
|
||||
|
|
191
Effects/model-pbr-transparent.eff
Normal file
191
Effects/model-pbr-transparent.eff
Normal file
|
@ -0,0 +1,191 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<PropertyList>
|
||||
<name>Effects/model-pbr-transparent</name>
|
||||
<inherits-from>Effects/model-pbr</inherits-from>
|
||||
<parameters>
|
||||
<texture n="8">
|
||||
<image>Textures/PBR/dfg_lut.dds</image>
|
||||
<type>2d</type>
|
||||
<filter>linear</filter>
|
||||
<mag-filter>linear</mag-filter>
|
||||
<wrap-s>clamp-to-edge</wrap-s>
|
||||
<wrap-t>clamp-to-edge</wrap-t>
|
||||
<internal-format>normalized</internal-format>
|
||||
</texture>
|
||||
<!-- Alpha Coverage -->
|
||||
<blend>1</blend>
|
||||
<alpha-cutoff>-1.0</alpha-cutoff>
|
||||
</parameters>
|
||||
|
||||
<technique n="19">
|
||||
<scheme>hdr-geometry</scheme>
|
||||
</technique>
|
||||
|
||||
<technique n="40">
|
||||
<scheme>hdr-forward</scheme>
|
||||
<pass>
|
||||
<!-- Reverse floating point depth buffer -->
|
||||
<depth>
|
||||
<function>gequal</function>
|
||||
<near>1.0</near>
|
||||
<far>0.0</far>
|
||||
</depth>
|
||||
<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>
|
||||
<mag-filter><use>texture[0]/mag-filter</use></mag-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[1]/type</use></type>
|
||||
<image><use>texture[1]/image</use></image>
|
||||
<filter><use>texture[1]/filter</use></filter>
|
||||
<mag-filter><use>texture[1]/mag-filter</use></mag-filter>
|
||||
<wrap-s><use>texture[1]/wrap-s</use></wrap-s>
|
||||
<wrap-t><use>texture[1]/wrap-t</use></wrap-t>
|
||||
</texture-unit>
|
||||
<texture-unit>
|
||||
<unit>2</unit>
|
||||
<type><use>texture[2]/type</use></type>
|
||||
<image><use>texture[2]/image</use></image>
|
||||
<filter><use>texture[2]/filter</use></filter>
|
||||
<mag-filter><use>texture[2]/mag-filter</use></mag-filter>
|
||||
<wrap-s><use>texture[2]/wrap-s</use></wrap-s>
|
||||
<wrap-t><use>texture[2]/wrap-t</use></wrap-t>
|
||||
</texture-unit>
|
||||
<texture-unit>
|
||||
<unit>3</unit>
|
||||
<type><use>texture[3]/type</use></type>
|
||||
<image><use>texture[3]/image</use></image>
|
||||
<filter><use>texture[3]/filter</use></filter>
|
||||
<mag-filter><use>texture[3]/mag-filter</use></mag-filter>
|
||||
<wrap-s><use>texture[3]/wrap-s</use></wrap-s>
|
||||
<wrap-t><use>texture[3]/wrap-t</use></wrap-t>
|
||||
</texture-unit>
|
||||
<texture-unit>
|
||||
<unit>4</unit>
|
||||
<type><use>texture[4]/type</use></type>
|
||||
<image><use>texture[4]/image</use></image>
|
||||
<filter><use>texture[4]/filter</use></filter>
|
||||
<mag-filter><use>texture[4]/mag-filter</use></mag-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>8</unit>
|
||||
<image><use>texture[8]/image</use></image>
|
||||
<type><use>texture[8]/type</use></type>
|
||||
<filter><use>texture[8]/filter</use></filter>
|
||||
<mag-filter><use>texture[8]/mag-filter</use></mag-filter>
|
||||
<wrap-s><use>texture[8]/wrap-s</use></wrap-s>
|
||||
<wrap-t><use>texture[8]/wrap-t</use></wrap-t>
|
||||
<internal-format><use>texture[8]/internal-format</use></internal-format>
|
||||
</texture-unit>
|
||||
<blend><use>blend</use></blend>
|
||||
<rendering-hint>transparent</rendering-hint>
|
||||
<cull-face><use>cull-face</use></cull-face>
|
||||
<program>
|
||||
<vertex-shader>Shaders/HDR/geometry-pbr-transparent.vert</vertex-shader>
|
||||
<fragment-shader>Shaders/HDR/geometry-pbr-transparent.frag</fragment-shader>
|
||||
<fragment-shader>Shaders/HDR/gbuffer-include.frag</fragment-shader>
|
||||
<fragment-shader>Shaders/HDR/lighting-include.frag</fragment-shader>
|
||||
<fragment-shader>Shaders/HDR/aerial-perspective-include.frag</fragment-shader>
|
||||
<attribute>
|
||||
<name>tangent</name>
|
||||
<index>6</index>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<name>binormal</name>
|
||||
<index>7</index>
|
||||
</attribute>
|
||||
</program>
|
||||
<uniform>
|
||||
<name>base_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>metallic_roughness_tex</name>
|
||||
<type>sampler-2d</type>
|
||||
<value type="int">2</value>
|
||||
</uniform>
|
||||
<uniform>
|
||||
<name>occlusion_tex</name>
|
||||
<type>sampler-2d</type>
|
||||
<value type="int">3</value>
|
||||
</uniform>
|
||||
<uniform>
|
||||
<name>emissive_tex</name>
|
||||
<type>sampler-2d</type>
|
||||
<value type="int">4</value>
|
||||
</uniform>
|
||||
<uniform>
|
||||
<name>base_color_factor</name>
|
||||
<type>float-vec4</type>
|
||||
<value><use>base-color-factor</use></value>
|
||||
</uniform>
|
||||
<uniform>
|
||||
<name>metallic_factor</name>
|
||||
<type>float</type>
|
||||
<value><use>metallic-factor</use></value>
|
||||
</uniform>
|
||||
<uniform>
|
||||
<name>roughness_factor</name>
|
||||
<type>float</type>
|
||||
<value><use>roughness-factor</use></value>
|
||||
</uniform>
|
||||
<uniform>
|
||||
<name>emissive_factor</name>
|
||||
<type>float-vec3</type>
|
||||
<value><use>emissive-factor</use></value>
|
||||
</uniform>
|
||||
<uniform>
|
||||
<name>flip_vertically</name>
|
||||
<type>bool</type>
|
||||
<value><use>flip-vertically</use></value>
|
||||
</uniform>
|
||||
<uniform>
|
||||
<name>alpha_cutoff</name>
|
||||
<type>float</type>
|
||||
<value><use>alpha-cutoff</use></value>
|
||||
</uniform>
|
||||
<!-- Lighting include -->
|
||||
<uniform>
|
||||
<name>dfg_lut</name>
|
||||
<type>sampler-2d</type>
|
||||
<value type="int">8</value>
|
||||
</uniform>
|
||||
<uniform>
|
||||
<name>prefiltered_envmap</name>
|
||||
<type>sampler-cube</type>
|
||||
<value type="int">9</value>
|
||||
</uniform>
|
||||
<uniform>
|
||||
<name>shadow_tex</name>
|
||||
<type>sampler-2d-shadow</type>
|
||||
<value type="int">10</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>
|
|
@ -27,12 +27,6 @@
|
|||
<type>white</type>
|
||||
</texture>
|
||||
<emissive-factor type="vec3d">0.0 0.0 0.0</emissive-factor>
|
||||
<!-- Alpha Coverage -->
|
||||
<blend>
|
||||
<active>false</active>
|
||||
</blend>
|
||||
<rendering-hint>opaque</rendering-hint>
|
||||
<alpha-cutoff>-1.0</alpha-cutoff>
|
||||
<!-- Double Sided -->
|
||||
<cull-face>back</cull-face>
|
||||
<!-- Whether to flip the texture vertically -->
|
||||
|
@ -98,8 +92,8 @@
|
|||
<wrap-s><use>texture[4]/wrap-s</use></wrap-s>
|
||||
<wrap-t><use>texture[4]/wrap-t</use></wrap-t>
|
||||
</texture-unit>
|
||||
<blend><active><use>blend/active</use></active></blend>
|
||||
<rendering-hint><use>rendering-hint</use></rendering-hint>
|
||||
<blend>0</blend>
|
||||
<rendering-hint>opaque</rendering-hint>
|
||||
<cull-face><use>cull-face</use></cull-face>
|
||||
<program>
|
||||
<vertex-shader>Shaders/HDR/geometry-pbr.vert</vertex-shader>
|
||||
|
@ -159,11 +153,6 @@
|
|||
<type>float-vec3</type>
|
||||
<value><use>emissive-factor</use></value>
|
||||
</uniform>
|
||||
<uniform>
|
||||
<name>alpha_cutoff</name>
|
||||
<type>float</type>
|
||||
<value><use>alpha-cutoff</use></value>
|
||||
</uniform>
|
||||
<uniform>
|
||||
<name>flip_vertically</name>
|
||||
<type>bool</type>
|
||||
|
|
|
@ -320,12 +320,12 @@
|
|||
<value type="bool">true</value>
|
||||
</uniform>
|
||||
<uniform>
|
||||
<name>sky_view_lut</name>
|
||||
<name>transmittance_lut</name>
|
||||
<type>sampler-2d</type>
|
||||
<value type="int">11</value>
|
||||
<value type="int">12</value>
|
||||
</uniform>
|
||||
<uniform>
|
||||
<name>transmittance_lut</name>
|
||||
<name>sky_view_lut</name>
|
||||
<type>sampler-2d</type>
|
||||
<value type="int">13</value>
|
||||
</uniform>
|
||||
|
@ -345,12 +345,12 @@
|
|||
<value type="bool">false</value>
|
||||
</uniform>
|
||||
<uniform>
|
||||
<name>sky_view_lut</name>
|
||||
<name>transmittance_lut</name>
|
||||
<type>sampler-2d</type>
|
||||
<value type="int">11</value>
|
||||
<value type="int">12</value>
|
||||
</uniform>
|
||||
<uniform>
|
||||
<name>transmittance_lut</name>
|
||||
<name>sky_view_lut</name>
|
||||
<type>sampler-2d</type>
|
||||
<value type="int">13</value>
|
||||
</uniform>
|
||||
|
|
73
Shaders/HDR/aerial-perspective-include.frag
Normal file
73
Shaders/HDR/aerial-perspective-include.frag
Normal file
|
@ -0,0 +1,73 @@
|
|||
#version 330 core
|
||||
|
||||
uniform sampler2D aerial_perspective_lut;
|
||||
uniform sampler2D transmittance_lut;
|
||||
|
||||
uniform float fg_SunZenithCosTheta;
|
||||
uniform float fg_CameraDistanceToEarthCenter;
|
||||
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 ATMOSPHERE_RADIUS = 6471e3;
|
||||
|
||||
vec4 sampleAerialPerspectiveSlice(vec2 coord, int slice)
|
||||
{
|
||||
// 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));
|
||||
}
|
||||
|
||||
vec4 sampleAerialPerspective(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) {
|
||||
// Handle special case of fragments behind the first slice
|
||||
color = mix(vec4(0.0, 0.0, 0.0, 1.0),
|
||||
sampleAerialPerspectiveSlice(coord, 0),
|
||||
w);
|
||||
} else {
|
||||
w -= 1.0;
|
||||
// Manually interpolate between slices
|
||||
color = mix(sampleAerialPerspectiveSlice(coord, int(floor(w))),
|
||||
sampleAerialPerspectiveSlice(coord, int(ceil(w))),
|
||||
sqrt(fract(w)));
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
vec3 addAerialPerspective(vec3 color, vec2 coord, float depth)
|
||||
{
|
||||
vec4 aerialPerspective = sampleAerialPerspective(coord, depth);
|
||||
return color * aerialPerspective.a + aerialPerspective.rgb
|
||||
* EXTRATERRESTRIAL_SOLAR_ILLUMINANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
vec3 getSunIntensity()
|
||||
{
|
||||
float normalizedHeight = (fg_CameraDistanceToEarthCenter - 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;
|
||||
|
||||
return EXTRATERRESTRIAL_SOLAR_ILLUMINANCE * transmittance;
|
||||
}
|
110
Shaders/HDR/geometry-pbr-transparent.frag
Normal file
110
Shaders/HDR/geometry-pbr-transparent.frag
Normal file
|
@ -0,0 +1,110 @@
|
|||
#version 330 core
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
in vec2 texCoord;
|
||||
in mat3 TBN;
|
||||
in vec3 ecPos;
|
||||
|
||||
uniform sampler2D base_color_tex;
|
||||
uniform sampler2D normal_tex;
|
||||
uniform sampler2D metallic_roughness_tex;
|
||||
uniform sampler2D occlusion_tex;
|
||||
uniform sampler2D emissive_tex;
|
||||
uniform vec4 base_color_factor;
|
||||
uniform float metallic_factor;
|
||||
uniform float roughness_factor;
|
||||
uniform vec3 emissive_factor;
|
||||
uniform float alpha_cutoff;
|
||||
|
||||
uniform mat4 osg_ViewMatrixInverse;
|
||||
|
||||
uniform vec4 fg_Viewport;
|
||||
uniform vec3 fg_SunDirection;
|
||||
|
||||
vec3 decodeSRGB(vec3 screenRGB);
|
||||
vec3 getF0Reflectance(vec3 baseColor, float metallic);
|
||||
float getShadowing(vec3 p, vec3 n, float NdotL);
|
||||
vec3 evaluateLight(
|
||||
vec3 baseColor,
|
||||
float metallic,
|
||||
float roughness,
|
||||
float clearcoat,
|
||||
float clearcoatRoughness,
|
||||
vec3 f0,
|
||||
vec3 intensity,
|
||||
float occlusion,
|
||||
vec3 n,
|
||||
vec3 l,
|
||||
vec3 v,
|
||||
float NdotL,
|
||||
float NdotV);
|
||||
vec3 evaluateIBL(
|
||||
vec3 baseColor,
|
||||
float metallic,
|
||||
float roughness,
|
||||
vec3 f0,
|
||||
float occlusion,
|
||||
vec3 nWorldSpace,
|
||||
float NdotV,
|
||||
vec3 reflected);
|
||||
vec3 addAerialPerspective(vec3 color, vec2 coord, float depth);
|
||||
vec3 getSunIntensity();
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 baseColorTexel = texture(base_color_tex, texCoord);
|
||||
vec4 baseColor = vec4(decodeSRGB(baseColorTexel.rgb), baseColorTexel.a)
|
||||
* base_color_factor;
|
||||
if (baseColor.a < alpha_cutoff)
|
||||
discard;
|
||||
|
||||
float occlusion = texture(occlusion_tex, texCoord).r;
|
||||
vec3 n = texture(normal_tex, texCoord).rgb * 2.0 - 1.0;
|
||||
n = normalize(TBN * n);
|
||||
|
||||
vec4 metallicRoughness = texture(metallic_roughness_tex, texCoord);
|
||||
float metallic = metallicRoughness.r * metallic_factor;
|
||||
float roughness = metallicRoughness.g * roughness_factor;
|
||||
|
||||
vec3 emissive = texture(emissive_tex, texCoord).rgb * emissive_factor;
|
||||
|
||||
vec3 v = normalize(-ecPos);
|
||||
vec3 l = fg_SunDirection;
|
||||
|
||||
float NdotL = dot(n, l);
|
||||
float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
|
||||
|
||||
vec3 f0 = getF0Reflectance(baseColor.rgb, metallic);
|
||||
|
||||
vec3 sunIlluminance = getSunIntensity() * clamp(NdotL, 0.0, 1.0);
|
||||
float shadowFactor = getShadowing(ecPos, n, NdotL);
|
||||
|
||||
vec3 color = evaluateLight(baseColor.rgb,
|
||||
metallic,
|
||||
roughness,
|
||||
0.0,
|
||||
0.0,
|
||||
f0,
|
||||
sunIlluminance,
|
||||
shadowFactor,
|
||||
n, l, v,
|
||||
NdotL, NdotV);
|
||||
|
||||
vec3 worldNormal = (osg_ViewMatrixInverse * vec4(n, 0.0)).xyz;
|
||||
vec3 worldReflected = (osg_ViewMatrixInverse * vec4(reflect(-v, n), 0.0)).xyz;
|
||||
|
||||
color += evaluateIBL(baseColor.rgb,
|
||||
metallic,
|
||||
roughness,
|
||||
f0,
|
||||
occlusion,
|
||||
worldNormal,
|
||||
NdotV,
|
||||
worldReflected);
|
||||
|
||||
vec2 coord = (gl_FragCoord.xy - fg_Viewport.xy) / fg_Viewport.zw;
|
||||
color = addAerialPerspective(color, coord, length(ecPos));
|
||||
|
||||
fragColor = vec4(color, baseColor.a);
|
||||
}
|
32
Shaders/HDR/geometry-pbr-transparent.vert
Normal file
32
Shaders/HDR/geometry-pbr-transparent.vert
Normal file
|
@ -0,0 +1,32 @@
|
|||
#version 330 core
|
||||
|
||||
layout(location = 0) in vec4 pos;
|
||||
layout(location = 1) in vec3 normal;
|
||||
layout(location = 3) in vec4 multiTexCoord0;
|
||||
layout(location = 6) in vec3 tangent;
|
||||
layout(location = 7) in vec3 binormal;
|
||||
|
||||
out vec2 texCoord;
|
||||
out mat3 TBN;
|
||||
out vec3 ecPos;
|
||||
|
||||
uniform mat4 osg_ModelViewMatrix;
|
||||
uniform mat4 osg_ModelViewProjectionMatrix;
|
||||
uniform mat3 osg_NormalMatrix;
|
||||
|
||||
uniform bool flip_vertically;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = osg_ModelViewProjectionMatrix * pos;
|
||||
texCoord = multiTexCoord0.st;
|
||||
if (flip_vertically)
|
||||
texCoord.y = 1.0 - texCoord.y;
|
||||
|
||||
vec3 T = normalize(osg_NormalMatrix * tangent);
|
||||
vec3 B = normalize(osg_NormalMatrix * binormal);
|
||||
vec3 N = normalize(osg_NormalMatrix * normal);
|
||||
TBN = mat3(T, B, N);
|
||||
|
||||
ecPos = (osg_ModelViewMatrix * pos).xyz;
|
||||
}
|
|
@ -16,7 +16,6 @@ uniform vec4 base_color_factor;
|
|||
uniform float metallic_factor;
|
||||
uniform float roughness_factor;
|
||||
uniform vec3 emissive_factor;
|
||||
uniform float alpha_cutoff;
|
||||
|
||||
vec2 encodeNormal(vec3 n);
|
||||
vec3 decodeSRGB(vec3 screenRGB);
|
||||
|
@ -26,8 +25,6 @@ void main()
|
|||
vec4 baseColorTexel = texture(base_color_tex, texCoord);
|
||||
vec4 baseColor = vec4(decodeSRGB(baseColorTexel.rgb), baseColorTexel.a)
|
||||
* base_color_factor;
|
||||
if (baseColor.a < alpha_cutoff)
|
||||
discard;
|
||||
gbuffer0.rgb = baseColor.rgb;
|
||||
|
||||
float occlusion = texture(occlusion_tex, texCoord).r;
|
||||
|
|
354
Shaders/HDR/lighting-include.frag
Normal file
354
Shaders/HDR/lighting-include.frag
Normal file
|
@ -0,0 +1,354 @@
|
|||
#version 330 core
|
||||
|
||||
uniform sampler2D dfg_lut;
|
||||
uniform samplerCube prefiltered_envmap;
|
||||
uniform sampler2DShadow shadow_tex;
|
||||
|
||||
uniform mat4 fg_LightMatrix_csm0;
|
||||
uniform mat4 fg_LightMatrix_csm1;
|
||||
uniform mat4 fg_LightMatrix_csm2;
|
||||
uniform mat4 fg_LightMatrix_csm3;
|
||||
|
||||
// Shadow mapping constants
|
||||
const int sun_atlas_size = 8192;
|
||||
const float DEPTH_BIAS = 2.0;
|
||||
const float BAND_SIZE = 0.1;
|
||||
const vec2 BAND_BOTTOM_LEFT = vec2(BAND_SIZE);
|
||||
const vec2 BAND_TOP_RIGHT = vec2(1.0 - BAND_SIZE);
|
||||
// Ideally these should be passed as an uniform, but we don't support uniform
|
||||
// arrays yet
|
||||
const vec2 uv_shifts[4] = vec2[4](
|
||||
vec2(0.0, 0.0), vec2(0.5, 0.0),
|
||||
vec2(0.0, 0.5), vec2(0.5, 0.5));
|
||||
const vec2 uv_factor = vec2(0.5, 0.5);
|
||||
|
||||
// BRDF constants
|
||||
const float PI = 3.14159265359;
|
||||
const float RECIPROCAL_PI = 0.31830988618;
|
||||
const float DIELECTRIC_SPECULAR = 0.04;
|
||||
const float MAX_PREFILTERED_LOD = 4.0;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Shadow mapping related stuff
|
||||
|
||||
float sampleOffset(vec4 pos, vec2 offset, vec2 invTexelSize)
|
||||
{
|
||||
return texture(
|
||||
shadow_tex, vec3(
|
||||
pos.xy + offset * invTexelSize,
|
||||
pos.z - DEPTH_BIAS * invTexelSize));
|
||||
}
|
||||
|
||||
// OptimizedPCF from https://github.com/TheRealMJP/Shadows
|
||||
// Original by Ignacio Castaño for The Witness
|
||||
// Released under The MIT License
|
||||
float sampleOptimizedPCF(vec4 pos)
|
||||
{
|
||||
vec2 invTexSize = vec2(1.0 / float(sun_atlas_size));
|
||||
|
||||
vec2 uv = pos.xy * sun_atlas_size;
|
||||
vec2 base_uv = floor(uv + 0.5);
|
||||
float s = (uv.x + 0.5 - base_uv.x);
|
||||
float t = (uv.y + 0.5 - base_uv.y);
|
||||
base_uv -= vec2(0.5);
|
||||
base_uv *= invTexSize;
|
||||
pos.xy = base_uv.xy;
|
||||
|
||||
float sum = 0.0;
|
||||
|
||||
float uw0 = (4.0 - 3.0 * s);
|
||||
float uw1 = 7.0;
|
||||
float uw2 = (1.0 + 3.0 * s);
|
||||
|
||||
float u0 = (3.0 - 2.0 * s) / uw0 - 2.0;
|
||||
float u1 = (3.0 + s) / uw1;
|
||||
float u2 = s / uw2 + 2.0;
|
||||
|
||||
float vw0 = (4.0 - 3.0 * t);
|
||||
float vw1 = 7.0;
|
||||
float vw2 = (1.0 + 3.0 * t);
|
||||
|
||||
float v0 = (3.0 - 2.0 * t) / vw0 - 2.0;
|
||||
float v1 = (3.0 + t) / vw1;
|
||||
float v2 = t / vw2 + 2.0;
|
||||
|
||||
sum += uw0 * vw0 * sampleOffset(pos, vec2(u0, v0), invTexSize);
|
||||
sum += uw1 * vw0 * sampleOffset(pos, vec2(u1, v0), invTexSize);
|
||||
sum += uw2 * vw0 * sampleOffset(pos, vec2(u2, v0), invTexSize);
|
||||
|
||||
sum += uw0 * vw1 * sampleOffset(pos, vec2(u0, v1), invTexSize);
|
||||
sum += uw1 * vw1 * sampleOffset(pos, vec2(u1, v1), invTexSize);
|
||||
sum += uw2 * vw1 * sampleOffset(pos, vec2(u2, v1), invTexSize);
|
||||
|
||||
sum += uw0 * vw2 * sampleOffset(pos, vec2(u0, v2), invTexSize);
|
||||
sum += uw1 * vw2 * sampleOffset(pos, vec2(u1, v2), invTexSize);
|
||||
sum += uw2 * vw2 * sampleOffset(pos, vec2(u2, v2), invTexSize);
|
||||
|
||||
return sum / 144.0;
|
||||
}
|
||||
|
||||
float sampleCascade(vec4 p, vec2 shift)
|
||||
{
|
||||
vec4 pos = p;
|
||||
pos.xy *= uv_factor;
|
||||
pos.xy += shift;
|
||||
return sampleOptimizedPCF(pos);
|
||||
}
|
||||
|
||||
float sampleAndBlendBand(vec4 p1, vec4 p2, vec2 s1, vec2 s2)
|
||||
{
|
||||
vec2 s = smoothstep(vec2(0.0), BAND_BOTTOM_LEFT, p1.xy)
|
||||
- smoothstep(BAND_TOP_RIGHT, vec2(1.0), p1.xy);
|
||||
float blend = 1.0 - s.x * s.y;
|
||||
return mix(sampleCascade(p1, s1),
|
||||
sampleCascade(p2, s2),
|
||||
blend);
|
||||
}
|
||||
|
||||
bool checkWithinBounds(vec2 coords, vec2 bottomLeft, vec2 topRight)
|
||||
{
|
||||
vec2 r = step(bottomLeft, coords) - step(topRight, coords);
|
||||
return bool(r.x * r.y);
|
||||
}
|
||||
|
||||
bool isInsideCascade(vec4 p)
|
||||
{
|
||||
return checkWithinBounds(p.xy, vec2(0.0), vec2(1.0)) && ((p.z / p.w) <= 1.0);
|
||||
}
|
||||
|
||||
bool isInsideBand(vec4 p)
|
||||
{
|
||||
return !checkWithinBounds(p.xy, BAND_BOTTOM_LEFT, BAND_TOP_RIGHT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the light space position of point p.
|
||||
* Both p and n must be in view space. The light matrix is also assumed to
|
||||
* transform from view space to light space.
|
||||
*/
|
||||
vec4 getLightSpacePosition(vec3 p, vec3 n, float NdotL, float bias,
|
||||
mat4 lightMatrix)
|
||||
{
|
||||
float sinTheta = sqrt(1.0 - NdotL * NdotL);
|
||||
vec3 offset = p + n * (sinTheta * bias);
|
||||
return lightMatrix * vec4(offset, 1.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get shadowing factor for a given position. 1.0 corresponds to a fragment
|
||||
* being completely lit, and 0.0 to a fragment being completely in shadow.
|
||||
* Both p and n must be in view space.
|
||||
*/
|
||||
float getShadowing(vec3 p, vec3 n, float NdotL)
|
||||
{
|
||||
// Ignore fragments that don't face the light
|
||||
if (NdotL <= 0.0)
|
||||
return 0.0;
|
||||
|
||||
float shadow = 1.0;
|
||||
|
||||
vec4 lightSpacePos[4];
|
||||
lightSpacePos[0] = getLightSpacePosition(p, n, NdotL, 0.05, fg_LightMatrix_csm0);
|
||||
lightSpacePos[1] = getLightSpacePosition(p, n, NdotL, 0.2, fg_LightMatrix_csm1);
|
||||
lightSpacePos[2] = getLightSpacePosition(p, n, NdotL, 1.0, fg_LightMatrix_csm2);
|
||||
lightSpacePos[3] = getLightSpacePosition(p, n, NdotL, 5.0, fg_LightMatrix_csm3);
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
// Map-based cascade selection
|
||||
// We test if we are inside the cascade bounds to find the tightest
|
||||
// map that contains the fragment.
|
||||
if (isInsideCascade(lightSpacePos[i])) {
|
||||
if (isInsideBand(lightSpacePos[i]) && ((i+1) < 4)) {
|
||||
// Blend between cascades if the fragment is near the
|
||||
// next cascade to avoid abrupt transitions.
|
||||
shadow = clamp(sampleAndBlendBand(lightSpacePos[i],
|
||||
lightSpacePos[i+1],
|
||||
uv_shifts[i],
|
||||
uv_shifts[i+1]),
|
||||
0.0, 1.0);
|
||||
} else {
|
||||
// We are far away from the borders of the cascade, so
|
||||
// we skip the blending to avoid the performance cost
|
||||
// of sampling the shadow map twice.
|
||||
shadow = clamp(sampleCascade(lightSpacePos[i], uv_shifts[i]),
|
||||
0.0, 1.0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return shadow;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// BRDF utility functions
|
||||
|
||||
/**
|
||||
* Fresnel term with included roughness to get a pleasant visual result.
|
||||
* See https://seblagarde.wordpress.com/2011/08/17/hello-world/
|
||||
*/
|
||||
vec3 F_SchlickRoughness(float NdotV, vec3 F0, float r)
|
||||
{
|
||||
return F0 + (max(vec3(1.0 - r), F0) - F0) * pow(max(1.0 - NdotV, 0.0), 5.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fresnel (specular F)
|
||||
* Schlick's approximation for the Cook-Torrance BRDF.
|
||||
*/
|
||||
vec3 F_Schlick(float VdotH, vec3 F0)
|
||||
{
|
||||
return F0 + (vec3(1.0) - F0) * pow(clamp(1.0 - VdotH, 0.0, 1.0), 5.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normal distribution function (NDF) (specular D)
|
||||
* Trowbridge-Reitz/GGX microfacet distribution. Includes Disney's
|
||||
* reparametrization of a=roughness*roughness
|
||||
*/
|
||||
float D_GGX(float NdotH, float a2)
|
||||
{
|
||||
float f = (NdotH * a2 - NdotH) * NdotH + 1.0;
|
||||
return a2 / (PI * f * f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Geometric attenuation (specular G)
|
||||
* Smith-GGX formulation.
|
||||
*/
|
||||
float G_SmithGGX(float NdotV, float NdotL, float a2)
|
||||
{
|
||||
float attV = 2.0 * NdotV / (NdotV + sqrt(a2 + (1.0 - a2) * (NdotV * NdotV)));
|
||||
float attL = 2.0 * NdotL / (NdotL + sqrt(a2 + (1.0 - a2) * (NdotL * NdotL)));
|
||||
return attV * attL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic Lambertian diffuse BRDF
|
||||
*/
|
||||
vec3 Fd_Lambert(vec3 c_diff)
|
||||
{
|
||||
return c_diff * RECIPROCAL_PI;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fresnel reflectance at 0 degrees (light hitting the surface
|
||||
* perpendicularly).
|
||||
*/
|
||||
vec3 getF0Reflectance(vec3 baseColor, float metallic)
|
||||
{
|
||||
return mix(vec3(DIELECTRIC_SPECULAR), baseColor, metallic);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// IBL evaluation
|
||||
|
||||
/**
|
||||
* Indirect diffuse irradiance
|
||||
* To get better results we should be precomputing the irradiance into a cubemap
|
||||
* or calculating spherical harmonics coefficients on the CPU.
|
||||
* Sampling the roughness=1 mipmap level of the prefiltered specular map
|
||||
* works too. :)
|
||||
*/
|
||||
vec3 evaluateDiffuseIrradianceIBL(vec3 n)
|
||||
{
|
||||
int roughnessOneLevel = int(MAX_PREFILTERED_LOD);
|
||||
ivec2 s = textureSize(prefiltered_envmap, roughnessOneLevel);
|
||||
float du = 1.0 / float(s.x);
|
||||
float dv = 1.0 / float(s.y);
|
||||
vec3 m0 = normalize(cross(n, vec3(0.0, 1.0, 0.0)));
|
||||
vec3 m1 = cross(m0, n);
|
||||
vec3 m0du = m0 * du;
|
||||
vec3 m1dv = m1 * dv;
|
||||
|
||||
vec3 c;
|
||||
c = textureLod(prefiltered_envmap, n - m0du - m1dv, roughnessOneLevel).rgb;
|
||||
c += textureLod(prefiltered_envmap, n + m0du - m1dv, roughnessOneLevel).rgb;
|
||||
c += textureLod(prefiltered_envmap, n + m0du + m1dv, roughnessOneLevel).rgb;
|
||||
c += textureLod(prefiltered_envmap, n - m0du + m1dv, roughnessOneLevel).rgb;
|
||||
return c * 0.25;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indirect specular (ambient specular)
|
||||
* Sample from the prefiltered environment map.
|
||||
*/
|
||||
vec3 evaluateSpecularIBL(float NdotV, vec3 reflected, float roughness, vec3 f)
|
||||
{
|
||||
vec3 prefilteredColor = textureLod(prefiltered_envmap,
|
||||
reflected,
|
||||
roughness * MAX_PREFILTERED_LOD).rgb;
|
||||
vec2 envBRDF = texture(dfg_lut, vec2(NdotV, roughness)).rg;
|
||||
return prefilteredColor * (f * envBRDF.x + envBRDF.y);
|
||||
}
|
||||
|
||||
vec3 evaluateIBL(
|
||||
vec3 baseColor,
|
||||
float metallic,
|
||||
float roughness,
|
||||
vec3 f0, // Use getF0Reflectance() to obtain this
|
||||
float occlusion,
|
||||
vec3 nWorldSpace, // Normal in world space
|
||||
float NdotV, // Must be positive and non-zero
|
||||
vec3 reflected // Reflected vector in world space: reflect(-v, n)
|
||||
)
|
||||
{
|
||||
vec3 f = F_SchlickRoughness(NdotV, f0, roughness);
|
||||
|
||||
vec3 specular = evaluateSpecularIBL(NdotV, reflected, roughness, f);
|
||||
vec3 diffuse = evaluateDiffuseIrradianceIBL(nWorldSpace) * baseColor
|
||||
* (vec3(1.0) - f) * (1.0 - metallic);
|
||||
|
||||
return (diffuse + specular) * occlusion;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Analytical light source evaluation
|
||||
|
||||
vec3 evaluateLight(
|
||||
vec3 baseColor,
|
||||
float metallic,
|
||||
float roughness,
|
||||
float clearcoat,
|
||||
float clearcoatRoughness,
|
||||
vec3 f0, // Use getF0Reflectance() to obtain this
|
||||
vec3 intensity,
|
||||
float occlusion,
|
||||
vec3 n,
|
||||
vec3 l,
|
||||
vec3 v,
|
||||
float NdotL, // Must not be clamped to [0,1]
|
||||
float NdotV // Must be positive and non-zero
|
||||
)
|
||||
{
|
||||
// Skip fragments that are completely occluded or that are not facing the light
|
||||
if (occlusion <= 0.0 || NdotL <= 0.0)
|
||||
return vec3(0.0);
|
||||
|
||||
NdotL = clamp(NdotL, 0.001, 1.0);
|
||||
|
||||
vec3 h = normalize(v + l);
|
||||
float NdotH = clamp(dot(n, h), 0.0, 1.0);
|
||||
float VdotH = clamp(dot(v, h), 0.0, 1.0);
|
||||
|
||||
vec3 c_diff = mix(baseColor * (1.0 - DIELECTRIC_SPECULAR), vec3(0.0), metallic);
|
||||
|
||||
float a = roughness * roughness;
|
||||
float a2 = a * a;
|
||||
|
||||
vec3 F = F_Schlick(VdotH, f0);
|
||||
float D = D_GGX(NdotH, a2);
|
||||
float G = G_SmithGGX(NdotV, NdotL, a2);
|
||||
|
||||
// Diffuse term
|
||||
// Lambertian diffuse model
|
||||
vec3 diffuse = (vec3(1.0) - F) * Fd_Lambert(c_diff);
|
||||
// Specular term
|
||||
// Cook-Torrance specular microfacet model
|
||||
vec3 specular = (F * D * G) / (4.0 * NdotV * NdotL);
|
||||
|
||||
vec3 material = diffuse + specular;
|
||||
|
||||
vec3 color = material * intensity * occlusion;
|
||||
return color;
|
||||
}
|
|
@ -9,376 +9,39 @@ uniform sampler2D gbuffer1_tex;
|
|||
uniform sampler2D gbuffer2_tex;
|
||||
uniform sampler2D depth_tex;
|
||||
uniform sampler2D ao_tex;
|
||||
uniform samplerCube prefiltered_envmap;
|
||||
uniform sampler2DShadow shadow_tex;
|
||||
uniform sampler2D dfg_lut;
|
||||
uniform sampler2D aerial_perspective_lut;
|
||||
uniform sampler2D transmittance_lut;
|
||||
|
||||
uniform mat4 fg_ViewMatrix;
|
||||
uniform mat4 fg_ViewMatrixInverse;
|
||||
uniform vec3 fg_SunDirection;
|
||||
uniform vec3 fg_SunDirectionWorld;
|
||||
uniform vec3 fg_CameraPositionCart;
|
||||
uniform vec3 fg_CameraPositionGeod;
|
||||
|
||||
uniform mat4 fg_LightMatrix_csm0;
|
||||
uniform mat4 fg_LightMatrix_csm1;
|
||||
uniform mat4 fg_LightMatrix_csm2;
|
||||
uniform mat4 fg_LightMatrix_csm3;
|
||||
|
||||
const float PI = 3.14159265359;
|
||||
const float RECIPROCAL_PI = 0.31830988618;
|
||||
|
||||
const int sun_atlas_size = 8192;
|
||||
|
||||
const float DEPTH_BIAS = 2.0;
|
||||
const float BAND_SIZE = 0.1;
|
||||
const vec2 BAND_BOTTOM_LEFT = vec2(BAND_SIZE);
|
||||
const vec2 BAND_TOP_RIGHT = vec2(1.0 - BAND_SIZE);
|
||||
|
||||
// Ideally these should be passed as an uniform, but we don't support uniform
|
||||
// arrays yet
|
||||
const vec2 uv_shifts[4] = vec2[4](
|
||||
vec2(0.0, 0.0), vec2(0.5, 0.0),
|
||||
vec2(0.0, 0.5), vec2(0.5, 0.5));
|
||||
const vec2 uv_factor = vec2(0.5, 0.5);
|
||||
|
||||
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 float MAX_PREFILTERED_LOD = 4.0;
|
||||
|
||||
const float ATMOSPHERE_RADIUS = 6471e3;
|
||||
const vec3 EXTRATERRESTRIAL_SOLAR_ILLUMINANCE = vec3(128.0);
|
||||
|
||||
vec3 decodeNormal(vec2 enc);
|
||||
vec3 positionFromDepth(vec2 pos, float depth);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Shadow mapping related stuff
|
||||
|
||||
float sampleOffset(vec4 pos, vec2 offset, vec2 invTexelSize)
|
||||
{
|
||||
return texture(
|
||||
shadow_tex, vec3(
|
||||
pos.xy + offset * invTexelSize,
|
||||
pos.z - DEPTH_BIAS * invTexelSize));
|
||||
}
|
||||
|
||||
// OptimizedPCF from https://github.com/TheRealMJP/Shadows
|
||||
// Original by Ignacio Castaño for The Witness
|
||||
// Released under The MIT License
|
||||
float sampleOptimizedPCF(vec4 pos)
|
||||
{
|
||||
vec2 invTexSize = vec2(1.0 / float(sun_atlas_size));
|
||||
|
||||
vec2 uv = pos.xy * sun_atlas_size;
|
||||
vec2 base_uv = floor(uv + 0.5);
|
||||
float s = (uv.x + 0.5 - base_uv.x);
|
||||
float t = (uv.y + 0.5 - base_uv.y);
|
||||
base_uv -= vec2(0.5);
|
||||
base_uv *= invTexSize;
|
||||
pos.xy = base_uv.xy;
|
||||
|
||||
float sum = 0.0;
|
||||
|
||||
float uw0 = (4.0 - 3.0 * s);
|
||||
float uw1 = 7.0;
|
||||
float uw2 = (1.0 + 3.0 * s);
|
||||
|
||||
float u0 = (3.0 - 2.0 * s) / uw0 - 2.0;
|
||||
float u1 = (3.0 + s) / uw1;
|
||||
float u2 = s / uw2 + 2.0;
|
||||
|
||||
float vw0 = (4.0 - 3.0 * t);
|
||||
float vw1 = 7.0;
|
||||
float vw2 = (1.0 + 3.0 * t);
|
||||
|
||||
float v0 = (3.0 - 2.0 * t) / vw0 - 2.0;
|
||||
float v1 = (3.0 + t) / vw1;
|
||||
float v2 = t / vw2 + 2.0;
|
||||
|
||||
sum += uw0 * vw0 * sampleOffset(pos, vec2(u0, v0), invTexSize);
|
||||
sum += uw1 * vw0 * sampleOffset(pos, vec2(u1, v0), invTexSize);
|
||||
sum += uw2 * vw0 * sampleOffset(pos, vec2(u2, v0), invTexSize);
|
||||
|
||||
sum += uw0 * vw1 * sampleOffset(pos, vec2(u0, v1), invTexSize);
|
||||
sum += uw1 * vw1 * sampleOffset(pos, vec2(u1, v1), invTexSize);
|
||||
sum += uw2 * vw1 * sampleOffset(pos, vec2(u2, v1), invTexSize);
|
||||
|
||||
sum += uw0 * vw2 * sampleOffset(pos, vec2(u0, v2), invTexSize);
|
||||
sum += uw1 * vw2 * sampleOffset(pos, vec2(u1, v2), invTexSize);
|
||||
sum += uw2 * vw2 * sampleOffset(pos, vec2(u2, v2), invTexSize);
|
||||
|
||||
return sum / 144.0;
|
||||
}
|
||||
|
||||
float sampleCascade(vec4 p, vec2 shift)
|
||||
{
|
||||
vec4 pos = p;
|
||||
pos.xy *= uv_factor;
|
||||
pos.xy += shift;
|
||||
return sampleOptimizedPCF(pos);
|
||||
}
|
||||
|
||||
float sampleAndBlendBand(vec4 p1, vec4 p2, vec2 s1, vec2 s2)
|
||||
{
|
||||
vec2 s = smoothstep(vec2(0.0), BAND_BOTTOM_LEFT, p1.xy)
|
||||
- smoothstep(BAND_TOP_RIGHT, vec2(1.0), p1.xy);
|
||||
float blend = 1.0 - s.x * s.y;
|
||||
return mix(sampleCascade(p1, s1),
|
||||
sampleCascade(p2, s2),
|
||||
blend);
|
||||
}
|
||||
|
||||
bool checkWithinBounds(vec2 coords, vec2 bottomLeft, vec2 topRight)
|
||||
{
|
||||
vec2 r = step(bottomLeft, coords) - step(topRight, coords);
|
||||
return bool(r.x * r.y);
|
||||
}
|
||||
|
||||
bool isInsideCascade(vec4 p)
|
||||
{
|
||||
return checkWithinBounds(p.xy, vec2(0.0), vec2(1.0)) && ((p.z / p.w) <= 1.0);
|
||||
}
|
||||
|
||||
bool isInsideBand(vec4 p)
|
||||
{
|
||||
return !checkWithinBounds(p.xy, BAND_BOTTOM_LEFT, BAND_TOP_RIGHT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the light space position of point p.
|
||||
* Both p and n must be in view space. The light matrix is also assumed to
|
||||
* transform from view space to light space.
|
||||
*/
|
||||
vec4 getLightSpacePosition(vec3 p, vec3 n, float NdotL, float bias,
|
||||
mat4 lightMatrix)
|
||||
{
|
||||
float sinTheta = sqrt(1.0 - NdotL * NdotL);
|
||||
vec3 offset = p + n * (sinTheta * bias);
|
||||
return lightMatrix * vec4(offset, 1.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get shadowing factor for a given position. 1.0 corresponds to a fragment
|
||||
* being completely lit, and 0.0 to a fragment being completely in shadow.
|
||||
* Both p and n must be in view space.
|
||||
*/
|
||||
float getShadowing(vec3 p, vec3 n, float NdotL)
|
||||
{
|
||||
float shadow = 1.0;
|
||||
|
||||
vec4 lightSpacePos[4];
|
||||
lightSpacePos[0] = getLightSpacePosition(p, n, NdotL, 0.05, fg_LightMatrix_csm0);
|
||||
lightSpacePos[1] = getLightSpacePosition(p, n, NdotL, 0.2, fg_LightMatrix_csm1);
|
||||
lightSpacePos[2] = getLightSpacePosition(p, n, NdotL, 1.0, fg_LightMatrix_csm2);
|
||||
lightSpacePos[3] = getLightSpacePosition(p, n, NdotL, 5.0, fg_LightMatrix_csm3);
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
// Map-based cascade selection
|
||||
// We test if we are inside the cascade bounds to find the tightest
|
||||
// map that contains the fragment.
|
||||
if (isInsideCascade(lightSpacePos[i])) {
|
||||
if (isInsideBand(lightSpacePos[i]) && ((i+1) < 4)) {
|
||||
// Blend between cascades if the fragment is near the
|
||||
// next cascade to avoid abrupt transitions.
|
||||
shadow = clamp(sampleAndBlendBand(lightSpacePos[i],
|
||||
lightSpacePos[i+1],
|
||||
uv_shifts[i],
|
||||
uv_shifts[i+1]),
|
||||
0.0, 1.0);
|
||||
} else {
|
||||
// We are far away from the borders of the cascade, so
|
||||
// we skip the blending to avoid the performance cost
|
||||
// of sampling the shadow map twice.
|
||||
shadow = clamp(sampleCascade(lightSpacePos[i], uv_shifts[i]),
|
||||
0.0, 1.0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return shadow;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// BRDF related stuff
|
||||
|
||||
/**
|
||||
* Indirect diffuse irradiance
|
||||
* To get better results we should be precomputing the irradiance into a cubemap
|
||||
* or calculating spherical harmonics coefficients on the CPU.
|
||||
* Sampling the roughness=1 mipmap level of the prefiltered specular map
|
||||
* works too. :)
|
||||
*/
|
||||
vec3 IBL_DiffuseIrradiance(vec3 n)
|
||||
{
|
||||
vec4 worldSpaceNormal = fg_ViewMatrixInverse * vec4(n, 0.0);
|
||||
vec3 coord = worldSpaceNormal.xyz;
|
||||
|
||||
int roughnessOneLevel = int(MAX_PREFILTERED_LOD);
|
||||
ivec2 s = textureSize(prefiltered_envmap, roughnessOneLevel);
|
||||
float du = 1.0 / float(s.x);
|
||||
float dv = 1.0 / float(s.y);
|
||||
vec3 m0 = normalize(cross(n, vec3(0.0, 1.0, 0.0)));
|
||||
vec3 m1 = cross(m0, n);
|
||||
vec3 m0du = m0 * du;
|
||||
vec3 m1dv = m1 * dv;
|
||||
|
||||
vec3 c;
|
||||
c = textureLod(prefiltered_envmap, coord - m0du - m1dv, roughnessOneLevel).rgb;
|
||||
c += textureLod(prefiltered_envmap, coord + m0du - m1dv, roughnessOneLevel).rgb;
|
||||
c += textureLod(prefiltered_envmap, coord + m0du + m1dv, roughnessOneLevel).rgb;
|
||||
c += textureLod(prefiltered_envmap, coord - m0du + m1dv, roughnessOneLevel).rgb;
|
||||
return c * 0.25;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indirect specular (ambient specular)
|
||||
* Sample from the prefiltered environment map.
|
||||
*/
|
||||
vec3 IBL_Specular(vec3 n, vec3 v, float NdotV, float roughness, vec3 F)
|
||||
{
|
||||
vec4 reflectVec = vec4(reflect(-v, n), 0.0);
|
||||
vec4 worldReflectVec = fg_ViewMatrixInverse * reflectVec;
|
||||
|
||||
vec3 prefilteredColor = textureLod(prefiltered_envmap,
|
||||
worldReflectVec.xyz,
|
||||
roughness * MAX_PREFILTERED_LOD).rgb;
|
||||
|
||||
vec2 envBRDF = texture(dfg_lut, vec2(NdotV, roughness)).rg;
|
||||
|
||||
return prefilteredColor * (F * envBRDF.x + envBRDF.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fresnel term with included roughness to get a pleasant visual result.
|
||||
* See https://seblagarde.wordpress.com/2011/08/17/hello-world/
|
||||
*/
|
||||
vec3 F_SchlickRoughness(float NdotV, vec3 F0, float r)
|
||||
{
|
||||
return F0 + (max(vec3(1.0 - r), F0) - F0) * pow(max(1.0 - NdotV, 0.0), 5.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fresnel (specular F)
|
||||
* Schlick's approximation for the Cook-Torrance BRDF.
|
||||
*/
|
||||
vec3 F_Schlick(float VdotH, vec3 F0)
|
||||
{
|
||||
return F0 + (vec3(1.0) - F0) * pow(clamp(1.0 - VdotH, 0.0, 1.0), 5.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normal distribution function (NDF) (specular D)
|
||||
* Trowbridge-Reitz/GGX microfacet distribution. Includes Disney's
|
||||
* reparametrization of a=roughness*roughness
|
||||
*/
|
||||
float D_GGX(float NdotH, float a2)
|
||||
{
|
||||
float f = (NdotH * a2 - NdotH) * NdotH + 1.0;
|
||||
return a2 / (PI * f * f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Geometric attenuation (specular G)
|
||||
* Smith-GGX formulation.
|
||||
*/
|
||||
float G_SmithGGX(float NdotV, float NdotL, float a2)
|
||||
{
|
||||
float attV = 2.0 * NdotV / (NdotV + sqrt(a2 + (1.0 - a2) * (NdotV * NdotV)));
|
||||
float attL = 2.0 * NdotL / (NdotL + sqrt(a2 + (1.0 - a2) * (NdotL * NdotL)));
|
||||
return attV * attL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic Lambertian diffuse BRDF
|
||||
*/
|
||||
vec3 BRDF_Diffuse_Lambert(vec3 c_diff)
|
||||
{
|
||||
return c_diff * RECIPROCAL_PI;
|
||||
}
|
||||
|
||||
vec3 BRDF(in vec3 albedo, in float metalness, in float roughness,
|
||||
in float clearcoat, in float clearcoatRoughness,
|
||||
in float NdotL, in float NdotV, in float NdotH, in float VdotH,
|
||||
out vec3 f0)
|
||||
{
|
||||
const float dielectricSpecular = 0.04;
|
||||
vec3 c_diff = mix(albedo * (1.0 - dielectricSpecular), vec3(0.0), metalness);
|
||||
f0 = mix(vec3(dielectricSpecular), albedo, metalness);
|
||||
|
||||
float a = roughness * roughness;
|
||||
float a2 = a * a;
|
||||
|
||||
vec3 F = F_Schlick(VdotH, f0);
|
||||
float D = D_GGX(NdotH, a2);
|
||||
float G = G_SmithGGX(NdotV, NdotL, a2);
|
||||
|
||||
// Diffuse term
|
||||
// Lambertian diffuse model
|
||||
vec3 f_diffuse = (vec3(1.0) - F) * BRDF_Diffuse_Lambert(c_diff);
|
||||
|
||||
// Specular term
|
||||
// Cook-Torrance specular microfacet model
|
||||
vec3 f_specular = F * D * G / (4.0 * NdotV * NdotL);
|
||||
|
||||
return f_diffuse + f_specular;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Atmospheric scattering
|
||||
|
||||
vec4 sampleAerialPerspectiveSlice(int slice)
|
||||
{
|
||||
// Sample at the pixel center
|
||||
float offset = slice * AERIAL_LUT_TILE_SIZE + AERIAL_LUT_TEXEL_SIZE * 0.5;
|
||||
float x = texCoord.x * (AERIAL_LUT_TILE_SIZE - AERIAL_LUT_TEXEL_SIZE) + offset;
|
||||
return texture(aerial_perspective_lut, vec2(x, texCoord.y));
|
||||
}
|
||||
|
||||
vec4 sampleAerialPerspective(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) {
|
||||
// Handle special case of fragments behind the first slice
|
||||
color = mix(vec4(0.0, 0.0, 0.0, 1.0), sampleAerialPerspectiveSlice(0), w);
|
||||
} else {
|
||||
w -= 1.0;
|
||||
// Manually interpolate between slices
|
||||
color = mix(sampleAerialPerspectiveSlice(int(floor(w))),
|
||||
sampleAerialPerspectiveSlice(int(ceil(w))),
|
||||
sqrt(fract(w)));
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
vec3 getSunIlluminance()
|
||||
{
|
||||
float cameraHeight = length(fg_CameraPositionCart);
|
||||
vec3 up = fg_CameraPositionCart / cameraHeight;
|
||||
float cosTheta = dot(fg_SunDirectionWorld, up);
|
||||
|
||||
float earthRadius = cameraHeight - max(fg_CameraPositionGeod.z, 0.0);
|
||||
float normalizedHeight = (cameraHeight - earthRadius)
|
||||
/ (ATMOSPHERE_RADIUS - earthRadius);
|
||||
|
||||
vec2 coords = vec2(cosTheta * 0.5 + 0.5, clamp(normalizedHeight, 0.0, 1.0));
|
||||
vec3 transmittance = texture(transmittance_lut, coords).rgb;
|
||||
|
||||
return EXTRATERRESTRIAL_SOLAR_ILLUMINANCE * transmittance;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
vec3 getF0Reflectance(vec3 baseColor, float metallic);
|
||||
float getShadowing(vec3 p, vec3 n, float NdotL);
|
||||
vec3 evaluateLight(
|
||||
vec3 baseColor,
|
||||
float metallic,
|
||||
float roughness,
|
||||
float clearcoat,
|
||||
float clearcoatRoughness,
|
||||
vec3 f0,
|
||||
vec3 intensity,
|
||||
float occlusion,
|
||||
vec3 n,
|
||||
vec3 l,
|
||||
vec3 v,
|
||||
float NdotL,
|
||||
float NdotV);
|
||||
vec3 evaluateIBL(
|
||||
vec3 baseColor,
|
||||
float metallic,
|
||||
float roughness,
|
||||
vec3 f0,
|
||||
float occlusion,
|
||||
vec3 nWorldSpace,
|
||||
float NdotV,
|
||||
vec3 reflected);
|
||||
vec3 addAerialPerspective(vec3 color, vec2 coord, float depth);
|
||||
vec3 getSunIntensity();
|
||||
|
||||
void main()
|
||||
{
|
||||
|
@ -394,42 +57,48 @@ void main()
|
|||
vec3 pos = positionFromDepth(texCoord, depth);
|
||||
vec3 v = normalize(-pos);
|
||||
vec3 n = decodeNormal(gbuffer1);
|
||||
vec3 l = fg_SunDirection;
|
||||
|
||||
vec3 albedo = gbuffer0.rgb;
|
||||
float NdotL = dot(n, l);
|
||||
float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
|
||||
|
||||
vec3 baseColor = gbuffer0.rgb;
|
||||
float cavity = gbuffer0.a;
|
||||
float metalness = gbuffer2.r;
|
||||
float metallic = gbuffer2.r;
|
||||
float roughness = gbuffer2.g;
|
||||
float clearcoat = gbuffer2.b;
|
||||
float clearcoatRoughness = gbuffer2.a;
|
||||
|
||||
vec3 l = fg_SunDirection;
|
||||
vec3 h = normalize(v + l);
|
||||
vec3 f0 = getF0Reflectance(baseColor, metallic);
|
||||
|
||||
float NdotL = clamp(dot(n, l), 0.001, 1.0);
|
||||
float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
|
||||
float NdotH = clamp(dot(n, h), 0.0, 1.0);
|
||||
float VdotH = clamp(dot(v, h), 0.0, 1.0);
|
||||
|
||||
vec3 f0;
|
||||
vec3 brdf = BRDF(albedo, metalness, roughness,
|
||||
clearcoat, clearcoatRoughness,
|
||||
NdotL, NdotV, NdotH, VdotH,
|
||||
f0);
|
||||
|
||||
vec3 sunIlluminance = getSunIlluminance() * NdotL;
|
||||
|
||||
vec3 f = F_SchlickRoughness(NdotV, f0, roughness);
|
||||
vec3 indirectSpecular = IBL_Specular(n, v, NdotV, roughness, f);
|
||||
vec3 indirectDiffuse = IBL_DiffuseIrradiance(n) * albedo
|
||||
* (vec3(1.0) - f) * (1.0 - metalness);
|
||||
|
||||
vec3 ambient = (indirectDiffuse + indirectSpecular) * ao * cavity;
|
||||
vec3 sunIlluminance = getSunIntensity() * clamp(NdotL, 0.0, 1.0);
|
||||
float shadowFactor = getShadowing(pos, n, NdotL);
|
||||
vec3 color = ambient + brdf * sunIlluminance * shadowFactor;
|
||||
|
||||
vec4 aerialPerspective = sampleAerialPerspective(length(pos));
|
||||
color = color * aerialPerspective.a + aerialPerspective.rgb
|
||||
* EXTRATERRESTRIAL_SOLAR_ILLUMINANCE;
|
||||
vec3 color = evaluateLight(baseColor,
|
||||
metallic,
|
||||
roughness,
|
||||
clearcoat,
|
||||
clearcoatRoughness,
|
||||
f0,
|
||||
sunIlluminance,
|
||||
shadowFactor,
|
||||
n, l, v,
|
||||
NdotL, NdotV);
|
||||
|
||||
float ambientOcclusion = ao * cavity;
|
||||
vec3 worldNormal = (fg_ViewMatrixInverse * vec4(n, 0.0)).xyz;
|
||||
vec3 worldReflected = (fg_ViewMatrixInverse * vec4(reflect(-v, n), 0.0)).xyz;
|
||||
|
||||
color += evaluateIBL(baseColor,
|
||||
metallic,
|
||||
roughness,
|
||||
f0,
|
||||
ambientOcclusion,
|
||||
worldNormal,
|
||||
NdotV,
|
||||
worldNormal);
|
||||
|
||||
color = addAerialPerspective(color, texCoord, length(pos));
|
||||
|
||||
fragHdrColor = color;
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ void main()
|
|||
float altitude = length(groundPoint);
|
||||
float scaledAltitude = altitude / 100000.0;
|
||||
|
||||
vec3 up = normalize(vec4(0.0, 0.0, 0.0, 1.0) - groundPoint).xyz;
|
||||
vec3 up = normalize(vec3(0.0, 0.0, 0.0) - groundPoint.xyz);
|
||||
float sunZenithCosTheta = dot(rayDirView, up);
|
||||
|
||||
vec2 coords = vec2(sunZenithCosTheta * 0.5 + 0.5,
|
||||
|
|
Loading…
Add table
Reference in a new issue