bgfx/examples/43-denoise/fs_denoise_txaa.sc
elvencache 4d272f4104
bokeh depth of field (#2372)
* Implement bokeh depth of field

Implement bokeh depth of field as described in the blog post here:
https://blog.tuxedolabs.com/2018/05/04/bokeh-depth-of-field-in-single-pass.html

Additionally, implement the optimizations discussed in the closing paragraph. Apply the effect in multiple passes. Calculate the circle of confusion and store in the alpha channel while downsampling the image. Then compute depth of field at this lower res, storing sample size in alpha. Then composite the blurred image, based on the sample size. Compositing the lower res like this can lead to blocky edges where there's a depth discontinuity and the blur is just enough. May be an area to improve on.

Provide an alternate means of determining radius of current sample when blurring. I find the blog post's sample pattern to be difficult to directly reason about. It is not obvious, given the parameters, how many samples will be taken. And it can be very many samples. Though the results are good. The 'sqrt' pattern chosen here looks alright and allows for the number of samples to be set directly. If you are going to use this in a project, may be worth exploring additional sample patterns. And certainly update the shader to remove the pattern choice from inside the sample loop.

* fix typo in shader of denoise example

copy/paste error, applying y offset to x component instead
2021-01-31 09:59:55 -08:00

209 lines
6.4 KiB
Scala

$input v_texcoord0
/*
* Copyright 2021 elven cache. All rights reserved.
* License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
*/
#include "../common/common.sh"
#include "parameters.sh"
#include "shared_functions.sh"
#define APPLY_TXAA_IN_LINEAR 0
#define DEBUG_HALF_SCREEN 0
SAMPLER2D(s_color, 0); // this frame's shaded color
SAMPLER2D(s_previousColor, 1); // previous frame's shaded color
SAMPLER2D(s_velocity, 2); // screenspace delta from previous to current frame
SAMPLER2D(s_depth, 3); // depth buffer
vec3 FindNearestDepth(sampler2D _depthSampler, vec2 _texCoord) {
vec2 du = vec2(u_viewTexel.x, 0.0);
vec2 dv = vec2(0.0, u_viewTexel.y);
vec2 coord = _texCoord - du - dv;
vec3 tcd0 = vec3(coord, texture2D(_depthSampler, coord).x);
coord = _texCoord - dv;
vec3 tcd1 = vec3(coord, texture2D(_depthSampler, coord).x);
coord = _texCoord + du - dv;
vec3 tcd2 = vec3(coord, texture2D(_depthSampler, coord).x);
coord = _texCoord + du;
vec3 tcd3 = vec3(coord, texture2D(_depthSampler, coord).x);
coord = _texCoord;
vec3 tcd4 = vec3(coord, texture2D(_depthSampler, coord).x);
coord = _texCoord - du;
vec3 tcd5 = vec3(coord, texture2D(_depthSampler, coord).x);
coord = _texCoord - du + dv;
vec3 tcd6 = vec3(coord, texture2D(_depthSampler, coord).x);
coord = _texCoord + dv;
vec3 tcd7 = vec3(coord, texture2D(_depthSampler, coord).x);
coord = _texCoord + du + dv;
vec3 tcd8 = vec3(coord, texture2D(_depthSampler, coord).x);
vec3 minTcd = tcd0;
if (tcd1.z < minTcd.z) minTcd = tcd1;
if (tcd2.z < minTcd.z) minTcd = tcd2;
if (tcd3.z < minTcd.z) minTcd = tcd3;
if (tcd4.z < minTcd.z) minTcd = tcd4;
if (tcd5.z < minTcd.z) minTcd = tcd5;
if (tcd6.z < minTcd.z) minTcd = tcd6;
if (tcd7.z < minTcd.z) minTcd = tcd7;
if (tcd8.z < minTcd.z) minTcd = tcd8;
return minTcd;
}
float Mitchell (float _b, float _c, float _x) {
float v = 0.0;
float x = abs(_x);
float x2 = x*x;
float x3 = x2*x;
if (x < 1.0) {
v = (12.0-9.0*_b-6.0*_c)*x3 + (-18.0+12.0*_b+6.0*_c)*x2 + (6.0-2.0*_b);
}
else if (x < 2.0) {
v = (-_b-6.0*_c)*x3 + (6.0*_b+30.0*_c)*x2 + (-12.0*_b-48.0*_c)*x + (8.0*_b+24.0*_c);
}
return v*(1.0/6.0);
}
void main()
{
vec2 texCoord = v_texcoord0;
vec3 colorCurr = texture2D(s_color, texCoord).xyz;
#if DEBUG_HALF_SCREEN
if (texCoord.x > 0.5) {
#endif
vec3 nearestCoordAndDepth = FindNearestDepth(s_depth, texCoord);
vec2 velocity = texture2D(s_velocity, nearestCoordAndDepth.xy).xy;
vec2 texCoordPrev = GetTexCoordPrevious(texCoord, velocity);
vec3 colorPrev = texture2D(s_previousColor, texCoordPrev).xyz;
// Sample local neighborhood for variance clipping
vec2 du = vec2(u_viewTexel.x, 0.0);
vec2 dv = vec2(0.0, u_viewTexel.y);
vec3 colorUL = texture2D(s_color, texCoord - du - dv).xyz;
vec3 colorUp = texture2D(s_color, texCoord - dv).xyz;
vec3 colorUR = texture2D(s_color, texCoord + du - dv).xyz;
vec3 colorRi = texture2D(s_color, texCoord + du ).xyz;
vec3 colorLe = texture2D(s_color, texCoord - du ).xyz;
vec3 colorDL = texture2D(s_color, texCoord - du + dv).xyz;
vec3 colorDo = texture2D(s_color, texCoord + dv).xyz;
vec3 colorDR = texture2D(s_color, texCoord + du + dv).xyz;
// in an ideal world, lighting and such is in linear space,
// would possibly want to convert to gamma and apply txaa
// there. but this sample isn't storing intermediate results
// in linear space (or doing any reasonable lighting) so
// would possibly want to do the opposite.
#if APPLY_TXAA_IN_LINEAR
colorCurr = toLinear(colorCurr);
colorPrev = toLinear(colorPrev);
colorUL = toLinear(colorUL);
colorUp = toLinear(colorUp);
colorUR = toLinear(colorUR);
colorLe = toLinear(colorLe);
colorRi = toLinear(colorRi);
colorDL = toLinear(colorDL);
colorDo = toLinear(colorDo);
colorDR = toLinear(colorDR);
#endif
// Compute variance box on color neighborhood, clip to box
float outVal = 0.0;
{
vec3 m1 = vec3_splat(0.0);
vec3 m2 = vec3_splat(0.0);
m1 += colorUL; m2 += colorUL*colorUL;
m1 += colorUp; m2 += colorUp*colorUp;
m1 += colorUR; m2 += colorUR*colorUR;
m1 += colorLe; m2 += colorLe*colorLe;
m1 += colorCurr; m2 += colorCurr*colorCurr;
m1 += colorRi; m2 += colorRi*colorRi;
m1 += colorDL; m2 += colorDL*colorDL;
m1 += colorDo; m2 += colorDo*colorDo;
m1 += colorDR; m2 += colorDR*colorDR;
m1 *= (1.0/9.0);
m2 *= (1.0/9.0);
vec3 var = max(vec3_splat(0.0), m2 - m1*m1);
vec3 sigma = sqrt(var);
outVal = max(sigma.x, max(sigma.y, sigma.z));
sigma *= 1.4;
vec3 colorMin = m1 - sigma;
vec3 colorMax = m1 + sigma;
vec3 displacement = colorPrev - m1;
vec3 units = abs(displacement / sigma);
float maxUnit = max(max(units.x, units.y), max(units.z, 1.0));
colorPrev = m1 + displacement * (1.0/maxUnit);
}
float lumaCurr = dot(colorCurr, vec3(0.3, 0.6, 0.1));
float lumaPrev = dot(colorPrev, vec3(0.3, 0.6, 0.1));
// adjust feedback/blend amount depending on color difference
float r = abs(lumaCurr-lumaPrev) / max(max(lumaCurr, lumaPrev), 0.2);
r = 1.0-r;
r = r*r;
float feedback = mix(u_feedbackMin, u_feedbackMax, r);
vec3 colorOut = mix(colorCurr, colorPrev, feedback);
// optionally blur current color, since we've already taken
// the samples to build the variance window. could use more
// blur when feedback is lower, to replace temporal accumulation
// with spatial accumulation. or could use filter to sharpen.
if (u_applyMitchellFilter > 0.0)
{
// adjust filter coefficients depending on color difference
float b = mix(3.0/2.0, 1.0/3.0, r);
float c = mix(-1.0/4.0, 1.0/3.0, r);
float m0 = Mitchell(b, c, 0.0);
float m1 = Mitchell(b, c, 1.0);
float m2 = Mitchell(b, c, sqrt(2.0));
vec3 colorFilter = m0 * colorCurr;
colorFilter += m1 * colorLe;
colorFilter += m1 * colorRi;
colorFilter += m1 * colorUp;
colorFilter += m1 * colorDo;
colorFilter += m2 * colorUL;
colorFilter += m2 * colorUR;
colorFilter += m2 * colorDL;
colorFilter += m2 * colorDR;
colorFilter *= 1.0/(m0 + 4.0*m1 + 4.0*m2);
colorOut = mix(colorFilter, colorPrev, feedback);
}
// in an ideal world, lighting and such is in linear space,
// would possibly want to convert to gamma and apply txaa
// there. but this sample isn't storing intermediate results
// in linear space (or doing any reasonable lighting) so
// would possibly want to do the opposite.
#if APPLY_TXAA_IN_LINEAR
colorCurr = toGamma(colorOut);
#else
colorCurr = colorOut;
#endif
#if DEBUG_HALF_SCREEN
}
#endif
gl_FragColor = vec4(colorCurr, 1.0);
}