1
0
Fork 0

HDR: Improve envmap pre-filtering

This commit is contained in:
Fernando García Liñán 2021-07-31 14:36:02 +02:00
parent 5c1c28c536
commit 526491790e
10 changed files with 177 additions and 62 deletions

View file

@ -4,6 +4,7 @@
<type>scene</type>
<implicit-attachment-mask>depth</implicit-attachment-mask>
<effect-scheme>hdr-envmap</effect-scheme>
<clear-mask>depth</clear-mask>
<!-- Only render the skydome and terrain -->
<!-- TODO: Explicitly select the LOD level -->
<cull-mask>0x800</cull-mask>

View file

@ -123,6 +123,18 @@
<wrap-t>clamp-to-edge</wrap-t>
<mipmap-levels>5</mipmap-levels>
</buffer>
<buffer>
<name>prefiltered-envmap</name>
<type>cubemap</type>
<width>128</width>
<height>128</height>
<format>rgb16f</format>
<min-filter>linear-mipmap-linear</min-filter>
<mag-filter>linear</mag-filter>
<wrap-s>clamp-to-edge</wrap-s>
<wrap-t>clamp-to-edge</wrap-t>
<mipmap-levels>5</mipmap-levels>
</buffer>
<!-- Shadow map atlas -->
<buffer>
@ -305,7 +317,6 @@
<component>color0</component>
<buffer>envmap</buffer>
<face>0</face>
<level>0</level>
</attachment>
</pass>
<pass include="env-capture-pass.xml">
@ -315,7 +326,6 @@
<component>color0</component>
<buffer>envmap</buffer>
<face>1</face>
<level>0</level>
</attachment>
</pass>
<pass include="env-capture-pass.xml">
@ -325,7 +335,6 @@
<component>color0</component>
<buffer>envmap</buffer>
<face>2</face>
<level>0</level>
</attachment>
</pass>
<pass include="env-capture-pass.xml">
@ -335,7 +344,6 @@
<component>color0</component>
<buffer>envmap</buffer>
<face>3</face>
<level>0</level>
</attachment>
</pass>
<pass include="env-capture-pass.xml">
@ -345,7 +353,6 @@
<component>color0</component>
<buffer>envmap</buffer>
<face>4</face>
<level>0</level>
</attachment>
</pass>
<pass include="env-capture-pass.xml">
@ -355,7 +362,8 @@
<component>color0</component>
<buffer>envmap</buffer>
<face>5</face>
<level>0</level>
<!-- Generate the mips after writing to the last face -->
<mipmap-generation>true</mipmap-generation>
</attachment>
</pass>
@ -364,169 +372,207 @@
We convolve the cubemap for five roughness values and store the results
on the mipmap levels of the cubemap. Later passes will choose which mipmap
level to use for reflections based on the roughness of the surface that's
being lighted/rendered.
being lighted/rendered. The first mipmap level can just be copied from the
original envmap as we'd like it to contain perfect mirror reflections.
Diffuse lighting is approximated by using the highest mipmap level
(roughness=1).
-->
<pass include="env-prefilter-pass.xml">
<name>env-prefilter0</name>
<effect>Effects/HDR/envmap-copy</effect>
<attachment>
<component>color0</component>
<buffer>prefiltered-envmap</buffer>
<face>0</face>
<level>0</level>
</attachment>
<attachment>
<component>color1</component>
<buffer>prefiltered-envmap</buffer>
<face>1</face>
<level>0</level>
</attachment>
<attachment>
<component>color2</component>
<buffer>prefiltered-envmap</buffer>
<face>2</face>
<level>0</level>
</attachment>
<attachment>
<component>color3</component>
<buffer>prefiltered-envmap</buffer>
<face>3</face>
<level>0</level>
</attachment>
<attachment>
<component>color4</component>
<buffer>prefiltered-envmap</buffer>
<face>4</face>
<level>0</level>
</attachment>
<attachment>
<component>color5</component>
<buffer>prefiltered-envmap</buffer>
<face>5</face>
<level>0</level>
</attachment>
</pass>
<pass include="env-prefilter-pass.xml">
<name>env-prefilter1</name>
<effect>Effects/HDR/envmap-prefilter1</effect>
<attachment>
<component>color0</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>0</face>
<level>1</level>
</attachment>
<attachment>
<component>color1</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>1</face>
<level>1</level>
</attachment>
<attachment>
<component>color2</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>2</face>
<level>1</level>
</attachment>
<attachment>
<component>color3</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>3</face>
<level>1</level>
</attachment>
<attachment>
<component>color4</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>4</face>
<level>1</level>
</attachment>
<attachment>
<component>color5</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>5</face>
<level>1</level>
</attachment>
</pass>
<pass include="env-prefilter-pass.xml">
<name>env-prefilter2</name>
<effect>Effects/HDR/envmap-prefilter2</effect>
<attachment>
<component>color0</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>0</face>
<level>2</level>
</attachment>
<attachment>
<component>color1</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>1</face>
<level>2</level>
</attachment>
<attachment>
<component>color2</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>2</face>
<level>2</level>
</attachment>
<attachment>
<component>color3</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>3</face>
<level>2</level>
</attachment>
<attachment>
<component>color4</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>4</face>
<level>2</level>
</attachment>
<attachment>
<component>color5</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>5</face>
<level>2</level>
</attachment>
</pass>
<pass include="env-prefilter-pass.xml">
<name>env-prefilter3</name>
<effect>Effects/HDR/envmap-prefilter3</effect>
<attachment>
<component>color0</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>0</face>
<level>3</level>
</attachment>
<attachment>
<component>color1</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>1</face>
<level>3</level>
</attachment>
<attachment>
<component>color2</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>2</face>
<level>3</level>
</attachment>
<attachment>
<component>color3</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>3</face>
<level>3</level>
</attachment>
<attachment>
<component>color4</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>4</face>
<level>3</level>
</attachment>
<attachment>
<component>color5</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>5</face>
<level>3</level>
</attachment>
</pass>
<pass include="env-prefilter-pass.xml">
<name>env-prefilter4</name>
<effect>Effects/HDR/envmap-prefilter4</effect>
<attachment>
<component>color0</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>0</face>
<level>4</level>
</attachment>
<attachment>
<component>color1</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>1</face>
<level>4</level>
</attachment>
<attachment>
<component>color2</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>2</face>
<level>4</level>
</attachment>
<attachment>
<component>color3</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>3</face>
<level>4</level>
</attachment>
<attachment>
<component>color4</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>4</face>
<level>4</level>
</attachment>
<attachment>
<component>color5</component>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
<face>5</face>
<level>4</level>
</attachment>
@ -662,7 +708,7 @@
</binding>
<binding>
<unit>9</unit>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
</binding>
<binding>
<unit>10</unit>
@ -695,7 +741,7 @@
<effect-scheme>hdr-forward</effect-scheme>
<binding>
<unit>9</unit>
<buffer>envmap</buffer>
<buffer>prefiltered-envmap</buffer>
</binding>
<binding>
<unit>11</unit>

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<PropertyList>
<name>Effects/HDR/envmap-copy</name>
<technique n="1">
<pass>
<program>
<vertex-shader>Shaders/HDR/envmap-prefilter.vert</vertex-shader>
<fragment-shader>Shaders/HDR/envmap-copy.frag</fragment-shader>
</program>
<uniform>
<name>envmap</name>
<type>sampler-cube</type>
<value type="int">0</value>
</uniform>
</pass>
</technique>
</PropertyList>

