diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2021-02-13 00:35:18 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2021-02-13 00:35:52 +0300 |
commit | 000a340afa67a12a38c103e19f7d52ed42c4492e (patch) | |
tree | d59ea49aedfba450d7de20240004399e1ee30dc8 /source/blender/draw/intern/shaders | |
parent | dd2ff266acf999dbb3a37b70162f5ca7c029426a (diff) |
EEVEE: Depth of field: New implementation
This is a complete refactor over the old system. The goal was to increase quality
first and then have something more flexible and optimised.
|{F9603145} | {F9603142}|{F9603147}|
This fixes issues we had with the old system which were:
- Too much overdraw (low performance).
- Not enough precision in render targets (hugly color banding/drifting).
- Poor resolution near in-focus regions.
- Wrong support of orthographic views.
- Missing alpha support in viewport.
- Missing bokeh shape inversion on foreground field.
- Issues on some GPUs. (see T72489) (But I'm sure this one will have other issues as well heh...)
- Fix T81092
I chose Unreal's Diaphragm DOF as a reference / goal implementation.
It is well described in the presentation "A Life of a Bokeh" by Guillaume Abadie.
You can check about it here https://epicgames.ent.box.com/s/s86j70iamxvsuu6j35pilypficznec04
Along side the main implementation we provide a way to increase the quality by jittering the
camera position for each sample (the ones specified under the Sampling tab).
The jittering is dividing the actual post processing dof radius so that it fills the undersampling.
The user can still add more overblur to have a noiseless image, but reducing bokeh shape sharpness.
Effect of overblur (left without, right with):
| {F9603122} | {F9603123}|
The actual implementation differs a bit:
- Foreground gather implementation uses the same "ring binning" accumulator as background
but uses a custom occlusion method. This gives the problem of inflating the foreground elements
when they are over background or in-focus regions.
This is was a hard decision but this was preferable to the other method that was giving poor
opacity masks for foreground and had other more noticeable issues. Do note it is possible
to improve this part in the future if a better alternative is found.
- Use occlusion texture for foreground. Presentation says it wasn't really needed for them.
- The TAA stabilisation pass is replace by a simple neighborhood clamping at the reduce copy
stage for simplicity.
- We don't do a brute-force in-focus separate gather pass. Instead we just do the brute force
pass during resolve. Using the separate pass could be a future optimization if needed but
might give less precise results.
- We don't use compute shaders at all so shader branching might not be optimal. But performance
is still way better than our previous implementation.
- We mainly rely on density change to fix all undersampling issues even for foreground (which
is something the reference implementation is not doing strangely).
Remaining issues (not considered blocking for me):
- Slight defocus stability: Due to slight defocus bruteforce gather using the bare scene color,
highlights are dilated and make convergence quite slow or imposible when using jittered DOF
(or gives )
- ~~Slight defocus inflating: There seems to be a 1px inflation discontinuity of the slight focus
convolution compared to the half resolution. This is not really noticeable if using jittered
camera.~~ Fixed
- Foreground occlusion approximation is a bit glitchy and gives incorrect result if the
a defocus foreground element overlaps a farther foreground element. Note that this is easily
mitigated using the jittered camera position.
|{F9603114}|{F9603115}|{F9603116}|
- Foreground is inflating, not revealing background. However this avoids some other bugs too
as discussed previously. Also mitigated with jittered camera position.
|{F9603130}|{F9603129}|
- Sensor vertical fit is still broken (does not match cycles).
- Scattred bokeh shapes can be a bit strange at polygon vertices. This is due to the distance field
stored in the Bokeh LUT which is not rounded at the edges. This is barely noticeable if the
shape does not rotate.
- ~~Sampling pattern of the jittered camera position is suboptimal. Could try something like hammersley
or poisson disc distribution.~~Used hexaweb sampling pattern which is not random but has better
stability and overall coverage.
- Very large bokeh (> 300 px) can exhibit undersampling artifact in gather pass and quite a bit of
bleeding. But at this size it is preferable to use jittered camera position.
Codewise the changes are pretty much self contained and each pass are well documented.
However the whole pipeline is quite complex to understand from bird's-eye view.
Notes:
- There is the possibility of using arbitrary bokeh texture with this implementation.
However implementation is a bit involved.
- Gathering max sample count is hardcoded to avoid to deal with shader variations. The actual
max sample count is already quite high but samples are not evenly distributed due to the
ring binning method.
- While this implementation does not need 32bit/channel textures to render correctly it does use
many other textures so actual VRAM usage is higher than previous method for viewport but less
for render. Textures are reused to avoid many allocations.
- Bokeh LUT computation is fast and done for each redraw because it can be animated. Also the
texture can be shared with other viewport with different camera settings.
Diffstat (limited to 'source/blender/draw/intern/shaders')
-rw-r--r-- | source/blender/draw/intern/shaders/common_math_lib.glsl | 52 |
1 files changed, 41 insertions, 11 deletions
diff --git a/source/blender/draw/intern/shaders/common_math_lib.glsl b/source/blender/draw/intern/shaders/common_math_lib.glsl index a82e0b5a5e9..9cb1723346d 100644 --- a/source/blender/draw/intern/shaders/common_math_lib.glsl +++ b/source/blender/draw/intern/shaders/common_math_lib.glsl @@ -3,12 +3,14 @@ /** \name Common Math Utilities * \{ */ -#define M_PI 3.14159265358979323846 /* pi */ -#define M_2PI 6.28318530717958647692 /* 2*pi */ -#define M_PI_2 1.57079632679489661923 /* pi/2 */ -#define M_1_PI 0.318309886183790671538 /* 1/pi */ -#define M_1_2PI 0.159154943091895335768 /* 1/(2*pi) */ -#define M_1_PI2 0.101321183642337771443 /* 1/(pi^2) */ +#define M_PI 3.14159265358979323846 /* pi */ +#define M_2PI 6.28318530717958647692 /* 2*pi */ +#define M_PI_2 1.57079632679489661923 /* pi/2 */ +#define M_1_PI 0.318309886183790671538 /* 1/pi */ +#define M_1_2PI 0.159154943091895335768 /* 1/(2*pi) */ +#define M_1_PI2 0.101321183642337771443 /* 1/(pi^2) */ +#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */ +#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */ #define FLT_MAX 3.402823e+38 vec3 mul(mat3 m, vec3 v) @@ -33,6 +35,13 @@ vec3 project_point(mat4 m, vec3 v) return tmp.xyz / tmp.w; } +mat2 rot2_from_angle(float a) +{ + float c = cos(a); + float s = sin(a); + return mat2(c, -s, s, c); +} + #define min3(a, b, c) min(a, min(b, c)) #define min4(a, b, c, d) min(a, min3(b, c, d)) #define min5(a, b, c, d, e) min(a, min4(b, c, d, e)) @@ -72,6 +81,21 @@ float sum(vec4 v) { return dot(vec4(1.0), v); } float avg(vec2 v) { return dot(vec2(1.0 / 2.0), v); } float avg(vec3 v) { return dot(vec3(1.0 / 3.0), v); } float avg(vec4 v) { return dot(vec4(1.0 / 4.0), v); } + +float safe_rcp(float a) { return (a != 0.0) ? (1.0 / a) : 0.0; } +vec2 safe_rcp(vec2 a) { return mix(vec2(0.0), (1.0 / a), notEqual(a, vec2(0.0))); } +vec4 safe_rcp(vec4 a) { return mix(vec4(0.0), (1.0 / a), notEqual(a, vec4(0.0))); } + +float sqr(float a) { return a * a; } +vec2 sqr(vec2 a) { return a * a; } +vec3 sqr(vec3 a) { return a * a; } +vec4 sqr(vec4 a) { return a * a; } + +float len_squared(vec3 a) { return dot(a, a); } +float len_squared(vec2 a) { return dot(a, a); } + +#define weighted_sum(val0, val1, val2, val3, weights) ((val0 * weights[0] + val1 * weights[1] + val2 * weights[2] + val3 * weights[3]) * safe_rcp(sum(weights))); +#define weighted_sum_array(val, weights) ((val[0] * weights[0] + val[1] * weights[1] + val[2] * weights[2] + val[3] * weights[3]) * safe_rcp(sum(weights))); /* clang-format on */ #define saturate(a) clamp(a, 0.0, 1.0) @@ -88,11 +112,6 @@ float distance_squared(vec3 a, vec3 b) return dot(a, a); } -float len_squared(vec3 a) -{ - return dot(a, a); -} - /** \} */ /* ---------------------------------------------------------------------- */ @@ -128,3 +147,14 @@ vec2 fast_acos(vec2 v) } /** \} */ + +/* + * For debugging purpose mainly. + * From https://www.shadertoy.com/view/4dsSzr + * By Morgan McGuire @morgan3d, http://graphicscodex.com + * Reuse permitted under the BSD license. + */ +vec3 neon_gradient(float t) +{ + return clamp(vec3(t * 1.3 + 0.1, sqr(abs(0.43 - t) * 1.7), (1.0 - t) * 1.7), 0.0, 1.0); +} |