1
0
Fork 0

HDR: Add support for glTF transparent objects

This commit is contained in:
Fernando García Liñán 2021-08-19 01:22:26 +02:00
parent 0c82a1f59a
commit 212f8fdab9
13 changed files with 863 additions and 430 deletions

View file

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

View file

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

View file

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

View 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>

View file

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

View file

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

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

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

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

View file

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

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

View file

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

View file

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