View file

@ -3,6 +3,7 @@
<name>Effects/HDR/envmap-prefilter</name>
<parameters>
<roughness type="float">0.0</roughness>
<num-samples type="int">1</num-samples>
</parameters>
<technique n="1">
<pass>
@ -20,6 +21,11 @@
<type>float</type>
<value><use>roughness</use></value>
</uniform>
<uniform>
<name>num_samples</name>
<type>int</type>
<value><use>num-samples</use></value>
</uniform>
</pass>
</technique>
</PropertyList>

View file

@ -4,5 +4,6 @@
<inherits-from>Effects/HDR/envmap-prefilter</inherits-from>
<parameters>
<roughness type="float">0.25</roughness>
<num-samples type="int">8</num-samples>
</parameters>
</PropertyList>

View file

@ -4,5 +4,6 @@
<inherits-from>Effects/HDR/envmap-prefilter</inherits-from>
<parameters>
<roughness type="float">0.5</roughness>
<num-samples type="int">32</num-samples>
</parameters>
</PropertyList>

View file

@ -4,5 +4,6 @@
<inherits-from>Effects/HDR/envmap-prefilter</inherits-from>
<parameters>
<roughness type="float">0.75</roughness>
<num-samples type="int">64</num-samples>
</parameters>
</PropertyList>

View file

@ -4,5 +4,6 @@
<inherits-from>Effects/HDR/envmap-prefilter</inherits-from>
<parameters>
<roughness type="float">1.0</roughness>
<num-samples type="int">128</num-samples>
</parameters>
</PropertyList>

View file

@ -0,0 +1,27 @@
#version 330 core
layout(location = 0) out vec3 fragColor0;
layout(location = 1) out vec3 fragColor1;
layout(location = 2) out vec3 fragColor2;
layout(location = 3) out vec3 fragColor3;
layout(location = 4) out vec3 fragColor4;
layout(location = 5) out vec3 fragColor5;
in vec3 cubemapCoord0;
in vec3 cubemapCoord1;
in vec3 cubemapCoord2;
in vec3 cubemapCoord3;
in vec3 cubemapCoord4;
in vec3 cubemapCoord5;
uniform samplerCube envmap;
void main()
{
fragColor0 = textureLod(envmap, cubemapCoord0, 0.0).rgb;
fragColor1 = textureLod(envmap, cubemapCoord1, 0.0).rgb;
fragColor2 = textureLod(envmap, cubemapCoord2, 0.0).rgb;
fragColor3 = textureLod(envmap, cubemapCoord3, 0.0).rgb;
fragColor4 = textureLod(envmap, cubemapCoord4, 0.0).rgb;
fragColor5 = textureLod(envmap, cubemapCoord5, 0.0).rgb;
}

