fix several bugs in the gpu prefilter code

- the miplevel wasn't computed correctly
- the random rotation was incorrect
- the default HDR compression was too aggressive
- better validation of the input cubemap
This commit is contained in:
Mathias Agopian
2021-04-20 23:59:34 -07:00
committed by Mathias Agopian
parent c819265597
commit cfd9689444
3 changed files with 36 additions and 26 deletions

View File

@@ -93,7 +93,7 @@ public:
* Filter configuration.
*/
struct Config {
uint16_t sampleCount = 1024u; //!< filter sample count
uint16_t sampleCount = 1024u; //!< filter sample count (max 2048)
uint8_t levelCount = 5u; //!< number of roughness levels
Kernel kernel = Kernel::D_GGX; //!< filter kernel
};
@@ -102,8 +102,8 @@ public:
* Filtering options for the current environment.
*/
struct Options {
float hdrLinear = 8.0f; //!< no HDR compression up to this value
float hdrMax = 16.0f; //!< HDR compression between hdrLinear and hdrMax
float hdrLinear = 1024.0f; //!< no HDR compression up to this value
float hdrMax = 16384.0f; //!< HDR compression between hdrLinear and hdrMax
bool generateMipmap = true; //!< set to false if the environment map already has mipmaps
};

View File

@@ -323,7 +323,13 @@ filament::Texture* IBLPrefilterContext::SpecularFilter::operator()(
ASSERT_PRECONDITION(environmentCubemap != nullptr, "outReflectionsTexture is null!");
ASSERT_PRECONDITION(environmentCubemap->getTarget() == Texture::Sampler::SAMPLER_CUBEMAP,
"outReflectionsTexture must be a cubemap!");
"outReflectionsTexture must be a cubemap.");
UTILS_UNUSED_IN_RELEASE
const uint8_t maxLevelCount = uint8_t(std::log2(environmentCubemap->getWidth()) + 0.5f) + 1u;
ASSERT_PRECONDITION(environmentCubemap->getLevels() == maxLevelCount,
"outReflectionsTexture must have %u mipmap levels allocated.", +maxLevelCount);
if (outReflectionsTexture == nullptr) {
outReflectionsTexture = createReflectionsTexture();
@@ -352,6 +358,7 @@ filament::Texture* IBLPrefilterContext::SpecularFilter::operator()(
const float compress = options.hdrMax;
const uint8_t levels = outReflectionsTexture->getLevels();
uint32_t dim = outReflectionsTexture->getWidth();
const float omegaP = (4.0f * f::PI) / float(6 * dim * dim);
TextureSampler environmentSampler;
environmentSampler.setMagFilter(SamplerMagFilter::LINEAR);
@@ -360,6 +367,7 @@ filament::Texture* IBLPrefilterContext::SpecularFilter::operator()(
mi->setParameter("environment", environmentCubemap, environmentSampler);
mi->setParameter("kernel", mKernelTexture, TextureSampler{ SamplerMagFilter::NEAREST });
mi->setParameter("compress", float2{ linear, compress });
mi->setParameter("log4OmegaP", log4(omegaP));
if (options.generateMipmap) {
// We need mipmaps for prefiltering
@@ -371,16 +379,13 @@ filament::Texture* IBLPrefilterContext::SpecularFilter::operator()(
.texture(RenderTarget::AttachmentPoint::COLOR1, outReflectionsTexture)
.texture(RenderTarget::AttachmentPoint::COLOR2, outReflectionsTexture);
for (size_t lod = 0; lod < levels; lod++) {
SYSTRACE_NAME("executeFilterLOD");
const float omegaP = (4.0f * f::PI) / float(6 * dim * dim);
mi->setParameter("sampleCount", uint32_t(lod == 0 ? 1u : sampleCount));
mi->setParameter("attachmentLevel", uint32_t(lod));
mi->setParameter("invKernelWeight", 1.0f / mKernelWeightArray[lod]);
mi->setParameter("log4OmegaP", log4(omegaP));
builder.mipLevel(RenderTarget::AttachmentPoint::COLOR0, lod)
.mipLevel(RenderTarget::AttachmentPoint::COLOR1, lod)

View File

@@ -46,17 +46,17 @@ material {
{
name : outx,
target : color,
type : float4
type : float3
},
{
name : outy,
target : color,
type : float4
type : float3
},
{
name : outz,
target : color,
type : float4
type : float3
}
],
variables : [
@@ -79,7 +79,8 @@ void dummy() {}
precision highp float;
mat3 lookAt(const vec3 N, const vec3 up) {
mat3 tangentSpace(const vec3 N) {
const vec3 up = vec3(0, 0, 1);
mat3 R;
R[0] = normalize(cross(up, N));
R[1] = cross(N, R[0]);
@@ -93,9 +94,9 @@ float random(const highp vec2 w) {
}
// See: http://graphicrants.blogspot.com/2013/12/tone-mapping.html, By Brian Karis
vec3 compress(const mediump vec3 color, const mediump float linear, const mediump float compressed) {
vec3 compress(const vec3 color, const float linear, const float compressed) {
const mediump vec3 rec709 = vec3(0.2126, 0.7152, 0.0722);
mediump float luma = dot(color, rec709); // REC 709
float luma = dot(color, rec709); // REC 709
float s = 1.0;
if (luma > linear) {
s = ((linear * linear - compressed * luma) / ((2.0 * linear - compressed - luma) * luma));
@@ -122,15 +123,19 @@ void postProcess(inout PostProcessInputs postProcess) {
vec3 ry = normalize(vec3( p.x, side, side * p.y));
vec3 rz = normalize(vec3(side * p.x, -p.y, side));
// random rotation around r, by picking a random "up" vector
float a = 2.0 * PI * random(gl_FragCoord.xy);
float ux = cos(a);
float uy = sin(a);
// random rotation around r
mediump float a = 2.0 * PI * random(gl_FragCoord.xy);
mediump float c = cos(a);
mediump float s = sin(a);
mat3 R;
R[0] = vec3( c, s, 0);
R[1] = vec3(-s, c, 0);
R[2] = vec3( 0, 0, 1);
// compute the rotation by which to transform our sample locations for each face
mat3 Rx = lookAt(rx, vec3(0, ux, uy));
mat3 Ry = lookAt(ry, vec3(ux, 0, uy));
mat3 Rz = lookAt(rz, vec3(ux, uy, 0));
mat3 Tx = tangentSpace(rx) * R;
mat3 Ty = tangentSpace(ry) * R;
mat3 Tz = tangentSpace(rz) * R;
// accumulated environment light for each face
vec3 Lx = vec3(0);
@@ -141,9 +146,9 @@ void postProcess(inout PostProcessInputs postProcess) {
// { L, lod }, with L.z == NoL
mediump vec4 entry = texelFetch(materialParams_kernel, ivec2(materialParams.attachmentLevel, i), 0);
float l = entry.w - materialParams.log4OmegaP; // we don't need to clamp, the h/w does it for us
Lx += sampleEnvironment(materialParams_environment, Rx * entry.xyz, l) * entry.z;
Ly += sampleEnvironment(materialParams_environment, Ry * entry.xyz, l) * entry.z;
Lz += sampleEnvironment(materialParams_environment, Rz * entry.xyz, l) * entry.z;
Lx += sampleEnvironment(materialParams_environment, Tx * entry.xyz, l) * entry.z;
Ly += sampleEnvironment(materialParams_environment, Ty * entry.xyz, l) * entry.z;
Lz += sampleEnvironment(materialParams_environment, Tz * entry.xyz, l) * entry.z;
}
highp float invKernelWeight = materialParams.invKernelWeight;
@@ -151,9 +156,9 @@ void postProcess(inout PostProcessInputs postProcess) {
Ly *= invKernelWeight;
Lz *= invKernelWeight;
postProcess.outx = vec4(Lx, 1.0);
postProcess.outy = vec4(Ly, 1.0);
postProcess.outz = vec4(Lz, 1.0);
postProcess.outx = Lx;
postProcess.outy = Ly;
postProcess.outz = Lz;
}
}