Fix IBL mip calculation

The max mip (i.e. mip at roughness 1) was off by one.
This commit is contained in:
Pixelflinger
2020-03-20 17:22:27 -07:00
committed by Mathias Agopian
parent 72a182122a
commit 2caa09bebd
6 changed files with 20 additions and 18 deletions

View File

@@ -180,7 +180,7 @@ namespace details {
FIndirectLight::FIndirectLight(FEngine& engine, const Builder& builder) noexcept {
if (builder->mReflectionsMap) {
mReflectionsMapHandle = upcast(builder->mReflectionsMap)->getHwHandle();
mMaxMipLevel = builder->mReflectionsMap->getLevels();
mLevelCount = builder->mReflectionsMap->getLevels();
}
std::copy(

View File

@@ -367,8 +367,8 @@ void FView::prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaSc
}
// Set up uniforms and sampler for the IBL, guaranteed to be non-null at this point.
float2 iblMaxMipLevel{ ibl->getMaxMipLevel(), 1u << ibl->getMaxMipLevel() };
u.setUniform(offsetof(PerViewUib, iblMaxMipLevel), iblMaxMipLevel);
float iblRoughnessOneLevel = ibl->getLevelCount() - 1.0f;
u.setUniform(offsetof(PerViewUib, iblRoughnessOneLevel), iblRoughnessOneLevel);
u.setUniform(offsetof(PerViewUib, iblLuminance), intensity * exposure);
u.setUniformArray(offsetof(PerViewUib, iblSH), ibl->getSH(), 9);
if (ibl->getReflectionMap()) {

View File

@@ -50,7 +50,7 @@ public:
void setIntensity(float intensity) noexcept { mIntensity = intensity; }
void setRotation(math::mat3f const& rotation) noexcept { mRotation = rotation; }
const math::mat3f& getRotation() const noexcept { return mRotation; }
size_t getMaxMipLevel() const noexcept { return mMaxMipLevel; }
size_t getLevelCount() const noexcept { return mLevelCount; }
math::float3 getDirectionEstimate() const noexcept;
math::float4 getColorEstimate(math::float3 direction) const noexcept;
static math::float3 getDirectionEstimate(const math::float3 sh[9]) noexcept;
@@ -62,7 +62,7 @@ private:
std::array<math::float3, 9> mIrradianceCoefs;
float mIntensity = DEFAULT_INTENSITY;
math::mat3f mRotation;
uint8_t mMaxMipLevel = 0;
uint8_t mLevelCount = 0;
};
FILAMENT_UPCAST(IndirectLight)

View File

@@ -85,7 +85,8 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
filament::math::float4 userTime; // time(s), (double)time - (float)time, 0, 0
filament::math::float2 iblMaxMipLevel; // maxlevel, float(1<<maxlevel)
float iblRoughnessOneLevel; // level for roughness == 1
float padding0;
float refractionLodOffset;
// bit 0: directional (sun) shadow enabled

View File

@@ -74,7 +74,8 @@ UniformInterfaceBlock const& UibGenerator::getPerViewUib() noexcept {
// user time
.add("userTime", 1, UniformInterfaceBlock::Type::FLOAT4)
// ibl max mip level
.add("iblMaxMipLevel", 1, UniformInterfaceBlock::Type::FLOAT2)
.add("iblRoughnessOneLevel", 1, UniformInterfaceBlock::Type::FLOAT)
.add("padding0", 1, UniformInterfaceBlock::Type::FLOAT)
.add("refractionLodOffset", 1, UniformInterfaceBlock::Type::FLOAT)
.add("directionalShadows", 1, UniformInterfaceBlock::Type::UINT)
// view

View File

@@ -63,7 +63,7 @@ vec3 Irradiance_SphericalHarmonics(const vec3 n) {
vec3 Irradiance_RoughnessOne(const vec3 n) {
// note: lod used is always integer, hopefully the hardware skips tri-linear filtering
return decodeDataForIBL(textureLod(light_iblSpecular, n, frameUniforms.iblMaxMipLevel.x));
return decodeDataForIBL(textureLod(light_iblSpecular, n, frameUniforms.iblRoughnessOneLevel));
}
//------------------------------------------------------------------------------
@@ -83,10 +83,10 @@ vec3 diffuseIrradiance(const vec3 n) {
//------------------------------------------------------------------------------
float perceptualRoughnessToLod(float perceptualRoughness) {
// The mapping below is a quadratic fit for log2(perceptualRoughness)+iblMaxMipLevel when
// iblMaxMipLevel is 4. We found empirically that this mapping works very well for
// a 256 cubemap with 5 levels used. But also scales well for other iblMaxMipLevel values.
return frameUniforms.iblMaxMipLevel.x * perceptualRoughness * (2.0 - perceptualRoughness);
// The mapping below is a quadratic fit for log2(perceptualRoughness)+iblRoughnessOneLevel when
// iblRoughnessOneLevel is 4. We found empirically that this mapping works very well for
// a 256 cubemap with 5 levels used. But also scales well for other iblRoughnessOneLevel values.
return frameUniforms.iblRoughnessOneLevel * perceptualRoughness * (2.0 - perceptualRoughness);
}
vec3 prefilteredRadiance(const vec3 r, float perceptualRoughness) {
@@ -95,7 +95,7 @@ vec3 prefilteredRadiance(const vec3 r, float perceptualRoughness) {
}
vec3 prefilteredRadiance(const vec3 r, float roughness, float offset) {
float lod = frameUniforms.iblMaxMipLevel.x * roughness;
float lod = frameUniforms.iblRoughnessOneLevel * roughness;
return decodeDataForIBL(textureLod(light_iblSpecular, r, lod + offset));
}
@@ -203,17 +203,17 @@ vec3 importanceSamplingVNdfDggx(vec2 u, float roughness, vec3 v) {
return h;
}
float prefilteredImportanceSampling(float ipdf, vec2 iblMaxMipLevel) {
float prefilteredImportanceSampling(float ipdf, float iblRoughnessOneLevel) {
// See: "Real-time Shading with Filtered Importance Sampling", Jaroslav Krivanek
// Prefiltering doesn't work with anisotropy
const float numSamples = float(IBL_INTEGRATION_IMPORTANCE_SAMPLING_COUNT);
const float invNumSamples = 1.0 / float(numSamples);
const float dim = iblMaxMipLevel.y;
const float dim = float(textureSize(light_iblSpecular, 0).x);
const float omegaP = (4.0 * PI) / (6.0 * dim * dim);
const float invOmegaP = 1.0 / omegaP;
const float K = 4.0;
float omegaS = invNumSamples * ipdf;
float mipLevel = clamp(log2(K * omegaS * invOmegaP) * 0.5, 0.0, iblMaxMipLevel.x);
float mipLevel = clamp(log2(K * omegaS * invOmegaP) * 0.5, 0.0, iblRoughnessOneLevel);
return mipLevel;
}
@@ -229,7 +229,7 @@ vec3 isEvaluateIBL(const PixelParams pixel, vec3 n, vec3 v, float NoV) {
float roughness = pixel.roughness;
float a2 = roughness * roughness;
vec2 iblMaxMipLevel = frameUniforms.iblMaxMipLevel;
float iblRoughnessOneLevel = frameUniforms.iblRoughnessOneLevel;
const uint numSamples = uint(IBL_INTEGRATION_IMPORTANCE_SAMPLING_COUNT);
const float invNumSamples = 1.0 / float(numSamples);
@@ -251,7 +251,7 @@ vec3 isEvaluateIBL(const PixelParams pixel, vec3 n, vec3 v, float NoV) {
// PDF inverse (we must use D_GGX() here, which is used to generate samples)
float ipdf = (4.0 * LoH) / (D_GGX(roughness, NoH, h) * NoH);
float mipLevel = prefilteredImportanceSampling(ipdf, iblMaxMipLevel);
float mipLevel = prefilteredImportanceSampling(ipdf, iblRoughnessOneLevel);
// we use texture() instead of textureLod() to take advantage of mipmapping
vec3 L = decodeDataForIBL(texture(light_iblSpecular, l, mipLevel));