View file

@ -1,3 +1,6 @@
// Mostly based on 'Moving Frostbite to Physically Based Rendering'
// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
#version 330 core
layout(location = 0) out vec3 fragColor0;
@ -16,11 +19,11 @@ in vec3 cubemapCoord5;
uniform samplerCube envmap;
uniform float roughness;
uniform int fg_CubemapFace;
uniform int num_samples;
const float PI = 3.14159265359;
const uint NUM_SAMPLES = 64u;
const float ENVMAP_SIZE = 128.0;
const float ENVMAP_MIP_COUNT = 4.0;
float RadicalInverse_VdC(uint bits)
{
@ -37,57 +40,68 @@ vec2 Hammersley(uint i, uint N)
return vec2(float(i)/float(N), RadicalInverse_VdC(i));
}
vec3 ImportanceSampleGGX(vec2 Xi, vec3 n, float r)
vec3 ImportanceSampleGGX(vec2 Xi, vec3 n, float a)
{
float a = r*r;
float phi = 2.0 * PI * Xi.x;
float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y));
float sinTheta = sqrt(1.0 - cosTheta*cosTheta);
vec3 h;
h.x = sinTheta * cos(phi);
h.y = sinTheta * sin(phi);
h.z = cosTheta;
return h;
}
vec3 up = abs(n.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
vec3 tangent = normalize(cross(up, n));
vec3 bitangent = cross(n, tangent);
vec3 sampleVec = tangent * h.x + bitangent * h.y + n * h.z;
return normalize(sampleVec);
float D_GGX(float NdotH, float a2)
{
float f = (NdotH * a2 - NdotH) * NdotH + 1.0;
return a2 / (PI * f * f);
}
vec3 prefilter(vec3 n)
{
n = normalize(n);
vec3 v = n; // n = v simplification
float a = roughness*roughness;
vec3 prefilteredColor = vec3(0.0);
float totalWeight = 0.0;
for (uint i = 0u; i < NUM_SAMPLES; ++i) {
vec2 Xi = Hammersley(i, NUM_SAMPLES);
vec3 h = ImportanceSampleGGX(Xi, n, roughness);
vec3 up = abs(n.z) < 0.999f ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
vec3 tangent = normalize(cross(up, n));
vec3 bitangent = cross(n, tangent);
mat3 tangentToWorld = mat3(tangent, bitangent, n);
uint sample_count = uint(num_samples);
for (uint i = 0u; i < sample_count; ++i) {
vec2 Xi = Hammersley(i, sample_count);
vec3 h = tangentToWorld * ImportanceSampleGGX(Xi, n, a);
vec3 l = normalize(2.0 * dot(v, h) * h - v);
float NdotL = max(dot(n, l), 0.0);
if (NdotL > 0.0) {
prefilteredColor += textureLod(envmap, l, 0.0).rgb * NdotL;
float NdotH = clamp(dot(n, h), 0.0, 1.0);
float VdotH = clamp(dot(v, h), 0.0, 1.0);
float pdf = D_GGX(NdotH, a) * NdotH / (4.0 * VdotH);
float omegaS = 1.0 / (float(sample_count) * pdf);
float omegaP = 4.0 * PI / (6.0 * ENVMAP_SIZE * ENVMAP_SIZE);
float mipLevel = clamp(0.5 * log2(omegaS / omegaP) + 1.0,
0.0, ENVMAP_MIP_COUNT);
prefilteredColor += textureLod(envmap, l, mipLevel).rgb * NdotL;
totalWeight += NdotL;
}
}
prefilteredColor /= totalWeight;
return prefilteredColor;
return prefilteredColor / totalWeight;
}
void main()
{
fragColor0 = prefilter(cubemapCoord0);
fragColor1 = prefilter(cubemapCoord1);
fragColor2 = prefilter(cubemapCoord2);
fragColor3 = prefilter(cubemapCoord3);
fragColor4 = prefilter(cubemapCoord4);
fragColor5 = prefilter(cubemapCoord5);
fragColor0 = prefilter(normalize(cubemapCoord0));
fragColor1 = prefilter(normalize(cubemapCoord1));
fragColor2 = prefilter(normalize(cubemapCoord2));
fragColor3 = prefilter(normalize(cubemapCoord3));
fragColor4 = prefilter(normalize(cubemapCoord4));
fragColor5 = prefilter(normalize(cubemapCoord5));
}