From e5bfe81d0c4e241af726ca4074ee86263a3797c1 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 29 Mar 2021 21:28:55 -0700 Subject: [PATCH] DoF: use only a single low res source for both fg and bg Before we were keeping a foreground and background version of the low resolution source, but in practice it really doesn't make a big difference and we save a fair amount of bandwidth this way. The bulk of the change is to remove a texture/target which mostly affects the downsample and mipmap passes. Also renamed some variables to be less confusing (MaxMin to MinMax for e.g.) and fixed a few typos. --- filament/src/PostProcessManager.cpp | 202 +++++++++---------- filament/src/Renderer.cpp | 4 +- filament/src/materials/dof/dof.mat | 48 ++--- filament/src/materials/dof/dofCoc.mat | 31 ++- filament/src/materials/dof/dofCombine.mat | 4 +- filament/src/materials/dof/dofDilate.mat | 12 -- filament/src/materials/dof/dofDownsample.mat | 65 ++---- filament/src/materials/dof/dofMipmap.mat | 82 ++------ filament/src/materials/dof/dofTiles.mat | 25 +-- filament/src/materials/dof/dofUtils.fs | 20 ++ 10 files changed, 210 insertions(+), 283 deletions(-) diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 831c6d790c..64a1dacd16 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -940,9 +940,8 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, struct PostProcessDofDownsample { FrameGraphId color; FrameGraphId depth; - FrameGraphId outForeground; - FrameGraphId outBackground; - FrameGraphId outCocFgBg; + FrameGraphId outColor; + FrameGraphId outCoc; }; auto& ppDoFDownsample = fg.addPass("DoF Downsample", @@ -950,21 +949,21 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, data.color = builder.sample(input); data.depth = builder.sample(depth); - data.outForeground = builder.createTexture("dof foreground output", { + data.outColor = builder.createTexture("dof downsample output", { .width = width, .height = height, .levels = mipmapCount, .format = format }); - data.outBackground = builder.createTexture("dof background output", { - .width = width, .height = height, .levels = mipmapCount, .format = format - }); - data.outCocFgBg = builder.createTexture("dof CoC output", { + data.outCoc = builder.createTexture("dof CoC output", { .width = width, .height = height, .levels = mipmapCount, - .format = TextureFormat::RG16F + .format = TextureFormat::R16F, + .swizzle = { + // the next stage expects min/max CoC in the red/green channel + .r = backend::TextureSwizzle::CHANNEL_0, + .g = backend::TextureSwizzle::CHANNEL_0 }, }); - data.outForeground = builder.write(data.outForeground, FrameGraphTexture::Usage::COLOR_ATTACHMENT); - data.outBackground = builder.write(data.outBackground, FrameGraphTexture::Usage::COLOR_ATTACHMENT); - data.outCocFgBg = builder.write(data.outCocFgBg, FrameGraphTexture::Usage::COLOR_ATTACHMENT); + data.outColor = builder.write(data.outColor, FrameGraphTexture::Usage::COLOR_ATTACHMENT); + data.outCoc = builder.write(data.outCoc, FrameGraphTexture::Usage::COLOR_ATTACHMENT); builder.declareRenderPass("DoF Target", { .attachments = { - .color = { data.outForeground, data.outBackground, data.outCocFgBg } + .color = { data.outColor, data.outCoc } } }); }, @@ -990,9 +989,8 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, */ struct PostProcessDofMipmap { - FrameGraphId inOutForeground; - FrameGraphId inOutBackground; - FrameGraphId inOutCocFgBg; + FrameGraphId inOutColor; + FrameGraphId inOutCoc; uint32_t rp[maxMipLevels]; }; @@ -1000,25 +998,22 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, auto& ppDoFMipmap = fg.addPass("DoF Mipmap", [&](FrameGraph::Builder& builder, auto& data) { - data.inOutForeground = builder.sample(ppDoFDownsample->outForeground); - data.inOutBackground = builder.sample(ppDoFDownsample->outBackground); - data.inOutCocFgBg = builder.sample(ppDoFDownsample->outCocFgBg); + data.inOutColor = builder.sample(ppDoFDownsample->outColor); + data.inOutCoc = builder.sample(ppDoFDownsample->outCoc); for (size_t i = 0; i < mipmapCount - 1u; i++) { // make sure inputs are always multiple of two (should be true by construction) // (this is so that we can compute clean mip levels) - assert_invariant((FTexture::valueForLevel(uint8_t(i), fg.getDescriptor(data.inOutForeground).width ) & 0x1u) == 0); - assert_invariant((FTexture::valueForLevel(uint8_t(i), fg.getDescriptor(data.inOutForeground).height) & 0x1u) == 0); + assert_invariant((FTexture::valueForLevel(uint8_t(i), fg.getDescriptor(data.inOutColor).width ) & 0x1u) == 0); + assert_invariant((FTexture::valueForLevel(uint8_t(i), fg.getDescriptor(data.inOutColor).height) & 0x1u) == 0); - auto inOutForeground = builder.createSubresource(data.inOutForeground, "Forground mip", { .level = uint8_t(i + 1) }); - auto inOutBackground = builder.createSubresource(data.inOutBackground, "Background mip", { .level = uint8_t(i + 1) }); - auto inOutCocFgBg = builder.createSubresource(data.inOutCocFgBg , "CocFgBg mip", { .level = uint8_t(i + 1) }); + auto inOutColor = builder.createSubresource(data.inOutColor, "Color mip", { .level = uint8_t(i + 1) }); + auto inOutCoc = builder.createSubresource(data.inOutCoc, "Coc mip", { .level = uint8_t(i + 1) }); - inOutForeground = builder.write(inOutForeground, FrameGraphTexture::Usage::COLOR_ATTACHMENT); - inOutBackground = builder.write(inOutBackground, FrameGraphTexture::Usage::COLOR_ATTACHMENT); - inOutCocFgBg = builder.write(inOutCocFgBg, FrameGraphTexture::Usage::COLOR_ATTACHMENT); + inOutColor = builder.write(inOutColor, FrameGraphTexture::Usage::COLOR_ATTACHMENT); + inOutCoc = builder.write(inOutCoc, FrameGraphTexture::Usage::COLOR_ATTACHMENT); data.rp[i] = builder.declareRenderPass("DoF Target", { .attachments = { - .color = { inOutForeground, inOutBackground, inOutCocFgBg } + .color = { inOutColor, inOutCoc } } }); } @@ -1026,15 +1021,13 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, [=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) { - auto inOutForeground = resources.getTexture(data.inOutForeground); - auto inOutBackground = resources.getTexture(data.inOutBackground); - auto inOutCocFgBg = resources.getTexture(data.inOutCocFgBg); + auto inOutColor = resources.getTexture(data.inOutColor); + auto inOutCoc = resources.getTexture(data.inOutCoc); auto const& material = getPostProcessMaterial("dofMipmap"); FMaterialInstance* const mi = material.getMaterialInstance(); - mi->setParameter("foreground", inOutForeground, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); - mi->setParameter("background", inOutBackground, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); - mi->setParameter("cocFgBg", inOutCocFgBg, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); + mi->setParameter("color", inOutColor, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); + mi->setParameter("coc", inOutCoc, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); mi->use(driver); const PipelineState pipeline(material.getPipelineState(variant)); @@ -1055,7 +1048,7 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, * - Generate min/max tiles for far/near fields (continued) */ - auto inTilesCocMaxMin = ppDoFDownsample->outCocFgBg; + auto inTilesCocMinMax = ppDoFDownsample->outCoc; // TODO: Should the tile size be in real pixels? i.e. always 16px instead of being dependant on // the DoF effect resolution? @@ -1067,8 +1060,8 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, const size_t tileReductionCount = std::log2(tileSize / dofResolution); struct PostProcessDofTiling1 { - FrameGraphId inCocMaxMin; - FrameGraphId outTilesCocMaxMin; + FrameGraphId inCocMinMax; + FrameGraphId outTilesCocMinMax; }; for (size_t i = 0; i < tileReductionCount; i++) { @@ -1076,29 +1069,29 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, [&](FrameGraph::Builder& builder, auto& data) { assert_invariant(((tileBufferWidth >> i) & 1u) == 0); assert_invariant(((tileBufferHeight >> i) & 1u) == 0); - data.inCocMaxMin = builder.sample(inTilesCocMaxMin); - data.outTilesCocMaxMin = builder.createTexture("dof tiles output", { + data.inCocMinMax = builder.sample(inTilesCocMinMax); + data.outTilesCocMinMax = builder.createTexture("dof tiles output", { .width = tileBufferWidth >> (i + 1u), .height = tileBufferHeight >> (i + 1u), .format = TextureFormat::RG16F }); - data.outTilesCocMaxMin = builder.declareRenderPass(data.outTilesCocMaxMin); + data.outTilesCocMinMax = builder.declareRenderPass(data.outTilesCocMinMax); }, [=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) { - auto const& inputDesc = resources.getDescriptor(data.inCocMaxMin); - auto const& outputDesc = resources.getDescriptor(data.outTilesCocMaxMin); + auto const& inputDesc = resources.getDescriptor(data.inCocMinMax); + auto const& outputDesc = resources.getDescriptor(data.outTilesCocMinMax); auto const& out = resources.getRenderPassInfo(); - auto inCocMaxMin = resources.getTexture(data.inCocMaxMin); + auto inCocMinMax = resources.getTexture(data.inCocMinMax); auto const& material = getPostProcessMaterial("dofTiles"); FMaterialInstance* const mi = material.getMaterialInstance(); - mi->setParameter("cocMaxMin", inCocMaxMin, { .filterMin = SamplerMinFilter::NEAREST }); + mi->setParameter("cocMinMax", inCocMinMax, { .filterMin = SamplerMinFilter::NEAREST }); mi->setParameter("uvscale", float4{ outputDesc.width, outputDesc.height, 1.0f / inputDesc.width, 1.0f / inputDesc.height }); commitAndRender(out, material, driver); }); - inTilesCocMaxMin = ppDoFTiling->outTilesCocMaxMin; + inTilesCocMinMax = ppDoFTiling->outTilesCocMinMax; } /* @@ -1109,33 +1102,33 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, auto dilate = [&](FrameGraphId input) -> FrameGraphId { struct PostProcessDofDilate { - FrameGraphId inTilesCocMaxMin; - FrameGraphId outTilesCocMaxMin; + FrameGraphId inTilesCocMinMax; + FrameGraphId outTilesCocMinMax; }; auto& ppDoFDilate = fg.addPass("DoF Dilate", [&](FrameGraph::Builder& builder, auto& data) { auto const& inputDesc = fg.getDescriptor(input); - data.inTilesCocMaxMin = builder.sample(input); - data.outTilesCocMaxMin = builder.createTexture("dof dilated tiles output", inputDesc); - data.outTilesCocMaxMin = builder.declareRenderPass(data.outTilesCocMaxMin ); + data.inTilesCocMinMax = builder.sample(input); + data.outTilesCocMinMax = builder.createTexture("dof dilated tiles output", inputDesc); + data.outTilesCocMinMax = builder.declareRenderPass(data.outTilesCocMinMax ); }, [=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) { auto const& out = resources.getRenderPassInfo(); - auto inTilesCocMaxMin = resources.getTexture(data.inTilesCocMaxMin); + auto inTilesCocMinMax = resources.getTexture(data.inTilesCocMinMax); auto const& material = getPostProcessMaterial("dofDilate"); FMaterialInstance* const mi = material.getMaterialInstance(); - mi->setParameter("tiles", inTilesCocMaxMin, { .filterMin = SamplerMinFilter::NEAREST }); + mi->setParameter("tiles", inTilesCocMinMax, { .filterMin = SamplerMinFilter::NEAREST }); commitAndRender(out, material, driver); }); - return ppDoFDilate->outTilesCocMaxMin; + return ppDoFDilate->outTilesCocMinMax; }; // Tiles of 16 full-resolution pixels requires two dilate rounds to accommodate our max Coc of 32 pixels // (note: when running at half-res, the tiles are 8 half-resolution pixels, and still need two // dilate rounds to accommodate the mac CoC pf 16 half-resolution pixels) - auto dilated = dilate(inTilesCocMaxMin); + auto dilated = dilate(inTilesCocMinMax); dilated = dilate(dilated); /* @@ -1143,64 +1136,59 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, */ struct PostProcessDof { - FrameGraphId foreground; - FrameGraphId background; - FrameGraphId cocFgBg; - FrameGraphId tilesCocMaxMin; - FrameGraphId outForeground; + FrameGraphId color; + FrameGraphId coc; + FrameGraphId tilesCocMinMax; + FrameGraphId outColor; FrameGraphId outAlpha; }; auto& ppDoF = fg.addPass("DoF", [&](FrameGraph::Builder& builder, auto& data) { - data.foreground = builder.sample(ppDoFMipmap->inOutForeground); - data.background = builder.sample(ppDoFMipmap->inOutBackground); - data.cocFgBg = builder.sample(ppDoFMipmap->inOutCocFgBg); - data.tilesCocMaxMin = builder.sample(dilated); + data.color = builder.sample(ppDoFMipmap->inOutColor); + data.coc = builder.sample(ppDoFMipmap->inOutCoc); + data.tilesCocMinMax = builder.sample(dilated); // The DoF buffer (output) doesn't need to be a multiple of 8 because it's not // mipmapped. We just need to adjust the uv properly. - data.outForeground = builder.createTexture("dof color output", { + data.outColor = builder.createTexture("dof color output", { .width = (colorDesc.width + (dofResolution / 2u)) / dofResolution, .height = (colorDesc.height + (dofResolution / 2u)) / dofResolution, - .format = fg.getDescriptor(data.foreground).format + .format = fg.getDescriptor(data.color).format }); data.outAlpha = builder.createTexture("dof alpha output", { - .width = builder.getDescriptor(data.outForeground).width, - .height = builder.getDescriptor(data.outForeground).height, + .width = builder.getDescriptor(data.outColor).width, + .height = builder.getDescriptor(data.outColor).height, .format = TextureFormat::R8 }); - data.outForeground = builder.write(data.outForeground, FrameGraphTexture::Usage::COLOR_ATTACHMENT); - data.outAlpha = builder.write(data.outAlpha, FrameGraphTexture::Usage::COLOR_ATTACHMENT); + data.outColor = builder.write(data.outColor, FrameGraphTexture::Usage::COLOR_ATTACHMENT); + data.outAlpha = builder.write(data.outAlpha, FrameGraphTexture::Usage::COLOR_ATTACHMENT); builder.declareRenderPass("DoF Target", { - .attachments = { .color = { data.outForeground, data.outAlpha }} + .attachments = { .color = { data.outColor, data.outAlpha }} }); }, [=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) { auto const& out = resources.getRenderPassInfo(); - auto foreground = resources.getTexture(data.foreground); - auto background = resources.getTexture(data.background); - auto cocFgBg = resources.getTexture(data.cocFgBg); - auto tilesCocMaxMin = resources.getTexture(data.tilesCocMaxMin); + auto color = resources.getTexture(data.color); + auto coc = resources.getTexture(data.coc); + auto tilesCocMinMax = resources.getTexture(data.tilesCocMinMax); - auto const& inputDesc = resources.getDescriptor(data.cocFgBg); - auto const& outputDesc = resources.getDescriptor(data.outForeground); - auto const& tilesDesc = resources.getDescriptor(data.tilesCocMaxMin); + auto const& inputDesc = resources.getDescriptor(data.coc); + auto const& outputDesc = resources.getDescriptor(data.outColor); + auto const& tilesDesc = resources.getDescriptor(data.tilesCocMinMax); auto const& material = getPostProcessMaterial("dof"); FMaterialInstance* const mi = material.getMaterialInstance(); // it's not safe to use bilinear filtering in the general case (causes artifacts around edges) - mi->setParameter("foreground", foreground, + mi->setParameter("color", color, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); - mi->setParameter("foregroundLinear", foreground, + mi->setParameter("colorLinear", color, { .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST }); - mi->setParameter("background", background, + mi->setParameter("coc", coc, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); - mi->setParameter("cocFgBg", cocFgBg, - { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); - mi->setParameter("tiles", tilesCocMaxMin, + mi->setParameter("tiles", tilesCocMinMax, { .filterMin = SamplerMinFilter::NEAREST }); mi->setParameter("cocToTexelScale", (1.0f / dofResolution) / float2{ inputDesc.width, inputDesc.height }); mi->setParameter("cocToPixelScale", (1.0f / dofResolution)); @@ -1220,43 +1208,43 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, struct PostProcessDofMedian { - FrameGraphId inForeground; + FrameGraphId inColor; FrameGraphId inAlpha; - FrameGraphId tilesCocMaxMin; - FrameGraphId outForeground; + FrameGraphId tilesCocMinMax; + FrameGraphId outColor; FrameGraphId outAlpha; }; auto& ppDoFMedian = fg.addPass("DoF Median", [&](FrameGraph::Builder& builder, auto& data) { - data.inForeground = builder.sample(ppDoF->outForeground); + data.inColor = builder.sample(ppDoF->outColor); data.inAlpha = builder.sample(ppDoF->outAlpha); - data.tilesCocMaxMin = builder.sample(dilated); + data.tilesCocMinMax = builder.sample(dilated); - data.outForeground = builder.createTexture("dof color output", fg.getDescriptor(data.inForeground)); - data.outAlpha = builder.createTexture("dof alpha output", fg.getDescriptor(data.inAlpha)); - data.outForeground = builder.write(data.outForeground, FrameGraphTexture::Usage::COLOR_ATTACHMENT); - data.outAlpha = builder.write(data.outAlpha, FrameGraphTexture::Usage::COLOR_ATTACHMENT); + data.outColor = builder.createTexture("dof color output", fg.getDescriptor(data.inColor)); + data.outAlpha = builder.createTexture("dof alpha output", fg.getDescriptor(data.inAlpha)); + data.outColor = builder.write(data.outColor, FrameGraphTexture::Usage::COLOR_ATTACHMENT); + data.outAlpha = builder.write(data.outAlpha, FrameGraphTexture::Usage::COLOR_ATTACHMENT); builder.declareRenderPass("DoF Target", { - .attachments = { .color = { data.outForeground, data.outAlpha }} + .attachments = { .color = { data.outColor, data.outAlpha }} }); }, [=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) { auto const& out = resources.getRenderPassInfo(); - auto inForeground = resources.getTexture(data.inForeground); + auto inColor = resources.getTexture(data.inColor); auto inAlpha = resources.getTexture(data.inAlpha); - auto tilesCocMaxMin = resources.getTexture(data.tilesCocMaxMin); + auto tilesCocMinMax = resources.getTexture(data.tilesCocMinMax); - auto const& outputDesc = resources.getDescriptor(data.outForeground); - auto const& tilesDesc = resources.getDescriptor(data.tilesCocMaxMin); + auto const& outputDesc = resources.getDescriptor(data.outColor); + auto const& tilesDesc = resources.getDescriptor(data.tilesCocMinMax); auto const& material = getPostProcessMaterial("dofMedian"); FMaterialInstance* const mi = material.getMaterialInstance(); - mi->setParameter("dof", inForeground, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); + mi->setParameter("dof", inColor, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); mi->setParameter("alpha", inAlpha, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); - mi->setParameter("tiles", tilesCocMaxMin, { .filterMin = SamplerMinFilter::NEAREST }); + mi->setParameter("tiles", tilesCocMinMax, { .filterMin = SamplerMinFilter::NEAREST }); mi->setParameter("uvscale", float2{ outputDesc.width / float(tileSize / dofResolution * tilesDesc.width), outputDesc.height / float(tileSize / dofResolution * tilesDesc.height) @@ -1269,10 +1257,10 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, * DoF recombine */ - auto outForeground = ppDoFMedian->outForeground; + auto outColor = ppDoFMedian->outColor; auto outAlpha = ppDoFMedian->outAlpha; if (dofOptions.filter == View::DepthOfFieldOptions::Filter::NONE) { - outForeground = ppDoF->outForeground; + outColor = ppDoF->outColor; outAlpha = ppDoF->outAlpha; } @@ -1280,16 +1268,16 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, FrameGraphId color; FrameGraphId dof; FrameGraphId alpha; - FrameGraphId tilesCocMaxMin; + FrameGraphId tilesCocMinMax; FrameGraphId output; }; auto& ppDoFCombine = fg.addPass("DoF combine", [&](FrameGraph::Builder& builder, auto& data) { data.color = builder.sample(input); - data.dof = builder.sample(outForeground); + data.dof = builder.sample(outColor); data.alpha = builder.sample(outAlpha); - data.tilesCocMaxMin = builder.sample(dilated); + data.tilesCocMinMax = builder.sample(dilated); auto const& inputDesc = fg.getDescriptor(data.color); data.output = builder.createTexture("DoF output", inputDesc); data.output = builder.declareRenderPass(data.output); @@ -1297,20 +1285,20 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, [=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) { auto const& dofDesc = resources.getDescriptor(data.dof); - auto const& tilesDesc = resources.getDescriptor(data.tilesCocMaxMin); + auto const& tilesDesc = resources.getDescriptor(data.tilesCocMinMax); auto const& out = resources.getRenderPassInfo(); auto color = resources.getTexture(data.color); auto dof = resources.getTexture(data.dof); auto alpha = resources.getTexture(data.alpha); - auto tilesCocMaxMin = resources.getTexture(data.tilesCocMaxMin); + auto tilesCocMinMax = resources.getTexture(data.tilesCocMinMax); auto const& material = getPostProcessMaterial("dofCombine"); FMaterialInstance* const mi = material.getMaterialInstance(); mi->setParameter("color", color, { .filterMin = SamplerMinFilter::NEAREST }); mi->setParameter("dof", dof, { .filterMag = SamplerMagFilter::NEAREST }); mi->setParameter("alpha", alpha, { .filterMag = SamplerMagFilter::NEAREST }); - mi->setParameter("tiles", tilesCocMaxMin, { .filterMin = SamplerMinFilter::NEAREST }); + mi->setParameter("tiles", tilesCocMinMax, { .filterMin = SamplerMinFilter::NEAREST }); mi->setParameter("uvscale", float4{ colorDesc.width / (dofDesc.width * float(dofResolution)), colorDesc.height / (dofDesc.height * float(dofResolution)), diff --git a/filament/src/Renderer.cpp b/filament/src/Renderer.cpp index b5667dfa6e..491429eab6 100644 --- a/filament/src/Renderer.cpp +++ b/filament/src/Renderer.cpp @@ -445,13 +445,13 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { }); // resolve depth -- which might be needed because of TAA or DoF. This pass will be culled - // if the depth is not use below. + // if the depth is not used below. auto& blackboard = fg.getBlackboard(); auto depth = blackboard.get("depth"); depth = ppm.resolve(fg, "Resolved Depth Buffer", depth); blackboard.put("depth", depth); - // TODO: DoF should be applied here, before TAA -- but if we do this it'll result is a lot + // TODO: DoF should be applied here, before TAA -- but if we do this it'll result in a lot of // fireflies due to the instability of the highlights. This can be fixed with a // dedicated TAA pass for the DoF, as explained in // "Life of a Bokeh" by Guillaume Abadie, SIGGRAPH 2018 diff --git a/filament/src/materials/dof/dof.mat b/filament/src/materials/dof/dof.mat index da1fbdd4de..344ebb175f 100644 --- a/filament/src/materials/dof/dof.mat +++ b/filament/src/materials/dof/dof.mat @@ -3,22 +3,17 @@ material { parameters : [ { type : sampler2d, - name : foreground, + name : color, precision: medium }, { type : sampler2d, - name : foregroundLinear, + name : colorLinear, precision: medium }, { type : sampler2d, - name : background, - precision: medium - }, - { - type : sampler2d, - name : cocFgBg, + name : coc, precision: medium }, { @@ -207,8 +202,9 @@ struct Bucket { precision mediump float; struct Sample { - vec4 s; // sample color - float coc; // sample coc + vec4 s; // sample color + float coc; // sample coc + float inLayer; // whether is sample is in the considered layer }; void initBucket(out Bucket ring) { @@ -268,7 +264,7 @@ void mergeRings(inout Bucket curr, inout Bucket prev, const float count) { void accumulate(inout Bucket curr, inout Bucket prev, const Sample tap, const float radius, const float border, const float mip, const bool first) { - float inLayer = isBackground(tap.coc); + float inLayer = tap.inLayer; float coc = abs(tap.coc); float w = inLayer * intersection(radius, coc, mip) * sampleWeight(coc, mip); @@ -303,11 +299,9 @@ void accumulate(inout Bucket curr, inout Bucket prev, const Sample tap, void accumulateBackground(inout Bucket curr, inout Bucket prev, const highp vec2 pos, const float radius, const float border, const float mip, const bool first) { Sample tap; - // FIXME: there are typos below. We should use materialParams_background and - // materialParams_cocFgBg's green (background coc) channel. - // however doing so, looks much worse and is much more unstable. - tap.s = textureLod(materialParams_foreground, pos, mip); - tap.coc = textureLod(materialParams_cocFgBg, pos, mip).r; + tap.s = textureLod(materialParams_color, pos, mip); + tap.coc = textureLod(materialParams_coc, pos, mip).r; + tap.inLayer = isBackground(tap.coc); accumulate(curr, prev, tap, radius, border, mip, first); } @@ -387,8 +381,8 @@ float foregroundFadding(const float coc) { void accumulateForeground(inout Layer layer[2], const vec2 tiles, const highp vec2 pos, const float border, const float mip) { - float coc = textureLod(materialParams_cocFgBg, pos, mip).r; - vec4 s = textureLod(materialParams_foreground, pos, mip); + float coc = textureLod(materialParams_coc, pos, mip).r; + vec4 s = textureLod(materialParams_color, pos, mip); float inLayer = isForeground(coc) * foregroundFadding(coc); float i = intersection(border, abs(coc), mip) * sampleWeight(coc, mip); @@ -417,12 +411,12 @@ void accumulateForegroundMirror(inout Layer layer[2], const vec2 tiles, // the geometry. highp vec2 pos0 = diaphragm(center, offset); - float coc0 = textureLod(materialParams_cocFgBg, pos0, mip).r; - vec4 s0 = textureLod(materialParams_foreground, pos0, mip); + float coc0 = textureLod(materialParams_coc, pos0, mip).r; + vec4 s0 = textureLod(materialParams_color, pos0, mip); highp vec2 pos1 = diaphragm(center, -offset); - float coc1 = textureLod(materialParams_cocFgBg, pos1, mip).r; - vec4 s1 = textureLod(materialParams_foreground, pos1, mip); + float coc1 = textureLod(materialParams_coc, pos1, mip).r; + vec4 s1 = textureLod(materialParams_color, pos1, mip); float coc = min(coc0, coc1); float inLayer = isForeground(coc) * foregroundFadding(coc); @@ -460,7 +454,7 @@ void fastTile(inout vec4 color, inout float alpha, highp vec2 uvCenter = uv + noise * cocToTexelScale; - color = textureLod(materialParams_foregroundLinear, uvCenter, mip); + color = textureLod(materialParams_colorLinear, uvCenter, mip); for (float i = 1.0; i < ringCountFast; i += 1.0) { float radius; float count; @@ -470,9 +464,9 @@ void fastTile(inout vec4 color, inout float alpha, // we must use this temporary in order to accomodate mediump precision vec4 t = vec4(0.0); for (float j = 0.0; j < count; j += 2.0) { - t += textureLod(materialParams_foregroundLinear, + t += textureLod(materialParams_colorLinear, diaphragm(uvCenter, p * cocToTexelScale), mip) - + textureLod(materialParams_foregroundLinear, + + textureLod(materialParams_colorLinear, diaphragm(uvCenter, -p * cocToTexelScale), mip); p = r * p; } @@ -545,7 +539,7 @@ void backgroundTile(inout vec4 background, inout float bgOpacity, // we select a ring cound per tile, to avoid divergence between pixels float ringCountGather = min(float(RING_COUNT_GATHER), computeNeededRings(tiles.g * cocToPixelScale)); - float centerCoc = textureLod(materialParams_cocFgBg, uv, 0.0).g; + float centerCoc = textureLod(materialParams_coc, uv, 0.0).r; float kernelSize = abs(centerCoc); float mip = getMipLevel(ringCountGather, kernelSize * cocToPixelScale); float noiseRadius = 0.0; @@ -650,7 +644,7 @@ void postProcess(inout PostProcessInputs postProcess) { // TODO: make this a quality setting if (fgOpacity > 0.0 && fgOpacity < 1.0 && bgOpacity == 0.0) { // this happens on very few pixels - float centerCoc = textureLod(materialParams_cocFgBg, uv, 0.0).r; + float centerCoc = textureLod(materialParams_coc, uv, 0.0).r; float correctedOpacity = max(fgOpacity, cocToAlpha(centerCoc)); float updateOpacity = correctedOpacity / fgOpacity; foreground *= updateOpacity; diff --git a/filament/src/materials/dof/dofCoc.mat b/filament/src/materials/dof/dofCoc.mat index c13766c81b..da7a7b830b 100644 --- a/filament/src/materials/dof/dofCoc.mat +++ b/filament/src/materials/dof/dofCoc.mat @@ -31,14 +31,9 @@ material { type : float4 }, { - name : background, + name : coc, target : color, - type : float4 - }, - { - name : cocFgBg, - target : color, - type : float2 + type : float } ], domain : postprocess, @@ -64,26 +59,30 @@ void dummy(){} void postProcess(inout PostProcessInputs postProcess) { highp vec2 uv = variable_vertex.xy; - vec4 color = textureLod(materialParams_color, uv, 0.0); + vec4 outColor = textureLod(materialParams_color, uv, 0.0); float d = textureLod(materialParams_depth, uv, 0.0).r; // Get the CoC radius. // We multiply by 0.5 to convert from diameter to radius. - float coc = getCOC(d, materialParams.cocParams) * 0.5; + float outCoc = getCOC(d, materialParams.cocParams) * 0.5; + + // Clamp to maximum allowable CoC radius. + outCoc = clamp(outCoc, -MAX_COC_RADIUS, MAX_COC_RADIUS); // If CoC is less that 0.5 full resolution pixel, we clamp to 0, this will reduce // artifacts in the "in focus" area and allow us to skip more tiles trivially. - if (abs(coc) < MAX_IN_FOCUS_COC) { - coc = 0.0; + if (abs(outCoc) < MAX_IN_FOCUS_COC) { + outCoc = 0.0; } - // Clamp to maximum allowable CoC radius. - coc = clamp(coc, -MAX_COC_RADIUS, MAX_COC_RADIUS); +#if POST_PROCESS_OPAQUE + // kill the work performed above + outColor.a = 1.0; +#endif // Output to MRTs - postProcess.color = color; - postProcess.background = color; - postProcess.cocFgBg = vec2(coc, coc); + postProcess.color = outColor; + postProcess.coc = outCoc; } } diff --git a/filament/src/materials/dof/dofCombine.mat b/filament/src/materials/dof/dofCombine.mat index b8b81daaf7..c52063d350 100644 --- a/filament/src/materials/dof/dofCombine.mat +++ b/filament/src/materials/dof/dofCombine.mat @@ -64,9 +64,9 @@ void postProcess(inout PostProcessInputs postProcess) { highp vec2 uv0 = variable_vertex.xy; highp vec2 uv1 = variable_vertex.xy * materialParams.uvscale.xy; float alpha = textureLod(materialParams_alpha, uv1, 0.0).r; - vec4 foreground = textureLod(materialParams_dof, uv1, 0.0); + vec4 dof = textureLod(materialParams_dof, uv1, 0.0); color = textureLod(materialParams_color, uv0, 0.0); - color = foreground + (1.0 - alpha) * color; + color = dof + (1.0 - alpha) * color; } #if POST_PROCESS_OPAQUE diff --git a/filament/src/materials/dof/dofDilate.mat b/filament/src/materials/dof/dofDilate.mat index 5b7339fa29..5b194760f7 100644 --- a/filament/src/materials/dof/dofDilate.mat +++ b/filament/src/materials/dof/dofDilate.mat @@ -35,20 +35,8 @@ vec2 tap(const vec2 uv, const vec2 offset) { } vec2 dilate(inout vec2 center, const vec2 tap) { - // 2.24 tiles is the longest distance between 2 pixels of adjacent tiles. - // sqrt(2^2 + 1^2) = 2.24 - const float maxDistance = max(-MAX_COC_RADIUS, -2.24 * TILE_SIZE); - // Tiles that can affect us need to transfer their CoC to us (for the foreground it's // min(ourCoc, theirCoc) because CoC are negative (the min is the larger radius). - - // TODO: However, if their CoC is very large, we can limit it by the distance to that - // tile, because we only need to reach *them*). - // The difficuty here is the this clamping depends on if we're negative or positive. - // e.g. for positive values - // center.g = min(center.g, max(tap.g, maxDistance)); - // center.r = max(center.r, max(tap.r, maxDistance)); - center.r = min(center.r, tap.r); center.g = max(center.g, tap.g); return center; diff --git a/filament/src/materials/dof/dofDownsample.mat b/filament/src/materials/dof/dofDownsample.mat index b7351ef2be..8b4073fd5a 100644 --- a/filament/src/materials/dof/dofDownsample.mat +++ b/filament/src/materials/dof/dofDownsample.mat @@ -31,14 +31,9 @@ material { type : float4 }, { - name : background, + name : coc, target : color, - type : float4 - }, - { - name : cocFgBg, - target : color, - type : float2 + type : float } ], domain : postprocess, @@ -73,63 +68,45 @@ void postProcess(inout PostProcessInputs postProcess) { vec4 hdr; // fireflies/flickering filtering - hdr.x = 1.0 / (1.0 + max3(s00.rgb)); - hdr.y = 1.0 / (1.0 + max3(s11.rgb)); - hdr.z = 1.0 / (1.0 + max3(s10.rgb)); - hdr.w = 1.0 / (1.0 + max3(s00.rgb)); + hdr[0] = 1.0 / (1.0 + max3(s01.rgb)); + hdr[1] = 1.0 / (1.0 + max3(s11.rgb)); + hdr[2] = 1.0 / (1.0 + max3(s10.rgb)); + hdr[3] = 1.0 / (1.0 + max3(s00.rgb)); vec4 d; #if defined(TARGET_VULKAN_ENVIRONMENT) || !defined(TARGET_MOBILE) d = textureGather(materialParams_depth, uv, 0); // 01, 11, 10, 00 #else - d.x = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(0, 1)).r; - d.y = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(1, 1)).r; - d.z = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(1, 0)).r; - d.w = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(0, 0)).r; + d[0] = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(0, 1)).r; + d[1] = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(1, 1)).r; + d[2] = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(1, 0)).r; + d[3] = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(0, 0)).r; #endif // Get the CoC radius of each four samples to downsample. // We multiply by 0.5 to convert from diameter to radius. - vec4 w = getCOC(d, materialParams.cocParams) * 0.5; + vec4 c = getCOC(d, materialParams.cocParams) * 0.5; + float outCoc = downsampleCoC(c); + vec4 w = downsampleCocWeights(c, outCoc, 1.0) * hdr; + vec4 outColor = (s01 * w[0] + s11 * w[1] + s10 * w[2] + s00 * w[3]) * rcp(dot(vec4(1.0), w)); - // We calculates the downsampled CoC to be conservative, that is if any - // of the 4 texels are foreground or background, then the whole new texel is too. - float fgCoc = min2(min(w.xy, w.zw)); - - // The forground bilateral weight is calculated as saturate(1.0 - (fgCoc - w)), which always - // yields 1.0. Note the "missing" abs(), this is because we want to let the background layer - // leak into the foreground layer, to avoid aliasing artifacts. - vec4 foreground = (s01 * hdr.x + s11 * hdr.y + s10 * hdr.z + s00 * hdr.w) * (1.0 / (hdr.x + hdr.y + hdr.z + hdr.w)); - - // the background bilateral weight is calculated as saturate(1.0 - abs(bgCoc - w)), but the - // abs() is not needed since bgCoc - w is guaranteed to be >= 0. - float bgCoc = max2(max(w.xy, w.zw)); - vec4 bw = saturate(1.0 - (bgCoc - w)) * hdr; - vec4 background = (s01 * bw.x + s11 * bw.y + s10 * bw.z + s00 * bw.w) * (1.0 / (bw.x + bw.y + bw.z + bw.w)); + // Clamp to maximum allowable CoC radius. + outCoc = clamp(outCoc, -MAX_COC_RADIUS, MAX_COC_RADIUS); // If CoC is less that 0.5 full resolution pixel, we clamp to 0, this will reduce // artifacts in the "in focus" area and allow us to skip more tiles trivially. - if (abs(fgCoc) < MAX_IN_FOCUS_COC) { - fgCoc = 0.0; + if (abs(outCoc) < MAX_IN_FOCUS_COC) { + outCoc = 0.0; } - if (abs(bgCoc) < MAX_IN_FOCUS_COC){ - bgCoc = 0.0; - } - - // Clamp to maximum allowable CoC radius. - fgCoc = clamp(fgCoc, -MAX_COC_RADIUS, MAX_COC_RADIUS); - bgCoc = clamp(bgCoc, -MAX_COC_RADIUS, MAX_COC_RADIUS); #if POST_PROCESS_OPAQUE // kill the work performed above - foreground.a = 1.0; - background.a = 1.0; + outColor.a = 1.0; #endif // Output to MRTs - postProcess.color = foreground; - postProcess.background = background; - postProcess.cocFgBg = vec2(fgCoc, bgCoc); + postProcess.color = outColor; + postProcess.coc = outCoc; } } diff --git a/filament/src/materials/dof/dofMipmap.mat b/filament/src/materials/dof/dofMipmap.mat index 249b3f4ba3..7d6ee4373f 100644 --- a/filament/src/materials/dof/dofMipmap.mat +++ b/filament/src/materials/dof/dofMipmap.mat @@ -3,17 +3,12 @@ material { parameters : [ { type : sampler2d, - name : foreground, + name : color, precision: medium }, { type : sampler2d, - name : background, - precision: medium - }, - { - type : sampler2d, - name : cocFgBg, + name : coc, precision: medium }, { @@ -35,14 +30,9 @@ material { type : float4 }, { - name : background, + name : coc, target : color, - type : float4 - }, - { - name : cocFgBg, - target : color, - type : float2 + type : float } ], domain : postprocess, @@ -63,7 +53,7 @@ fragment { void dummy(){} void postProcess(inout PostProcessInputs postProcess) { - highp vec2 pixelSize = 1.0 / vec2(textureSize(materialParams_foreground, materialParams.mip)); + highp vec2 pixelSize = 1.0 / vec2(textureSize(materialParams_color, materialParams.mip)); highp vec2 uv = variable_vertex.xy - pixelSize.xy * 0.5; // the source is guaranteed to be a multiple of two, so we now the bilinear weights are 0.25 @@ -72,60 +62,30 @@ void postProcess(inout PostProcessInputs postProcess) { float weightScale = materialParams.weightScale; float mip = float(materialParams.mip); - // fetch the 4 corresponding CoC - vec4 d0, d1; - d0.xy = textureLodOffset(materialParams_cocFgBg, uv, mip, ivec2(0, 1)).rg; - d0.zw = textureLodOffset(materialParams_cocFgBg, uv, mip, ivec2(1, 1)).rg; - d1.xy = textureLodOffset(materialParams_cocFgBg, uv, mip, ivec2(1, 0)).rg; - d1.zw = textureLodOffset(materialParams_cocFgBg, uv, mip, ivec2(0, 0)).rg; - vec4 fw = vec4(d0.xz, d1.xz); - vec4 bw = vec4(d0.yw, d1.yw); + vec4 s01 = textureLodOffset(materialParams_color, uv, mip, ivec2(0, 1)); + vec4 s11 = textureLodOffset(materialParams_color, uv, mip, ivec2(1, 1)); + vec4 s10 = textureLodOffset(materialParams_color, uv, mip, ivec2(1, 0)); + vec4 s00 = textureLodOffset(materialParams_color, uv, mip, ivec2(0, 0)); - /* - * foreground - */ + // fetch the 4 corresponding CoC (textureGather with LOD doesn't exist) + vec4 c; + c[0] = textureLodOffset(materialParams_coc, uv, mip, ivec2(0, 1)).r; + c[1] = textureLodOffset(materialParams_coc, uv, mip, ivec2(1, 1)).r; + c[2] = textureLodOffset(materialParams_coc, uv, mip, ivec2(1, 0)).r; + c[3] = textureLodOffset(materialParams_coc, uv, mip, ivec2(0, 0)).r; - vec4 fg01 = textureLodOffset(materialParams_foreground, uv, mip, ivec2(0, 1)); - vec4 fg11 = textureLodOffset(materialParams_foreground, uv, mip, ivec2(1, 1)); - vec4 fg10 = textureLodOffset(materialParams_foreground, uv, mip, ivec2(1, 0)); - vec4 fg00 = textureLodOffset(materialParams_foreground, uv, mip, ivec2(0, 0)); - - // We calculates the downsampled CoC to be conservative, that is if any - // of the 4 texels are foreground or background, then the whole new texel is too. - float fgCoc = min4(fw); - - // The forground bilateral weight is calculated as saturate(1.0 - (fgCoc - w)), which always - // yields 1.0. Note the "missing" abs(), this is because we want to let the background layer - // leak into the foreground layer, to avoid aliasing artifacts. - vec4 foreground = (fg01 + fg11 + fg10 + fg00) * 0.25; - - /* - * background - */ - - vec4 bg01 = textureLodOffset(materialParams_background, uv, mip, ivec2(0, 1)); - vec4 bg11 = textureLodOffset(materialParams_background, uv, mip, ivec2(1, 1)); - vec4 bg10 = textureLodOffset(materialParams_background, uv, mip, ivec2(1, 0)); - vec4 bg00 = textureLodOffset(materialParams_background, uv, mip, ivec2(0, 0)); - - float bgCoc = max4(bw); - - // The background bilateral weight is calculated as saturate(1.0 - abs(bgCoc - w)), - // but the abs() is not needed since bgCoc - w is guaranteed to be >= 0. - bw = saturate(1.0 - (bgCoc - bw) * weightScale); - float bgScale = 1.0 / (bw.x + bw.y + bw.z + bw.w); - vec4 background = (bg01 * bw.x + bg11 * bw.y + bg10 * bw.z + bg00 * bw.w) * bgScale; + float outCoc = downsampleCoC(c); + vec4 w = downsampleCocWeights(c, outCoc, weightScale); + vec4 outColor = (s01 * w[0] + s11 * w[1] + s10 * w[2] + s00 * w[3]) * rcp(dot(vec4(1.0), w)); #if POST_PROCESS_OPAQUE // kill the work performed above - foreground.a = 1.0; - background.a = 1.0; + outColor.a = 1.0; #endif // Output to MRTs - postProcess.color = foreground; - postProcess.background = background; - postProcess.cocFgBg = vec2(fgCoc, bgCoc); + postProcess.color = outColor; + postProcess.coc = outCoc; } } diff --git a/filament/src/materials/dof/dofTiles.mat b/filament/src/materials/dof/dofTiles.mat index 8ff850d1c3..b84a030eac 100644 --- a/filament/src/materials/dof/dofTiles.mat +++ b/filament/src/materials/dof/dofTiles.mat @@ -3,7 +3,7 @@ material { parameters : [ { type : sampler2d, - name : cocMaxMin, + name : cocMinMax, precision: medium }, { @@ -42,27 +42,28 @@ float min4(const vec4 f) { void postProcess(inout PostProcessInputs postProcess) { highp vec2 uv = variable_vertex.xy; - highp vec2 size = vec2(textureSize(materialParams_cocMaxMin, 0)); + highp vec2 size = vec2(textureSize(materialParams_cocMinMax, 0)); ivec2 i = ivec2(uv * size - 0.5); + // note: the first time, the source texture is swizzled -- so .r and .g are the same channel #if defined(TARGET_VULKAN_ENVIRONMENT) || !defined(TARGET_MOBILE) - vec4 f = textureGather(materialParams_cocMaxMin, uv, 0); // 01, 11, 10, 00 - vec4 b = textureGather(materialParams_cocMaxMin, uv, 1); // 01, 11, 10, 00 + vec4 mi = textureGather(materialParams_cocMinMax, uv, 0); // 01, 11, 10, 00 + vec4 ma = textureGather(materialParams_cocMinMax, uv, 1); // 01, 11, 10, 00 #else - vec2 s01 = texelFetchOffset(materialParams_cocMaxMin, i, 0, ivec2(0, 1)).rg; - vec2 s11 = texelFetchOffset(materialParams_cocMaxMin, i, 0, ivec2(1, 1)).rg; - vec2 s10 = texelFetchOffset(materialParams_cocMaxMin, i, 0, ivec2(1, 0)).rg; - vec2 s00 = texelFetchOffset(materialParams_cocMaxMin, i, 0, ivec2(0, 0)).rg; - vec4 f = vec4(s01.r, s11.r, s10.r, s00.r); - vec4 b = vec4(s01.g, s11.g, s10.g, s00.g); + vec2 s01 = texelFetchOffset(materialParams_cocMinMax, i, 0, ivec2(0, 1)).rg; + vec2 s11 = texelFetchOffset(materialParams_cocMinMax, i, 0, ivec2(1, 1)).rg; + vec2 s10 = texelFetchOffset(materialParams_cocMinMax, i, 0, ivec2(1, 0)).rg; + vec2 s00 = texelFetchOffset(materialParams_cocMinMax, i, 0, ivec2(0, 0)).rg; + vec4 mi = vec4(s01.r, s11.r, s10.r, s00.r); + vec4 ma = vec4(s01.g, s11.g, s10.g, s00.g); #endif // compute tile's min CoC - postProcess.color.r = min4(min(f, b)); + postProcess.color.r = min4(mi); // compute tile's max CoC - postProcess.color.g = max4(max(f, b)); + postProcess.color.g = max4(ma); } } diff --git a/filament/src/materials/dof/dofUtils.fs b/filament/src/materials/dof/dofUtils.fs index 56db650221..d577ce672c 100644 --- a/filament/src/materials/dof/dofUtils.fs +++ b/filament/src/materials/dof/dofUtils.fs @@ -96,3 +96,23 @@ bool isTrivialTile(const vec2 tiles) { float maxCocRadius = max(abs(tiles.r), abs(tiles.g)); return maxCocRadius < MAX_IN_FOCUS_COC; } + +float downsampleCoC(const vec4 c) { + // We need to compute a suitable CoC to represent the 4 pixels that are downsampled. + // We pick the min because this always selects the most foreground sample if there is one, + // because the foreground can leak on the background, but not the reverse. + return min4(c); +} + +vec4 downsampleCocWeights(const vec4 c, const float outCoc, const float scale) { + // The bilateral weight is normally computed as saturate(1 - |outCoc - c|) which selects + // the sample with the outCoc weight (and does a little bit of cross-fade if other samples + // are close). However, this can also cause some aliasing with dithered objects, so by + // omitting the abs() we allow samples in the background to "leak" into the foreground, which + // is not completely different from the "mirror hole filling" of the DoF pass. + // See "Life of a Bokeh" by Guillaume Abadie, SIGGRAPH 2018. + + // Note: With the implementation of downsampleCoC() above, this always resolves to 1.0; + // return saturate(1.0 - (outCoc - c) * scale); + return vec4(1.0); +}