diff --git a/filament/CMakeLists.txt b/filament/CMakeLists.txt index 997b87fcf4..7707ef80e3 100644 --- a/filament/CMakeLists.txt +++ b/filament/CMakeLists.txt @@ -124,6 +124,7 @@ set(PRIVATE_HDRS set(MATERIAL_SRCS src/materials/defaultMaterial.mat + src/materials/mipmapDepth.mat src/materials/skybox.mat src/materials/skyboxRGBM.mat src/materials/sao.mat diff --git a/filament/src/Engine.cpp b/filament/src/Engine.cpp index 38ddf14f91..2e72428f04 100644 --- a/filament/src/Engine.cpp +++ b/filament/src/Engine.cpp @@ -215,15 +215,15 @@ void FEngine::init() { .intensity(1.0f) .build(*this)); - mPostProcessManager.init(*this); - mLightManager.init(*this); - mDFG.reset(new DFG(*this)); - // Always initialize the default material, most materials' depth shaders fallback on it. mDefaultMaterial = upcast( FMaterial::DefaultMaterialBuilder() .package(MATERIALS_DEFAULTMATERIAL_DATA, MATERIALS_DEFAULTMATERIAL_SIZE) .build(*const_cast(this))); + + mPostProcessManager.init(*this); + mLightManager.init(*this); + mDFG.reset(new DFG(*this)); } FEngine::~FEngine() noexcept { diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 0f52c50d7d..311cd6b48d 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -62,10 +62,13 @@ void PostProcessManager::init(FEngine& engine) noexcept { mSSAOMaterial = upcast(Material::Builder().package( MATERIALS_SAO_DATA, MATERIALS_SAO_SIZE).build(engine)); - mSSAOMaterialInstance = mSSAOMaterial->getDefaultInstance(); - mSSAOProgram = mSSAOMaterial->getProgram(0); + + mMipmapDepthMaterial = upcast(Material::Builder().package( + MATERIALS_MIPMAPDEPTH_DATA, MATERIALS_MIPMAPDEPTH_SIZE).build(engine)); + mMipmapDepthMaterialInstance = mMipmapDepthMaterial->getDefaultInstance(); + mMipmapDepthProgram = mMipmapDepthMaterial->getProgram(0); } void PostProcessManager::terminate(backend::DriverApi& driver) noexcept { @@ -307,12 +310,16 @@ FrameGraphResource PostProcessManager::ssao(FrameGraph& fg, RenderPass& pass, RenderPass::Command const* first = pass.getCommands().begin(); RenderPass::Command const* last = pass.getCommands().end(); + // We limit the level size to 32 pixels (which is where the -5 comes from) + const size_t levelCount = std::max(1, std::ilogbf(std::max(svp.width, svp.height) / 2) + 1 - 5); + // SSAO generates its own depth path at 1/4 resolution auto& ssaoDepthPass = fg.addPass("SSAO Depth Pass", - [&svp](FrameGraph::Builder& builder, DepthPassData& data) { + [&svp, levelCount](FrameGraph::Builder& builder, DepthPassData& data) { data.depth = builder.createTexture("Depth Buffer", { .width = svp.width / 2, .height = svp.height / 2, + .levels = uint8_t(levelCount), .format = TextureFormat::DEPTH24 }); data.depth = builder.useRenderTarget("SSAO Depth Target", @@ -326,6 +333,49 @@ FrameGraphResource PostProcessManager::ssao(FrameGraph& fg, RenderPass& pass, FrameGraphResource depth = ssaoDepthPass.getData().depth; + // The first mip already exists, so we process n-1 lods + for (size_t level = 0; level < levelCount - 1; level++) { + struct DepthMipData { + FrameGraphResource in; + FrameGraphResource out; + }; + + auto& depthMipmappass = fg.addPass("Depth Mipmap Pass", + [depth, level](FrameGraph::Builder& builder, DepthMipData& data) { + const char* name = builder.getName(depth); + data.in = builder.useRenderTarget(name, { + .attachments.depth = { + depth, uint8_t(level), FrameGraphRenderTarget::Attachments::READ }}).depth; + + data.out = builder.useRenderTarget(name, { + .attachments.depth = { + depth, uint8_t(level + 1), FrameGraphRenderTarget::Attachments::WRITE }}).depth; + }, + [this, fullScreenRenderPrimitive, level](FrameGraphPassResources const& resources, + DepthMipData const& data, DriverApi& driver) { + + auto in = resources.getTexture(data.in); + auto out = resources.getRenderTarget(data.out, level + 1u); + + SamplerParams params; + FMaterialInstance* const pInstance = mMipmapDepthMaterialInstance; + pInstance->setParameter("depth", in, params); + pInstance->setParameter("level", uint32_t(level)); + pInstance->commit(driver); + pInstance->use(driver); + + PipelineState pipeline; + pipeline.program = mMipmapDepthProgram; + pipeline.rasterState = mMipmapDepthMaterial->getRasterState(); + + driver.beginRenderPass(out.target, out.params); + driver.draw(pipeline, fullScreenRenderPrimitive); + driver.endRenderPass(); + }); + + depth = depthMipmappass.getData().out; + } + struct SSAOPassData { FrameGraphResource depth; FrameGraphResource ssao; @@ -349,7 +399,7 @@ FrameGraphResource PostProcessManager::ssao(FrameGraph& fg, RenderPass& pass, .attachments.depth = { data.depth, FrameGraphRenderTarget::Attachments::READ } }, TargetBufferFlags::NONE).color; }, - [this, fullScreenRenderPrimitive](FrameGraphPassResources const& resources, + [this, levelCount, fullScreenRenderPrimitive](FrameGraphPassResources const& resources, SSAOPassData const& data, DriverApi& driver) { auto depth = resources.getTexture(data.depth); auto ssao = resources.getRenderTarget(data.ssao); @@ -365,6 +415,7 @@ FrameGraphResource PostProcessManager::ssao(FrameGraph& fg, RenderPass& pass, pInstance->setParameter("projectionScaleRadius", 500.0f * data.options.radius); pInstance->setParameter("bias", data.options.bias); pInstance->setParameter("power", data.options.power); + pInstance->setParameter("maxLevel", uint32_t(levelCount - 1)); pInstance->commit(driver); pInstance->use(driver); diff --git a/filament/src/PostProcessManager.h b/filament/src/PostProcessManager.h index 4bcef0c701..4d12bba481 100644 --- a/filament/src/PostProcessManager.h +++ b/filament/src/PostProcessManager.h @@ -79,6 +79,10 @@ private: details::FMaterialInstance* mSSAOMaterialInstance = nullptr; backend::Handle mSSAOProgram; + details::FMaterial* mMipmapDepthMaterial = nullptr; + details::FMaterialInstance* mMipmapDepthMaterialInstance = nullptr; + backend::Handle mMipmapDepthProgram; + backend::Handle mNoSSAOTexture; }; diff --git a/filament/src/fg/FrameGraph.cpp b/filament/src/fg/FrameGraph.cpp index 7d2cab2eeb..95a4329b36 100644 --- a/filament/src/fg/FrameGraph.cpp +++ b/filament/src/fg/FrameGraph.cpp @@ -639,7 +639,7 @@ FrameGraphResource::Descriptor const& FrameGraphPassResources::getDescriptor( // ------------------------------------------------------------------------------------------------ FrameGraph::FrameGraph() - : mArena("FrameGraph Arena", 16384), // TODO: the Area will eventually come from outside + : mArena("FrameGraph Arena", 32768), // TODO: the Area will eventually come from outside mPassNodes(mArena), mResourceNodes(mArena), mRenderTargets(mArena), diff --git a/filament/src/materials/mipmapDepth.mat b/filament/src/materials/mipmapDepth.mat new file mode 100644 index 0000000000..c5f414b1db --- /dev/null +++ b/filament/src/materials/mipmapDepth.mat @@ -0,0 +1,46 @@ +material { + name : mipmapDepth, + parameters : [ + { + type : sampler2d, + name : depth, + precision: high + }, + { + type : int, + name : level + } + ], + variables : [ + ], + vertexDomain : device, + depthWrite : true, + depthCulling : false, + shadingModel : unlit, + variantFilter : [ skinning ], + culling: none +} + +fragment { + void material(inout MaterialInputs material) { + prepareMaterial(material); + + int level = materialParams.level; + ivec2 xy = ivec2(gl_FragCoord.xy) * 2; + highp float d00 = texelFetch(materialParams_depth, xy + ivec2(0, 0), level).r; + highp float d10 = texelFetch(materialParams_depth, xy + ivec2(1, 0), level).r; + highp float d01 = texelFetch(materialParams_depth, xy + ivec2(0, 1), level).r; + highp float d11 = texelFetch(materialParams_depth, xy + ivec2(1, 1), level).r; + + gl_FragDepth = + + // conservative occlusion + //max(d00, max(d01, max(d10, d11))); + + // conservative visibility + //min(d00, min(d01, min(d10, d11))); + + // arithmetic mean: preserve screen-space area + (d00 + d01 + d10 + d11) * 0.25; + } +} diff --git a/filament/src/materials/sao.mat b/filament/src/materials/sao.mat index 0b47ea5152..4fb5991664 100644 --- a/filament/src/materials/sao.mat +++ b/filament/src/materials/sao.mat @@ -1,5 +1,5 @@ material { - name : ssao, + name : sao, parameters : [ { type : sampler2d, @@ -29,6 +29,10 @@ material { { type : float, name : power + }, + { + type : int, + name : maxLevel } ], variables : [ @@ -53,6 +57,8 @@ vertex { } fragment { + #define LOG2_LOD_RATE 4 + #define NOISE_NONE 0 #define NOISE_PATTERN 1 #define NOISE_RANDOM 2 @@ -162,8 +168,8 @@ fragment { return vec3(M * kSipralSamples[i].xy, radius * radius); } - ivec2 clampToEdge(ivec2 uv) { - return clamp(uv, ivec2(0), textureSize(materialParams_depth, 0) - ivec2(1)); + ivec2 clampToEdge(ivec2 uv, int level) { + return clamp(uv, ivec2(0), textureSize(materialParams_depth, level) - ivec2(1)); } float computeAmbientOcclusionSAO(uint i, float ssDiskRadius, const ivec2 ssOrigin, const highp vec3 origin, const vec3 normal, const vec3 noise) { @@ -172,7 +178,10 @@ fragment { ivec2 ssSamplePos = ssOrigin + ivec2(ssRadius * tap.xy); vec2 uvSamplePos = (vec2(ssSamplePos) + vec2(0.5)) * materialParams.resolution.zw; - highp float occlusionDepth = linearizeDepth(texelFetch(materialParams_depth, clampToEdge(ssSamplePos), 0).r); + + // level = floor(log2(screenSpaceRadius/rate))) + int level = clamp(int(floor(log2(ssRadius))) - LOG2_LOD_RATE, 0, materialParams.maxLevel); + highp float occlusionDepth = linearizeDepth(texelFetch(materialParams_depth, clampToEdge(ssSamplePos >> level, level), level).r); highp vec3 p = computeViewSpacePositionFromDepth(uvSamplePos * 2.0 - 1.0, occlusionDepth); // now we have the sample, compute AO diff --git a/filament/src/materials/ssao.mat b/filament/src/materials/ssao.mat index df0dfdb0ce..b44258a3c1 100644 --- a/filament/src/materials/ssao.mat +++ b/filament/src/materials/ssao.mat @@ -29,6 +29,10 @@ material { { type : float, name : power + }, + { + type : int, + name : maxLevel } ], variables : [