diff --git a/libs/iblprefilter/include/filament/iblprefilter/IBLPrefilterContext.h b/libs/iblprefilter/include/filament/iblprefilter/IBLPrefilterContext.h index c505830708..e2a9184c25 100644 --- a/libs/iblprefilter/include/filament/iblprefilter/IBLPrefilterContext.h +++ b/libs/iblprefilter/include/filament/iblprefilter/IBLPrefilterContext.h @@ -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 }; diff --git a/libs/iblprefilter/src/IBLPrefilterContext.cpp b/libs/iblprefilter/src/IBLPrefilterContext.cpp index 7fd2b2d98e..1d71e82c1a 100644 --- a/libs/iblprefilter/src/IBLPrefilterContext.cpp +++ b/libs/iblprefilter/src/IBLPrefilterContext.cpp @@ -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) diff --git a/libs/iblprefilter/src/materials/iblprefilter.mat b/libs/iblprefilter/src/materials/iblprefilter.mat index 603d7e6b47..cc7977b40e 100644 --- a/libs/iblprefilter/src/materials/iblprefilter.mat +++ b/libs/iblprefilter/src/materials/iblprefilter.mat @@ -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; } }