From 3f6e14e667c4442bcc1f4d1fc795633cc706150f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Thu, 24 Jan 2019 17:28:51 +0100 Subject: Workbench: Depth Of Field: Initial Commit The algorithm used is borrowed from : http://tuxedolabs.blogspot.com/2018/05/bokeh-depth-of-field-in-single-pass.html This makes it possible to have a decent blur for foreground over defocused background in one pass only. The algorithm is using a gather approach that is much faster than the scatter approach used in Eevee. This makes it possible to have custom bokeh shapes (not implemented yet) which would be impossible with a separable gaussian technique. The blur is done in 2 steps. The first one define the shape of the bokeh and the second that fill the undersampling. A downsample max-CoC tile texture speed up the gathering process. --- source/blender/draw/CMakeLists.txt | 2 + .../shaders/workbench_effect_dof_frag.glsl | 265 ++++++++++++++++++ .../draw/engines/workbench/workbench_deferred.c | 15 +- .../draw/engines/workbench/workbench_effect_dof.c | 297 +++++++++++++++++++++ .../draw/engines/workbench/workbench_forward.c | 1 + .../draw/engines/workbench/workbench_private.h | 35 ++- 6 files changed, 612 insertions(+), 3 deletions(-) create mode 100644 source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl create mode 100644 source/blender/draw/engines/workbench/workbench_effect_dof.c (limited to 'source') diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index cf41b3b3e28..20be15b3102 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -115,6 +115,7 @@ set(SRC engines/workbench/workbench_data.c engines/workbench/workbench_deferred.c engines/workbench/workbench_effect_aa.c + engines/workbench/workbench_effect_dof.c engines/workbench/workbench_effect_fxaa.c engines/workbench/workbench_effect_taa.c engines/workbench/workbench_engine.c @@ -227,6 +228,7 @@ data_to_c_simple(engines/workbench/shaders/workbench_common_lib.glsl SRC) data_to_c_simple(engines/workbench/shaders/workbench_data_lib.glsl SRC) data_to_c_simple(engines/workbench/shaders/workbench_deferred_composite_frag.glsl SRC) data_to_c_simple(engines/workbench/shaders/workbench_deferred_background_frag.glsl SRC) +data_to_c_simple(engines/workbench/shaders/workbench_effect_dof_frag.glsl SRC) data_to_c_simple(engines/workbench/shaders/workbench_effect_fxaa_frag.glsl SRC) data_to_c_simple(engines/workbench/shaders/workbench_effect_taa_frag.glsl SRC) data_to_c_simple(engines/workbench/shaders/workbench_forward_composite_frag.glsl SRC) diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl new file mode 100644 index 00000000000..08eb22d01c3 --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_dof_frag.glsl @@ -0,0 +1,265 @@ +/** + * Separable Hexagonal Bokeh Blur by Colin Barré-Brisebois + * https://colinbarrebrisebois.com/2017/04/18/hexagonal-bokeh-blur-revisited-part-1-basic-3-pass-version/ + * Converted and adapted from HLSL to GLSL by Clément Foucault + **/ + +uniform mat4 ProjectionMatrix; +uniform vec2 invertedViewportSize; +uniform vec2 nearFar; +uniform vec3 dofParams; +uniform sampler2D inputCocTex; +uniform sampler2D maxCocTilesTex; +uniform sampler2D sceneColorTex; +uniform sampler2D sceneDepthTex; +uniform sampler2D backgroundTex; +uniform sampler2D halfResColorTex; +uniform sampler2D blurTex; + +#define dof_aperturesize dofParams.x +#define dof_distance dofParams.y +#define dof_invsensorsize dofParams.z + +#define NUM_SAMPLES 25 + +#define THRESHOLD 1.0 +#define M_PI 3.1415926535897932 /* pi */ + +const float GOLDEN_ANGLE = 2.39996323; +const float MAX_BLUR_SIZE = 20.0; +const float RAD_SCALE = 2.0; // Smaller = nicer blur, larger = faster +const float MAX_COC_SIZE = 40.0; + +float max_v4(vec4 v) { return max(max(v.x, v.y), max(v.z, v.w)); } + +#define weighted_sum(a, b, c, d, e, e_sum) ((a) * e.x + (b) * e.y + (c) * e.z + (d) * e.w) / max(1e-6, e_sum); + +#define encode_signed_coc(coc) (((coc) / MAX_COC_SIZE) * 0.5 + 0.5); +#define decode_signed_coc(coc) (((coc) * 2.0 - 1.0) * MAX_COC_SIZE); + +/* divide by sensor size to get the normalized size */ +#define calculate_coc(zdepth) (dof_aperturesize * (dof_distance / zdepth - 1.0) * dof_invsensorsize) + +#define linear_depth(z) ((ProjectionMatrix[3][3] == 0.0) \ + ? (nearFar.x * nearFar.y) / (z * (nearFar.x - nearFar.y) + nearFar.y) \ + : (z * 2.0 - 1.0) * nearFar.y) + +vec2 encode_coc(float near, float far) { return vec2(near, far) / MAX_COC_SIZE; } +float decode_coc(vec2 cocs) { return max(cocs.x, cocs.y) * MAX_COC_SIZE; } + +/** + * ----------------- STEP 0 ------------------ + * Coc aware downsample. + **/ +#ifdef PREPARE + +layout(location = 0) out vec4 backgroundColorCoc; +layout(location = 1) out vec2 normalizedCoc; + +void main() +{ + /* Half Res pass */ + vec2 uv = (floor(gl_FragCoord.xy) * 2.0 + 0.5) * invertedViewportSize; + + ivec4 texel = ivec4(gl_FragCoord.xyxy) * 2 + ivec4(0, 0, 1, 1); + + /* custom downsampling */ + vec4 color1 = texelFetch(sceneColorTex, texel.xy, 0); + vec4 color2 = texelFetch(sceneColorTex, texel.zw, 0); + vec4 color3 = texelFetch(sceneColorTex, texel.zy, 0); + vec4 color4 = texelFetch(sceneColorTex, texel.xw, 0); + + vec3 ofs = vec3(invertedViewportSize.xy, 0.0); + vec4 depths; + depths.x = texelFetch(sceneDepthTex, texel.xy, 0).x; + depths.y = texelFetch(sceneDepthTex, texel.zw, 0).x; + depths.z = texelFetch(sceneDepthTex, texel.zy, 0).x; + depths.w = texelFetch(sceneDepthTex, texel.xw, 0).x; + + vec4 zdepths = linear_depth(depths); + vec4 cocs_near = calculate_coc(zdepths); + vec4 cocs_far = -cocs_near; + + float coc_near = max(max_v4(cocs_near), 0.0); + float coc_far = max(max_v4(cocs_far), 0.0); + + /* now we need to write the near-far fields premultiplied by the coc + * also use bilateral weighting by each coc values to avoid bleeding. */ + vec4 near_weights = step(0.0, cocs_near) * clamp(1.0 - abs(coc_near - cocs_near), 0.0, 1.0); + vec4 far_weights = step(0.0, cocs_far) * clamp(1.0 - abs(coc_far - cocs_far), 0.0, 1.0); + + /* now write output to weighted buffers. */ + // backgroundColorCoc = weighted_sum(color1, color2, color3, color4, cocs_far, coc_far); + float tot_weight_near = dot(near_weights, vec4(1.0)); + float tot_weight_far = dot(far_weights, vec4(1.0)); + backgroundColorCoc = weighted_sum(color1, color2, color3, color4, near_weights, tot_weight_near); + backgroundColorCoc += weighted_sum(color1, color2, color3, color4, far_weights, tot_weight_far); + if (tot_weight_near > 0.0 && tot_weight_far > 0.0) { + backgroundColorCoc *= 0.5; + } + + normalizedCoc = encode_coc(cocs_near.x, cocs_far.x); +} +#endif + +/** + * ----------------- STEP 1 ------------------ + * Flatten COC buffer using max filter. + **/ +#if defined(FLATTEN_VERTICAL) || defined(FLATTEN_HORIZONTAL) + +layout(location = 0) out vec2 flattenedCoc; + +void main() +{ +#ifdef FLATTEN_HORIZONTAL + ivec2 texel = ivec2(gl_FragCoord.xy) * ivec2(8, 1); + vec2 cocs1 = texelFetchOffset(inputCocTex, texel, 0, ivec2(0, 0)).rg; + vec2 cocs2 = texelFetchOffset(inputCocTex, texel, 0, ivec2(1, 0)).rg; + vec2 cocs3 = texelFetchOffset(inputCocTex, texel, 0, ivec2(2, 0)).rg; + vec2 cocs4 = texelFetchOffset(inputCocTex, texel, 0, ivec2(3, 0)).rg; + vec2 cocs5 = texelFetchOffset(inputCocTex, texel, 0, ivec2(4, 0)).rg; + vec2 cocs6 = texelFetchOffset(inputCocTex, texel, 0, ivec2(5, 0)).rg; + vec2 cocs7 = texelFetchOffset(inputCocTex, texel, 0, ivec2(6, 0)).rg; + vec2 cocs8 = texelFetchOffset(inputCocTex, texel, 0, ivec2(7, 0)).rg; +#else /* FLATTEN_VERTICAL */ + ivec2 texel = ivec2(gl_FragCoord.xy) * ivec2(1, 8); + vec2 cocs1 = texelFetchOffset(inputCocTex, texel, 0, ivec2(0, 0)).rg; + vec2 cocs2 = texelFetchOffset(inputCocTex, texel, 0, ivec2(0, 1)).rg; + vec2 cocs3 = texelFetchOffset(inputCocTex, texel, 0, ivec2(0, 2)).rg; + vec2 cocs4 = texelFetchOffset(inputCocTex, texel, 0, ivec2(0, 3)).rg; + vec2 cocs5 = texelFetchOffset(inputCocTex, texel, 0, ivec2(0, 4)).rg; + vec2 cocs6 = texelFetchOffset(inputCocTex, texel, 0, ivec2(0, 5)).rg; + vec2 cocs7 = texelFetchOffset(inputCocTex, texel, 0, ivec2(0, 6)).rg; + vec2 cocs8 = texelFetchOffset(inputCocTex, texel, 0, ivec2(0, 7)).rg; +#endif + flattenedCoc = max(max(max(cocs1, cocs2), max(cocs3, cocs4)), max(max(cocs5, cocs6), max(cocs7, cocs8))); +} +#endif + +/** + * ----------------- STEP 1.ax------------------ + * Dilate COC buffer using min filter. + **/ +#if defined(DILATE_VERTICAL) || defined(DILATE_HORIZONTAL) + +layout(location = 0) out vec2 dilatedCoc; + +void main() +{ + vec2 texel_size = 1.0 / vec2(textureSize(inputCocTex, 0)); + vec2 uv = gl_FragCoord.xy * texel_size; +#ifdef DILATE_VERTICAL + // vec2 cocs1 = texture(inputCocTex, uv + texel_size * vec2(-3, 0)).rg; + vec2 cocs2 = texture(inputCocTex, uv + texel_size * vec2(-2, 0)).rg; + vec2 cocs3 = texture(inputCocTex, uv + texel_size * vec2(-1, 0)).rg; + vec2 cocs4 = texture(inputCocTex, uv + texel_size * vec2( 0, 0)).rg; + vec2 cocs5 = texture(inputCocTex, uv + texel_size * vec2( 1, 0)).rg; + vec2 cocs6 = texture(inputCocTex, uv + texel_size * vec2( 2, 0)).rg; + // vec2 cocs7 = texture(inputCocTex, uv + texel_size * vec2( 3, 0)).rg; +#else /* DILATE_HORIZONTAL */ + // vec2 cocs1 = texture(inputCocTex, uv + texel_size * vec2(0, -3)).rg; + vec2 cocs2 = texture(inputCocTex, uv + texel_size * vec2(0, -2)).rg; + vec2 cocs3 = texture(inputCocTex, uv + texel_size * vec2(0, -1)).rg; + vec2 cocs4 = texture(inputCocTex, uv + texel_size * vec2(0, 0)).rg; + vec2 cocs5 = texture(inputCocTex, uv + texel_size * vec2(0, 1)).rg; + vec2 cocs6 = texture(inputCocTex, uv + texel_size * vec2(0, 2)).rg; + // vec2 cocs7 = texture(inputCocTex, uv + texel_size * vec2(0, 3)).rg; +#endif + dilatedCoc = max(max(cocs3, cocs4), max(max(cocs5, cocs6), cocs2)); + // dilatedCoc = max(max(max(cocs1, cocs2), max(cocs3, cocs4)), max(max(cocs5, cocs6), cocs7)); +} +#endif + +/** + * ----------------- STEP 2 ------------------ + * Blur vertically and diagonally. + * Outputs vertical blur and combined blur in MRT + **/ +#ifdef BLUR1 +layout(location = 0) out vec4 blurColor; + +void main() +{ + /* Half Res pass */ + vec2 uv = gl_FragCoord.xy * invertedViewportSize * 2.0; + + vec2 size = vec2(textureSize(halfResColorTex, 0).xy); + ivec2 texel = ivec2(uv * size); + + vec3 color = texelFetch(halfResColorTex, texel, 0).rgb; + float coc = decode_coc(texelFetch(inputCocTex, texel, 0).rg); + + /* TODO Ensure alignement */ + vec2 max_radii = texture(maxCocTilesTex, (0.5 + floor(gl_FragCoord.xy / 8.0)) / vec2(textureSize(maxCocTilesTex, 0))).rg; + float max_radius = decode_coc(max_radii); + + float center_coc = coc; + float tot = 1.0; + float radius = RAD_SCALE; + + for (float ang = 0.0; radius < MAX_BLUR_SIZE && radius < max_radius; ang += GOLDEN_ANGLE) { + vec2 tc = uv + vec2(cos(ang), sin(ang)) * invertedViewportSize * radius; + + vec3 samp = texture(halfResColorTex, tc).rgb; + + coc = decode_coc(texture(inputCocTex, tc).rg); + if (coc > center_coc) { + coc = clamp(abs(coc), 0.0, abs(center_coc) * 2.0); + } + + float m = smoothstep(radius - 0.5, radius + 0.5, abs(coc)); + color += mix(color / tot, samp, m); + tot += 1.0; + radius += RAD_SCALE / radius; + } + + blurColor.rgb = color / tot; + blurColor.a = 1.0; +} +#endif + +/** + * ----------------- STEP 3 ------------------ + * Additional 3x3 blur + **/ +#ifdef BLUR2 +out vec4 finalColor; + +void main() +{ + /* Half Res pass */ + vec2 pixel_size = vec2(1.0, 1.0) / vec2(textureSize(blurTex, 0).xy); + vec2 uv = gl_FragCoord.xy * pixel_size.xy; + vec2 max_radii = texture(inputCocTex, uv).rg; + /* Scale filter */ + float rad = min(max(max_radii.x, max_radii.y) * MAX_COC_SIZE, 4.0) * 0.25; + finalColor = texture(blurTex, uv + pixel_size * vec2(-0.5, -0.5) * rad); + finalColor += texture(blurTex, uv + pixel_size * vec2(-0.5, 1.5) * rad); + finalColor += texture(blurTex, uv + pixel_size * vec2( 1.5, -0.5) * rad); + finalColor += texture(blurTex, uv + pixel_size * vec2( 1.5, 1.5) * rad); + finalColor *= 0.25; +} +#endif + +/** + * ----------------- STEP 4 ------------------ + **/ +#ifdef RESOLVE +out vec4 finalColor; + +void main() +{ + /* Fullscreen pass */ + vec2 pixel_size = 0.5 / vec2(textureSize(halfResColorTex, 0).xy); + vec2 uv = gl_FragCoord.xy * pixel_size; + + /* TODO MAKE SURE TO ALIGN SAMPLE POSITION TO AVOID OFFSET IN THE BOKEH */ + float depth = texelFetch(sceneDepthTex, ivec2(gl_FragCoord.xy), 0).r; + float zdepth = linear_depth(depth); + float coc = calculate_coc(zdepth); + + finalColor = texture(halfResColorTex, uv); + finalColor.a = smoothstep(1.0, 3.0, abs(coc)); +} +#endif \ No newline at end of file diff --git a/source/blender/draw/engines/workbench/workbench_deferred.c b/source/blender/draw/engines/workbench/workbench_deferred.c index b48e64293fa..d1e32ea42d9 100644 --- a/source/blender/draw/engines/workbench/workbench_deferred.c +++ b/source/blender/draw/engines/workbench/workbench_deferred.c @@ -343,6 +343,9 @@ void workbench_deferred_engine_init(WORKBENCH_Data *vedata) WORKBENCH_PassList *psl = vedata->psl; DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); const DRWContextState *draw_ctx = DRW_context_state_get(); + RegionView3D *rv3d = draw_ctx->rv3d; + View3D *v3d = draw_ctx->v3d; + Object *camera = (rv3d->persp == RV3D_CAMOB) ? v3d->camera : NULL; if (!stl->g_data) { /* Alloc transient pointers */ @@ -406,6 +409,8 @@ void workbench_deferred_engine_init(WORKBENCH_Data *vedata) WORKBENCH_PrivateData *wpd = stl->g_data; workbench_private_data_init(wpd); + workbench_dof_engine_init(vedata, camera); + { const float *viewport_size = DRW_viewport_size_get(); const int size[2] = {(int)viewport_size[0], (int)viewport_size[1]}; @@ -448,7 +453,7 @@ void workbench_deferred_engine_init(WORKBENCH_Data *vedata) GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(e_data.composite_buffer_tx), }); - GPU_framebuffer_ensure_config(&fbl->volume_fb, { + GPU_framebuffer_ensure_config(&fbl->color_only_fb, { GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(e_data.composite_buffer_tx), }); @@ -514,6 +519,10 @@ void workbench_deferred_engine_init(WORKBENCH_Data *vedata) workbench_aa_create_pass(vedata, &e_data.color_buffer_tx); } + { + workbench_dof_create_pass(vedata, &e_data.composite_buffer_tx); + } + if (CAVITY_ENABLED(wpd)) { int state = DRW_STATE_WRITE_COLOR; GPUShader *shader = workbench_cavity_shader_get(SSAO_ENABLED(wpd), CURVATURE_ENABLED(wpd)); @@ -583,6 +592,7 @@ void workbench_deferred_engine_free(void) workbench_volume_engine_free(); workbench_fxaa_engine_free(); workbench_taa_engine_free(); + workbench_dof_engine_free(); } static void workbench_composite_uniforms(WORKBENCH_PrivateData *wpd, DRWShadingGroup *grp) @@ -1089,10 +1099,11 @@ void workbench_deferred_draw_scene(WORKBENCH_Data *vedata) DRW_draw_pass(psl->background_pass); if (wpd->volumes_do) { - GPU_framebuffer_bind(fbl->volume_fb); + GPU_framebuffer_bind(fbl->color_only_fb); DRW_draw_pass(psl->volume_pass); } + workbench_dof_draw_pass(vedata); workbench_aa_draw_pass(vedata, e_data.composite_buffer_tx); } diff --git a/source/blender/draw/engines/workbench/workbench_effect_dof.c b/source/blender/draw/engines/workbench/workbench_effect_dof.c new file mode 100644 index 00000000000..85213a7460c --- /dev/null +++ b/source/blender/draw/engines/workbench/workbench_effect_dof.c @@ -0,0 +1,297 @@ +/* + * Copyright 2016, Blender Foundation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Blender Institute + * + */ + +/** \file workbench_effect_dof.c + * \ingroup draw_engine + */ + +#include "workbench_private.h" + +#include "BKE_camera.h" +#include "DEG_depsgraph_query.h" + +#include "DNA_camera_types.h" + +/* *********** STATIC *********** */ +static struct { + struct GPUShader *effect_dof_prepare_sh; + struct GPUShader *effect_dof_flatten_v_sh; + struct GPUShader *effect_dof_flatten_h_sh; + struct GPUShader *effect_dof_dilate_v_sh; + struct GPUShader *effect_dof_dilate_h_sh; + struct GPUShader *effect_dof_blur1_sh; + struct GPUShader *effect_dof_blur2_sh; + struct GPUShader *effect_dof_resolve_sh; +} e_data = {NULL}; + +/* Shaders */ +extern char datatoc_workbench_effect_dof_frag_glsl[]; + +/* *********** Functions *********** */ +void workbench_dof_engine_init(WORKBENCH_Data *vedata, Object *camera) +{ + WORKBENCH_StorageList *stl = vedata->stl; + WORKBENCH_PrivateData *wpd = stl->g_data; + WORKBENCH_FramebufferList *fbl = vedata->fbl; + + if (camera == NULL) { + wpd->dof_enabled = false; + return; + } + + if (e_data.effect_dof_prepare_sh == NULL) { + e_data.effect_dof_prepare_sh = DRW_shader_create_fullscreen( + datatoc_workbench_effect_dof_frag_glsl, + "#define PREPARE\n"); + + e_data.effect_dof_flatten_v_sh = DRW_shader_create_fullscreen( + datatoc_workbench_effect_dof_frag_glsl, + "#define FLATTEN_VERTICAL\n"); + + e_data.effect_dof_flatten_h_sh = DRW_shader_create_fullscreen( + datatoc_workbench_effect_dof_frag_glsl, + "#define FLATTEN_HORIZONTAL\n"); + + e_data.effect_dof_dilate_v_sh = DRW_shader_create_fullscreen( + datatoc_workbench_effect_dof_frag_glsl, + "#define DILATE_VERTICAL\n"); + + e_data.effect_dof_dilate_h_sh = DRW_shader_create_fullscreen( + datatoc_workbench_effect_dof_frag_glsl, + "#define DILATE_HORIZONTAL\n"); + + e_data.effect_dof_blur1_sh = DRW_shader_create_fullscreen( + datatoc_workbench_effect_dof_frag_glsl, + "#define BLUR1\n"); + + e_data.effect_dof_blur2_sh = DRW_shader_create_fullscreen( + datatoc_workbench_effect_dof_frag_glsl, + "#define BLUR2\n"); + + e_data.effect_dof_resolve_sh = DRW_shader_create_fullscreen( + datatoc_workbench_effect_dof_frag_glsl, + "#define RESOLVE\n"); + } + + const float *full_size = DRW_viewport_size_get(); + int size[2] = {full_size[0] / 2, full_size[1] / 2}; + /* NOTE: We Ceil here in order to not miss any edge texel if using a NPO2 texture. */ + int shrink_h_size[2] = {ceilf(size[0] / 8.0f), size[1]}; + int shrink_w_size[2] = {shrink_h_size[0], ceilf(size[1] / 8.0f)}; + + wpd->half_res_col_tx = DRW_texture_pool_query_2D(size[0], size[1], GPU_R11F_G11F_B10F, &draw_engine_workbench_solid); + wpd->dof_blur_tx = DRW_texture_pool_query_2D(size[0], size[1], GPU_R11F_G11F_B10F, &draw_engine_workbench_solid); + wpd->coc_halfres_tx = DRW_texture_pool_query_2D(size[0], size[1], GPU_RG8, &draw_engine_workbench_solid); + wpd->coc_temp_tx = DRW_texture_pool_query_2D(shrink_h_size[0], shrink_h_size[1], GPU_RG8, &draw_engine_workbench_solid); + wpd->coc_tiles_tx[0] = DRW_texture_pool_query_2D(shrink_w_size[0], shrink_w_size[1], GPU_RG8, &draw_engine_workbench_solid); + wpd->coc_tiles_tx[1] = DRW_texture_pool_query_2D(shrink_w_size[0], shrink_w_size[1], GPU_RG8, &draw_engine_workbench_solid); + + GPU_framebuffer_ensure_config(&fbl->dof_downsample_fb, { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(wpd->half_res_col_tx), + GPU_ATTACHMENT_TEXTURE(wpd->coc_halfres_tx), + }); + GPU_framebuffer_ensure_config(&fbl->dof_coc_tile_h_fb, { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(wpd->coc_temp_tx), + }); + GPU_framebuffer_ensure_config(&fbl->dof_coc_tile_v_fb, { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(wpd->coc_tiles_tx[0]), + }); + GPU_framebuffer_ensure_config(&fbl->dof_coc_dilate_fb, { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(wpd->coc_tiles_tx[1]), + }); + GPU_framebuffer_ensure_config(&fbl->dof_blur1_fb, { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(wpd->dof_blur_tx), + }); + GPU_framebuffer_ensure_config(&fbl->dof_blur2_fb, { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(wpd->half_res_col_tx), + }); + + { + const DRWContextState *draw_ctx = DRW_context_state_get(); + const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); + RegionView3D *rv3d = draw_ctx->rv3d; + Camera *cam = (Camera *)camera->data; + + /* Parameters */ + /* TODO UI Options */ + float fstop = cam->gpu_dof.fstop; + float sensor = BKE_camera_sensor_size(cam->sensor_fit, cam->sensor_x, cam->sensor_y); + float focus_dist = BKE_camera_object_dof_distance(camera); + float focal_len = cam->lens; + + /* TODO(fclem) deduplicate with eevee */ + + /* this is factor that converts to the scene scale. focal length and sensor are expressed in mm + * unit.scale_length is how many meters per blender unit we have. We want to convert to blender units though + * because the shader reads coordinates in world space, which is in blender units. + * Note however that focus_distance is already in blender units and shall not be scaled here (see T48157). */ + float scale = (scene_eval->unit.system) ? scene_eval->unit.scale_length : 1.0f; + float scale_camera = 0.001f / scale; + /* we want radius here for the aperture number */ + float aperture = 0.5f * scale_camera * focal_len / fstop; + float focal_len_scaled = scale_camera * focal_len; + float sensor_scaled = scale_camera * sensor; + + if (rv3d != NULL) { + sensor_scaled *= rv3d->viewcamtexcofac[0]; + } + + wpd->dof_aperturesize = aperture * fabsf(focal_len_scaled / (focus_dist - focal_len_scaled)); + wpd->dof_distance = -focus_dist; + wpd->dof_invsensorsize = full_size[0] / sensor_scaled; + + wpd->dof_near_far[0] = -cam->clipsta; + wpd->dof_near_far[1] = -cam->clipend; + } + + wpd->dof_enabled = true; +} + +void workbench_dof_create_pass(WORKBENCH_Data *vedata, GPUTexture **dof_input) +{ + WORKBENCH_PassList *psl = vedata->psl; + WORKBENCH_StorageList *stl = vedata->stl; + WORKBENCH_PrivateData *wpd = stl->g_data; + struct GPUBatch *quad = DRW_cache_fullscreen_quad_get(); + + if (!wpd->dof_enabled) { + return; + } + + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + + psl->dof_down_ps = DRW_pass_create("DoF DownSample", DRW_STATE_WRITE_COLOR); + psl->dof_flatten_h_ps = DRW_pass_create("DoF Flatten Coc H", DRW_STATE_WRITE_COLOR); + psl->dof_flatten_v_ps = DRW_pass_create("DoF Flatten Coc V", DRW_STATE_WRITE_COLOR); + psl->dof_dilate_h_ps = DRW_pass_create("DoF Dilate Coc H", DRW_STATE_WRITE_COLOR); + psl->dof_dilate_v_ps = DRW_pass_create("DoF Dilate Coc V", DRW_STATE_WRITE_COLOR); + psl->dof_blur1_ps = DRW_pass_create("DoF Blur 1", DRW_STATE_WRITE_COLOR); + psl->dof_blur2_ps = DRW_pass_create("DoF Blur 2", DRW_STATE_WRITE_COLOR); + psl->dof_resolve_ps = DRW_pass_create("DoF Resolve", DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND); + + { + DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_prepare_sh, psl->dof_down_ps); + DRW_shgroup_uniform_texture_ref(grp, "sceneColorTex", dof_input); + DRW_shgroup_uniform_texture(grp, "sceneDepthTex", dtxl->depth); + DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1); + DRW_shgroup_uniform_vec3(grp, "dofParams", &wpd->dof_aperturesize, 1); + DRW_shgroup_uniform_vec2(grp, "nearFar", wpd->dof_near_far, 1); + DRW_shgroup_call_add(grp, quad, NULL); + } + { + DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_flatten_h_sh, psl->dof_flatten_h_ps); + DRW_shgroup_uniform_texture(grp, "inputCocTex", wpd->coc_halfres_tx); + DRW_shgroup_call_add(grp, quad, NULL); + } + { + DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_flatten_v_sh, psl->dof_flatten_v_ps); + DRW_shgroup_uniform_texture(grp, "inputCocTex", wpd->coc_temp_tx); + DRW_shgroup_call_add(grp, quad, NULL); + } + { + DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_dilate_v_sh, psl->dof_dilate_v_ps); + DRW_shgroup_uniform_texture(grp, "inputCocTex", wpd->coc_tiles_tx[0]); + DRW_shgroup_call_add(grp, quad, NULL); + } + { + DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_dilate_h_sh, psl->dof_dilate_h_ps); + DRW_shgroup_uniform_texture(grp, "inputCocTex", wpd->coc_tiles_tx[1]); + DRW_shgroup_call_add(grp, quad, NULL); + } + { + DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_blur1_sh, psl->dof_blur1_ps); + DRW_shgroup_uniform_texture(grp, "inputCocTex", wpd->coc_halfres_tx); + DRW_shgroup_uniform_texture(grp, "maxCocTilesTex", wpd->coc_tiles_tx[0]); + DRW_shgroup_uniform_texture(grp, "halfResColorTex", wpd->half_res_col_tx); + DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1); + DRW_shgroup_call_add(grp, quad, NULL); + } + { + DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_blur2_sh, psl->dof_blur2_ps); + DRW_shgroup_uniform_texture(grp, "inputCocTex", wpd->coc_halfres_tx); + DRW_shgroup_uniform_texture(grp, "blurTex", wpd->dof_blur_tx); + DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1); + DRW_shgroup_call_add(grp, quad, NULL); + } + { + DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_resolve_sh, psl->dof_resolve_ps); + DRW_shgroup_uniform_texture(grp, "halfResColorTex", wpd->half_res_col_tx); + DRW_shgroup_uniform_texture(grp, "sceneDepthTex", dtxl->depth); + DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1); + DRW_shgroup_uniform_vec3(grp, "dofParams", &wpd->dof_aperturesize, 1); + DRW_shgroup_uniform_vec2(grp, "nearFar", wpd->dof_near_far, 1); + DRW_shgroup_call_add(grp, quad, NULL); + } +} + +void workbench_dof_engine_free(void) +{ + DRW_SHADER_FREE_SAFE(e_data.effect_dof_prepare_sh); + DRW_SHADER_FREE_SAFE(e_data.effect_dof_flatten_v_sh); + DRW_SHADER_FREE_SAFE(e_data.effect_dof_flatten_h_sh); + DRW_SHADER_FREE_SAFE(e_data.effect_dof_dilate_v_sh); + DRW_SHADER_FREE_SAFE(e_data.effect_dof_dilate_h_sh); + DRW_SHADER_FREE_SAFE(e_data.effect_dof_blur1_sh); + DRW_SHADER_FREE_SAFE(e_data.effect_dof_blur2_sh); + DRW_SHADER_FREE_SAFE(e_data.effect_dof_resolve_sh); +} + +void workbench_dof_draw_pass(WORKBENCH_Data *vedata) +{ + WORKBENCH_FramebufferList *fbl = vedata->fbl; + WORKBENCH_StorageList *stl = vedata->stl; + WORKBENCH_PassList *psl = vedata->psl; + WORKBENCH_PrivateData *wpd = stl->g_data; + + if (!wpd->dof_enabled) { + return; + } + + GPU_framebuffer_bind(fbl->dof_downsample_fb); + DRW_draw_pass(psl->dof_down_ps); + + GPU_framebuffer_bind(fbl->dof_coc_tile_h_fb); + DRW_draw_pass(psl->dof_flatten_h_ps); + + GPU_framebuffer_bind(fbl->dof_coc_tile_v_fb); + DRW_draw_pass(psl->dof_flatten_v_ps); + + GPU_framebuffer_bind(fbl->dof_coc_dilate_fb); + DRW_draw_pass(psl->dof_dilate_v_ps); + + GPU_framebuffer_bind(fbl->dof_coc_tile_v_fb); + DRW_draw_pass(psl->dof_dilate_h_ps); + + GPU_framebuffer_bind(fbl->dof_blur1_fb); + DRW_draw_pass(psl->dof_blur1_ps); + + GPU_framebuffer_bind(fbl->dof_blur2_fb); + DRW_draw_pass(psl->dof_blur2_ps); + + GPU_framebuffer_bind(fbl->color_only_fb); + DRW_draw_pass(psl->dof_resolve_ps); +} diff --git a/source/blender/draw/engines/workbench/workbench_forward.c b/source/blender/draw/engines/workbench/workbench_forward.c index ead9d51aaac..b353f96d18d 100644 --- a/source/blender/draw/engines/workbench/workbench_forward.c +++ b/source/blender/draw/engines/workbench/workbench_forward.c @@ -430,6 +430,7 @@ void workbench_forward_engine_free() workbench_volume_engine_free(); workbench_fxaa_engine_free(); workbench_taa_engine_free(); + workbench_dof_engine_free(); } void workbench_forward_cache_init(WORKBENCH_Data *UNUSED(vedata)) diff --git a/source/blender/draw/engines/workbench/workbench_private.h b/source/blender/draw/engines/workbench/workbench_private.h index bbcae7f81d4..62622b56805 100644 --- a/source/blender/draw/engines/workbench/workbench_private.h +++ b/source/blender/draw/engines/workbench/workbench_private.h @@ -92,7 +92,14 @@ typedef struct WORKBENCH_FramebufferList { struct GPUFrameBuffer *effect_fb; struct GPUFrameBuffer *effect_taa_fb; struct GPUFrameBuffer *depth_buffer_fb; - struct GPUFrameBuffer *volume_fb; + struct GPUFrameBuffer *color_only_fb; + + struct GPUFrameBuffer *dof_downsample_fb; + struct GPUFrameBuffer *dof_coc_tile_h_fb; + struct GPUFrameBuffer *dof_coc_tile_v_fb; + struct GPUFrameBuffer *dof_coc_dilate_fb; + struct GPUFrameBuffer *dof_blur1_fb; + struct GPUFrameBuffer *dof_blur2_fb; /* Forward render buffers */ struct GPUFrameBuffer *object_outline_fb; @@ -129,6 +136,14 @@ typedef struct WORKBENCH_PassList { struct DRWPass *background_pass_clip; struct DRWPass *ghost_resolve_pass; struct DRWPass *effect_aa_pass; + struct DRWPass *dof_down_ps; + struct DRWPass *dof_flatten_v_ps; + struct DRWPass *dof_flatten_h_ps; + struct DRWPass *dof_dilate_h_ps; + struct DRWPass *dof_dilate_v_ps; + struct DRWPass *dof_blur1_ps; + struct DRWPass *dof_blur2_ps; + struct DRWPass *dof_resolve_ps; struct DRWPass *volume_pass; /* forward rendering */ @@ -218,6 +233,18 @@ typedef struct WORKBENCH_PrivateData { float ssao_params[4]; float ssao_settings[4]; + /* Dof */ + struct GPUTexture *half_res_col_tx; + struct GPUTexture *dof_blur_tx; + struct GPUTexture *coc_halfres_tx; + struct GPUTexture *coc_temp_tx; + struct GPUTexture *coc_tiles_tx[2]; + float dof_aperturesize; + float dof_distance; + float dof_invsensorsize; + float dof_near_far[2]; + bool dof_enabled; + /* Color Management */ bool use_color_view_settings; } WORKBENCH_PrivateData; /* Transient data */ @@ -302,6 +329,12 @@ void workbench_taa_draw_scene_end(WORKBENCH_Data *vedata); void workbench_taa_view_updated(WORKBENCH_Data *vedata); int workbench_taa_calculate_num_iterations(WORKBENCH_Data *vedata); +/* workbench_effect_dof.c */ +void workbench_dof_engine_init(WORKBENCH_Data *vedata, Object *camera); +void workbench_dof_engine_free(void); +void workbench_dof_create_pass(WORKBENCH_Data *vedata, GPUTexture **dof_input); +void workbench_dof_draw_pass(WORKBENCH_Data *vedata); + /* workbench_materials.c */ int workbench_material_determine_color_type(WORKBENCH_PrivateData *wpd, Image *ima, Object *ob); void workbench_material_get_image_and_mat(Object *ob, int mat_nr, Image **r_image, int *r_interp, Material **r_mat); -- cgit v1.2.3