diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index abf2b2f513..7dd8179a57 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -5,6 +5,8 @@ A new header is inserted each time a *tag* is created.
## Next release
+- Improve high roughness material rendering by default when regenerating environments maps
+
## v1.4.3
- Fixed an assertion when a parameter array occurs last in a material definition.
diff --git a/filament/src/IndirectLight.cpp b/filament/src/IndirectLight.cpp
index c43e7643cc..021f302637 100644
--- a/filament/src/IndirectLight.cpp
+++ b/filament/src/IndirectLight.cpp
@@ -156,12 +156,6 @@ IndirectLight* IndirectLight::Builder::build(Engine& engine) {
return nullptr;
}
- if (!ASSERT_POSTCONDITION_NON_FATAL(mImpl->mReflectionsMap->getLevels() ==
- upcast(mImpl->mReflectionsMap)->getMaxLevelCount(),
- "reflection map must have %u mipmap levels",
- upcast(mImpl->mReflectionsMap)->getMaxLevelCount())) {
- return nullptr;
- }
if (IBL_INTEGRATION == IBL_INTEGRATION_IMPORTANCE_SAMPLING) {
mImpl->mReflectionsMap->generateMipmaps(engine);
}
diff --git a/tools/cmgen/src/cmgen.cpp b/tools/cmgen/src/cmgen.cpp
index 6a94bee740..9398cb6b95 100644
--- a/tools/cmgen/src/cmgen.cpp
+++ b/tools/cmgen/src/cmgen.cpp
@@ -58,6 +58,7 @@ enum class ShFile {
static const size_t DFG_LUT_DEFAULT_SIZE = 128;
static const size_t IBL_DEFAULT_SIZE = 256;
+static const size_t IBL_DEFAULT_MIN_LOD_SIZE = 16;
enum class OutputType {
FACES, KTX, EQUIRECT, OCTAHEDRON
@@ -71,6 +72,7 @@ static float g_extract_blur = 0.0;
static utils::Path g_extract_dir;
static size_t g_output_size = 0;
+static size_t g_min_lod_size = 0;
static bool g_quiet = false;
static bool g_debug = false;
@@ -210,6 +212,8 @@ static void printUsage(char* name) {
" Diffuse irradiance into
\n\n"
" --ibl-no-prefilter\n"
" Use importance sampling instead of prefiltered importance sampling\n\n"
+ " --ibl-min-lod-size\n"
+ " Minimum LOD size [default: 16]\n\n"
" --sh=bands\n"
" SH decomposition of input cubemap\n\n"
" --sh-output=filename.[exr|hdr|psd|rgbm|rgb32f|png|dds|txt]\n"
@@ -240,7 +244,7 @@ static void license() {
}
static int handleCommandLineArgments(int argc, char* argv[]) {
- static constexpr const char* OPTSTR = "hqidt:f:c:s:x:w:";
+ static constexpr const char* OPTSTR = "hqidt:f:c:s:x:w:S:";
static const struct option OPTIONS[] = {
{ "help", no_argument, nullptr, 'h' },
{ "license", no_argument, nullptr, 'l' },
@@ -264,6 +268,7 @@ static int handleCommandLineArgments(int argc, char* argv[]) {
{ "ibl-dfg-multiscatter", no_argument, nullptr, 'u' },
{ "ibl-dfg-cloth", no_argument, nullptr, 'C' },
{ "ibl-no-prefilter", no_argument, nullptr, 'n' },
+ { "ibl-min-lod-size", required_argument, nullptr, 'S' },
{ "ibl-samples", required_argument, nullptr, 'k' },
{ "deploy", required_argument, nullptr, 'x' },
{ "no-mirror", no_argument, nullptr, 'm' },
@@ -353,6 +358,13 @@ static int handleCommandLineArgments(int argc, char* argv[]) {
exit(0);
}
break;
+ case 'S':
+ g_min_lod_size = std::stoul(arg);
+ if (!isPOT(g_min_lod_size)) {
+ std::cerr << "min LOD size must be a power of two" << std::endl;
+ exit(0);
+ }
+ break;
case 'z':
g_sh_compute = 1;
g_sh_output = true;
@@ -877,8 +889,13 @@ void iblRoughnessPrefilter(
const bool DEBUG_FULL_RESOLUTION = false;
const size_t baseExp = utils::ctz(g_output_size ? g_output_size : IBL_DEFAULT_SIZE);
+ size_t minLod = utils::ctz(g_min_lod_size ? g_min_lod_size : IBL_DEFAULT_MIN_LOD_SIZE);
+ if (minLod >= baseExp) {
+ minLod = 0;
+ }
+
size_t numSamples = g_num_samples;
- const size_t numLevels = baseExp + 1;
+ const size_t numLevels = (baseExp + 1) - minLod;
// It's convenient to create an empty KTX bundle on the stack in this scope, regardless of
// whether KTX is requested. It does not consume memory if empty.
@@ -895,7 +912,7 @@ void iblRoughnessPrefilter(
.pixelDepth = 0,
};
- for (ssize_t i = baseExp; i >= 0; --i) {
+ for (ssize_t i = baseExp; i >= ssize_t((baseExp + 1) - numLevels) ; --i) {
const size_t dim = 1U << (DEBUG_FULL_RESOLUTION ? baseExp : i); // NOLINT
const size_t level = baseExp - i;
if (level >= 2) {