diff --git a/Compositor/HDR/env-capture-pass.xml b/Compositor/HDR/env-capture-pass.xml index 2abcdad44..403051d22 100644 --- a/Compositor/HDR/env-capture-pass.xml +++ b/Compositor/HDR/env-capture-pass.xml @@ -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> diff --git a/Compositor/HDR/hdr.xml b/Compositor/HDR/hdr.xml index fc26a7e30..df2eb8bbe 100644 --- a/Compositor/HDR/hdr.xml +++ b/Compositor/HDR/hdr.xml @@ -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> diff --git a/Effects/HDR/lighting.eff b/Effects/HDR/lighting.eff index cc129bbba..8c4e4675d 100644 --- a/Effects/HDR/lighting.eff +++ b/Effects/HDR/lighting.eff @@ -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> diff --git a/Effects/model-pbr-transparent.eff b/Effects/model-pbr-transparent.eff new file mode 100644 index 000000000..225552fa3 --- /dev/null +++ b/Effects/model-pbr-transparent.eff @@ -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> diff --git a/Effects/model-pbr.eff b/Effects/model-pbr.eff index 5ea62b6e7..a000eea4e 100644 --- a/Effects/model-pbr.eff +++ b/Effects/model-pbr.eff @@ -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> diff --git a/Effects/skydome.eff b/Effects/skydome.eff index f853cf769..03de89cc0 100644 --- a/Effects/skydome.eff +++ b/Effects/skydome.eff @@ -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> diff --git a/Shaders/HDR/aerial-perspective-include.frag b/Shaders/HDR/aerial-perspective-include.frag new file mode 100644 index 000000000..13062e63d --- /dev/null +++ b/Shaders/HDR/aerial-perspective-include.frag @@ -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; +} diff --git a/Shaders/HDR/geometry-pbr-transparent.frag b/Shaders/HDR/geometry-pbr-transparent.frag new file mode 100644 index 000000000..0de99bbe3 --- /dev/null +++ b/Shaders/HDR/geometry-pbr-transparent.frag @@ -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); +} diff --git a/Shaders/HDR/geometry-pbr-transparent.vert b/Shaders/HDR/geometry-pbr-transparent.vert new file mode 100644 index 000000000..23179eb19 --- /dev/null +++ b/Shaders/HDR/geometry-pbr-transparent.vert @@ -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; +} diff --git a/Shaders/HDR/geometry-pbr.frag b/Shaders/HDR/geometry-pbr.frag index ce486ab5e..7f9b70116 100644 --- a/Shaders/HDR/geometry-pbr.frag +++ b/Shaders/HDR/geometry-pbr.frag @@ -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; diff --git a/Shaders/HDR/lighting-include.frag b/Shaders/HDR/lighting-include.frag new file mode 100644 index 000000000..816f2120a --- /dev/null +++ b/Shaders/HDR/lighting-include.frag @@ -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; +} diff --git a/Shaders/HDR/lighting.frag b/Shaders/HDR/lighting.frag index 5de8a01ae..4f834118c 100644 --- a/Shaders/HDR/lighting.frag +++ b/Shaders/HDR/lighting.frag @@ -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; } diff --git a/Shaders/HDR/skydome.frag b/Shaders/HDR/skydome.frag index 9592f62d2..0e293bd20 100644 --- a/Shaders/HDR/skydome.frag +++ b/Shaders/HDR/skydome.frag @@ -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,