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.
This commit is contained in:
committed by
Mathias Agopian
parent
c4eb790065
commit
e5bfe81d0c
@@ -940,9 +940,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
|
||||
struct PostProcessDofDownsample {
|
||||
FrameGraphId<FrameGraphTexture> color;
|
||||
FrameGraphId<FrameGraphTexture> depth;
|
||||
FrameGraphId<FrameGraphTexture> outForeground;
|
||||
FrameGraphId<FrameGraphTexture> outBackground;
|
||||
FrameGraphId<FrameGraphTexture> outCocFgBg;
|
||||
FrameGraphId<FrameGraphTexture> outColor;
|
||||
FrameGraphId<FrameGraphTexture> outCoc;
|
||||
};
|
||||
|
||||
auto& ppDoFDownsample = fg.addPass<PostProcessDofDownsample>("DoF Downsample",
|
||||
@@ -950,21 +949,21 @@ FrameGraphId<FrameGraphTexture> 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<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
|
||||
*/
|
||||
|
||||
struct PostProcessDofMipmap {
|
||||
FrameGraphId<FrameGraphTexture> inOutForeground;
|
||||
FrameGraphId<FrameGraphTexture> inOutBackground;
|
||||
FrameGraphId<FrameGraphTexture> inOutCocFgBg;
|
||||
FrameGraphId<FrameGraphTexture> inOutColor;
|
||||
FrameGraphId<FrameGraphTexture> inOutCoc;
|
||||
uint32_t rp[maxMipLevels];
|
||||
};
|
||||
|
||||
@@ -1000,25 +998,22 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
|
||||
|
||||
auto& ppDoFMipmap = fg.addPass<PostProcessDofMipmap>("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<FrameGraphTexture> 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<FrameGraphTexture> 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<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
|
||||
const size_t tileReductionCount = std::log2(tileSize / dofResolution);
|
||||
|
||||
struct PostProcessDofTiling1 {
|
||||
FrameGraphId<FrameGraphTexture> inCocMaxMin;
|
||||
FrameGraphId<FrameGraphTexture> outTilesCocMaxMin;
|
||||
FrameGraphId<FrameGraphTexture> inCocMinMax;
|
||||
FrameGraphId<FrameGraphTexture> outTilesCocMinMax;
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < tileReductionCount; i++) {
|
||||
@@ -1076,29 +1069,29 @@ FrameGraphId<FrameGraphTexture> 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<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
|
||||
auto dilate = [&](FrameGraphId<FrameGraphTexture> input) -> FrameGraphId<FrameGraphTexture> {
|
||||
|
||||
struct PostProcessDofDilate {
|
||||
FrameGraphId<FrameGraphTexture> inTilesCocMaxMin;
|
||||
FrameGraphId<FrameGraphTexture> outTilesCocMaxMin;
|
||||
FrameGraphId<FrameGraphTexture> inTilesCocMinMax;
|
||||
FrameGraphId<FrameGraphTexture> outTilesCocMinMax;
|
||||
};
|
||||
|
||||
auto& ppDoFDilate = fg.addPass<PostProcessDofDilate>("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<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
|
||||
*/
|
||||
|
||||
struct PostProcessDof {
|
||||
FrameGraphId<FrameGraphTexture> foreground;
|
||||
FrameGraphId<FrameGraphTexture> background;
|
||||
FrameGraphId<FrameGraphTexture> cocFgBg;
|
||||
FrameGraphId<FrameGraphTexture> tilesCocMaxMin;
|
||||
FrameGraphId<FrameGraphTexture> outForeground;
|
||||
FrameGraphId<FrameGraphTexture> color;
|
||||
FrameGraphId<FrameGraphTexture> coc;
|
||||
FrameGraphId<FrameGraphTexture> tilesCocMinMax;
|
||||
FrameGraphId<FrameGraphTexture> outColor;
|
||||
FrameGraphId<FrameGraphTexture> outAlpha;
|
||||
};
|
||||
|
||||
auto& ppDoF = fg.addPass<PostProcessDof>("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<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
|
||||
|
||||
|
||||
struct PostProcessDofMedian {
|
||||
FrameGraphId<FrameGraphTexture> inForeground;
|
||||
FrameGraphId<FrameGraphTexture> inColor;
|
||||
FrameGraphId<FrameGraphTexture> inAlpha;
|
||||
FrameGraphId<FrameGraphTexture> tilesCocMaxMin;
|
||||
FrameGraphId<FrameGraphTexture> outForeground;
|
||||
FrameGraphId<FrameGraphTexture> tilesCocMinMax;
|
||||
FrameGraphId<FrameGraphTexture> outColor;
|
||||
FrameGraphId<FrameGraphTexture> outAlpha;
|
||||
};
|
||||
|
||||
auto& ppDoFMedian = fg.addPass<PostProcessDofMedian>("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<FrameGraphTexture> 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<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
|
||||
FrameGraphId<FrameGraphTexture> color;
|
||||
FrameGraphId<FrameGraphTexture> dof;
|
||||
FrameGraphId<FrameGraphTexture> alpha;
|
||||
FrameGraphId<FrameGraphTexture> tilesCocMaxMin;
|
||||
FrameGraphId<FrameGraphTexture> tilesCocMinMax;
|
||||
FrameGraphId<FrameGraphTexture> output;
|
||||
};
|
||||
|
||||
auto& ppDoFCombine = fg.addPass<PostProcessDofCombine>("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<FrameGraphTexture> 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)),
|
||||
|
||||
@@ -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<FrameGraphTexture>("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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user