From c476c36e400883d929a7149def8dcb6ad6157a86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Wed, 11 Mar 2020 17:07:43 +0100 Subject: Workbench Simplification Refactor This patch is (almost) a complete rewrite of workbench engine. The features remain unchanged but the code quality is greatly improved. Hair shading is brighter but also more correct. This also introduce the concept of `DRWShaderLibrary` to make a simple include system inside the GLSL files. Differential Revision: https://developer.blender.org/D7060 --- source/blender/draw/CMakeLists.txt | 47 +- .../blender/draw/engines/overlay/overlay_engine.c | 3 - .../draw/engines/overlay/overlay_wireframe.c | 10 +- .../workbench/shaders/workbench_cavity_frag.glsl | 81 -- .../workbench/shaders/workbench_cavity_lib.glsl | 97 +- .../workbench/shaders/workbench_common_lib.glsl | 154 +-- .../shaders/workbench_composite_frag.glsl | 44 + .../workbench/shaders/workbench_curvature_lib.glsl | 47 +- .../workbench/shaders/workbench_data_lib.glsl | 34 +- .../workbench_deferred_background_frag.glsl | 30 - .../shaders/workbench_deferred_composite_frag.glsl | 103 -- .../shaders/workbench_effect_cavity_frag.glsl | 31 + .../shaders/workbench_effect_fxaa_frag.glsl | 14 - .../shaders/workbench_effect_outline_frag.glsl | 24 + .../shaders/workbench_effect_smaa_frag.glsl | 44 + .../shaders/workbench_effect_smaa_vert.glsl | 21 + .../shaders/workbench_effect_taa_frag.glsl | 11 +- .../shaders/workbench_forward_composite_frag.glsl | 36 - .../shaders/workbench_forward_depth_frag.glsl | 20 - .../workbench_forward_transparent_accum_frag.glsl | 118 -- .../shaders/workbench_ghost_resolve_frag.glsl | 13 - .../workbench/shaders/workbench_image_lib.glsl | 83 ++ .../workbench/shaders/workbench_matcap_lib.glsl | 30 + .../workbench/shaders/workbench_material_lib.glsl | 21 + .../shaders/workbench_merge_infront_frag.glsl | 18 + .../shaders/workbench_object_outline_lib.glsl | 12 - .../workbench/shaders/workbench_prepass_frag.glsl | 97 +- .../shaders/workbench_prepass_hair_vert.glsl | 94 ++ .../workbench/shaders/workbench_prepass_vert.glsl | 114 +- .../shaders/workbench_shader_interface_lib.glsl | 21 + .../shaders/workbench_shadow_debug_frag.glsl | 16 +- .../shaders/workbench_transparent_accum_frag.glsl | 89 ++ .../workbench_transparent_resolve_frag.glsl | 26 + .../workbench/shaders/workbench_volume_frag.glsl | 37 +- .../workbench/shaders/workbench_volume_vert.glsl | 3 + .../shaders/workbench_world_light_lib.glsl | 88 +- source/blender/draw/engines/workbench/solid_mode.c | 119 -- .../draw/engines/workbench/transparent_mode.c | 97 -- .../draw/engines/workbench/workbench_data.c | 407 +++--- .../draw/engines/workbench/workbench_deferred.c | 1415 -------------------- .../draw/engines/workbench/workbench_effect_aa.c | 101 -- .../workbench/workbench_effect_antialiasing.c | 421 ++++++ .../engines/workbench/workbench_effect_cavity.c | 182 +++ .../draw/engines/workbench/workbench_effect_dof.c | 217 ++- .../draw/engines/workbench/workbench_effect_fxaa.c | 59 - .../engines/workbench/workbench_effect_outline.c | 55 + .../draw/engines/workbench/workbench_effect_taa.c | 305 ----- .../draw/engines/workbench/workbench_engine.c | 547 +++++++- .../draw/engines/workbench/workbench_engine.h | 2 - .../draw/engines/workbench/workbench_forward.c | 817 ----------- .../draw/engines/workbench/workbench_materials.c | 529 +++----- .../draw/engines/workbench/workbench_opaque.c | 165 +++ .../draw/engines/workbench/workbench_private.h | 670 +++++---- .../draw/engines/workbench/workbench_render.c | 95 +- .../draw/engines/workbench/workbench_shader.c | 533 ++++++++ .../draw/engines/workbench/workbench_shadow.c | 367 +++++ .../draw/engines/workbench/workbench_studiolight.c | 257 ---- .../draw/engines/workbench/workbench_transparent.c | 180 +++ .../draw/engines/workbench/workbench_volume.c | 117 +- source/blender/draw/intern/DRW_render.h | 26 +- source/blender/draw/intern/draw_common.h | 5 + source/blender/draw/intern/draw_hair.c | 29 +- source/blender/draw/intern/draw_manager.c | 38 +- source/blender/draw/intern/draw_manager_data.c | 20 +- source/blender/draw/intern/draw_manager_shader.c | 129 ++ source/blender/draw/intern/draw_manager_texture.c | 7 + 66 files changed, 4370 insertions(+), 5272 deletions(-) delete mode 100644 source/blender/draw/engines/workbench/shaders/workbench_cavity_frag.glsl create mode 100644 source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl delete mode 100644 source/blender/draw/engines/workbench/shaders/workbench_deferred_background_frag.glsl delete mode 100644 source/blender/draw/engines/workbench/shaders/workbench_deferred_composite_frag.glsl create mode 100644 source/blender/draw/engines/workbench/shaders/workbench_effect_cavity_frag.glsl delete mode 100644 source/blender/draw/engines/workbench/shaders/workbench_effect_fxaa_frag.glsl create mode 100644 source/blender/draw/engines/workbench/shaders/workbench_effect_outline_frag.glsl create mode 100644 source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_frag.glsl create mode 100644 source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_vert.glsl delete mode 100644 source/blender/draw/engines/workbench/shaders/workbench_forward_composite_frag.glsl delete mode 100644 source/blender/draw/engines/workbench/shaders/workbench_forward_depth_frag.glsl delete mode 100644 source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl delete mode 100644 source/blender/draw/engines/workbench/shaders/workbench_ghost_resolve_frag.glsl create mode 100644 source/blender/draw/engines/workbench/shaders/workbench_image_lib.glsl create mode 100644 source/blender/draw/engines/workbench/shaders/workbench_matcap_lib.glsl create mode 100644 source/blender/draw/engines/workbench/shaders/workbench_material_lib.glsl create mode 100644 source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl delete mode 100644 source/blender/draw/engines/workbench/shaders/workbench_object_outline_lib.glsl create mode 100644 source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl create mode 100644 source/blender/draw/engines/workbench/shaders/workbench_shader_interface_lib.glsl create mode 100644 source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl create mode 100644 source/blender/draw/engines/workbench/shaders/workbench_transparent_resolve_frag.glsl delete mode 100644 source/blender/draw/engines/workbench/solid_mode.c delete mode 100644 source/blender/draw/engines/workbench/transparent_mode.c delete mode 100644 source/blender/draw/engines/workbench/workbench_deferred.c delete mode 100644 source/blender/draw/engines/workbench/workbench_effect_aa.c create mode 100644 source/blender/draw/engines/workbench/workbench_effect_antialiasing.c create mode 100644 source/blender/draw/engines/workbench/workbench_effect_cavity.c delete mode 100644 source/blender/draw/engines/workbench/workbench_effect_fxaa.c create mode 100644 source/blender/draw/engines/workbench/workbench_effect_outline.c delete mode 100644 source/blender/draw/engines/workbench/workbench_effect_taa.c delete mode 100644 source/blender/draw/engines/workbench/workbench_forward.c create mode 100644 source/blender/draw/engines/workbench/workbench_opaque.c create mode 100644 source/blender/draw/engines/workbench/workbench_shader.c create mode 100644 source/blender/draw/engines/workbench/workbench_shadow.c delete mode 100644 source/blender/draw/engines/workbench/workbench_studiolight.c create mode 100644 source/blender/draw/engines/workbench/workbench_transparent.c (limited to 'source/blender/draw') diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 1f04739644e..fe03a32fd07 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -97,19 +97,18 @@ set(SRC engines/eevee/eevee_subsurface.c engines/eevee/eevee_temporal_sampling.c engines/eevee/eevee_volumes.c - engines/workbench/solid_mode.c - engines/workbench/transparent_mode.c engines/workbench/workbench_data.c - engines/workbench/workbench_deferred.c - engines/workbench/workbench_effect_aa.c + engines/workbench/workbench_effect_antialiasing.c + engines/workbench/workbench_effect_cavity.c engines/workbench/workbench_effect_dof.c - engines/workbench/workbench_effect_fxaa.c - engines/workbench/workbench_effect_taa.c + engines/workbench/workbench_effect_outline.c engines/workbench/workbench_engine.c - engines/workbench/workbench_forward.c engines/workbench/workbench_materials.c + engines/workbench/workbench_opaque.c engines/workbench/workbench_render.c - engines/workbench/workbench_studiolight.c + engines/workbench/workbench_shader.c + engines/workbench/workbench_shadow.c + engines/workbench/workbench_transparent.c engines/workbench/workbench_volume.c engines/external/external_engine.c engines/gpencil/gpencil_antialiasing.c @@ -247,28 +246,32 @@ data_to_c_simple(engines/eevee/shaders/volumetric_scatter_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/volumetric_integration_frag.glsl SRC) data_to_c_simple(engines/workbench/shaders/workbench_cavity_lib.glsl SRC) -data_to_c_simple(engines/workbench/shaders/workbench_cavity_frag.glsl SRC) data_to_c_simple(engines/workbench/shaders/workbench_common_lib.glsl SRC) +data_to_c_simple(engines/workbench/shaders/workbench_composite_frag.glsl SRC) +data_to_c_simple(engines/workbench/shaders/workbench_curvature_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_cavity_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_outline_frag.glsl SRC) +data_to_c_simple(engines/workbench/shaders/workbench_effect_smaa_frag.glsl SRC) +data_to_c_simple(engines/workbench/shaders/workbench_effect_smaa_vert.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) -data_to_c_simple(engines/workbench/shaders/workbench_forward_depth_frag.glsl SRC) -data_to_c_simple(engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl SRC) -data_to_c_simple(engines/workbench/shaders/workbench_ghost_resolve_frag.glsl SRC) -data_to_c_simple(engines/workbench/shaders/workbench_object_outline_lib.glsl SRC) -data_to_c_simple(engines/workbench/shaders/workbench_curvature_lib.glsl SRC) -data_to_c_simple(engines/workbench/shaders/workbench_prepass_vert.glsl SRC) +data_to_c_simple(engines/workbench/shaders/workbench_image_lib.glsl SRC) +data_to_c_simple(engines/workbench/shaders/workbench_matcap_lib.glsl SRC) +data_to_c_simple(engines/workbench/shaders/workbench_material_lib.glsl SRC) +data_to_c_simple(engines/workbench/shaders/workbench_merge_infront_frag.glsl SRC) data_to_c_simple(engines/workbench/shaders/workbench_prepass_frag.glsl SRC) -data_to_c_simple(engines/workbench/shaders/workbench_shadow_vert.glsl SRC) -data_to_c_simple(engines/workbench/shaders/workbench_shadow_geom.glsl SRC) +data_to_c_simple(engines/workbench/shaders/workbench_prepass_hair_vert.glsl SRC) +data_to_c_simple(engines/workbench/shaders/workbench_prepass_vert.glsl SRC) +data_to_c_simple(engines/workbench/shaders/workbench_shader_interface_lib.glsl SRC) data_to_c_simple(engines/workbench/shaders/workbench_shadow_caps_geom.glsl SRC) data_to_c_simple(engines/workbench/shaders/workbench_shadow_debug_frag.glsl SRC) -data_to_c_simple(engines/workbench/shaders/workbench_volume_vert.glsl SRC) +data_to_c_simple(engines/workbench/shaders/workbench_shadow_geom.glsl SRC) +data_to_c_simple(engines/workbench/shaders/workbench_shadow_vert.glsl SRC) +data_to_c_simple(engines/workbench/shaders/workbench_transparent_accum_frag.glsl SRC) +data_to_c_simple(engines/workbench/shaders/workbench_transparent_resolve_frag.glsl SRC) data_to_c_simple(engines/workbench/shaders/workbench_volume_frag.glsl SRC) +data_to_c_simple(engines/workbench/shaders/workbench_volume_vert.glsl SRC) data_to_c_simple(engines/workbench/shaders/workbench_world_light_lib.glsl SRC) data_to_c_simple(intern/shaders/common_colormanagement_lib.glsl SRC) diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c index cfa0fa9eb1a..740ca42800e 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.c +++ b/source/blender/draw/engines/overlay/overlay_engine.c @@ -381,9 +381,6 @@ static void OVERLAY_cache_finish(void *vedata) DRW_texture_ensure_fullscreen_2d(&dtxl->depth_in_front, GPU_DEPTH24_STENCIL8, 0); - GPU_framebuffer_ensure_config( - &dfbl->default_fb, - {GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(dtxl->color)}); GPU_framebuffer_ensure_config( &dfbl->in_front_fb, {GPU_ATTACHMENT_TEXTURE(dtxl->depth_in_front), GPU_ATTACHMENT_TEXTURE(dtxl->color)}); diff --git a/source/blender/draw/engines/overlay/overlay_wireframe.c b/source/blender/draw/engines/overlay/overlay_wireframe.c index 722055c36f0..6b0e5e0b72e 100644 --- a/source/blender/draw/engines/overlay/overlay_wireframe.c +++ b/source/blender/draw/engines/overlay/overlay_wireframe.c @@ -68,22 +68,17 @@ void OVERLAY_wireframe_cache_init(OVERLAY_Data *vedata) OVERLAY_shader_wireframe(); for (int xray = 0; xray < (is_material_shmode ? 1 : 2); xray++) { - /* Only do stencil test if stencil buffer is written by the render engine. */ - DRWState stencil_state = is_material_shmode ? 0 : DRW_STATE_STENCIL_EQUAL; DRWState state = DRW_STATE_FIRST_VERTEX_CONVENTION | DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; DRWPass *pass; - uint stencil_mask; if (xray == 0) { - DRW_PASS_CREATE(psl->wireframe_ps, state | stencil_state | pd->clipping_state); + DRW_PASS_CREATE(psl->wireframe_ps, state | pd->clipping_state); pass = psl->wireframe_ps; - stencil_mask = 0xFF; } else { DRW_PASS_CREATE(psl->wireframe_xray_ps, state | pd->clipping_state); pass = psl->wireframe_xray_ps; - stencil_mask = 0x00; } for (int use_coloring = 0; use_coloring < 2; use_coloring++) { @@ -94,17 +89,14 @@ void OVERLAY_wireframe_cache_init(OVERLAY_Data *vedata) DRW_shgroup_uniform_bool_copy(grp, "isTransform", (G.moving & G_TRANSFORM_OBJ) != 0); DRW_shgroup_uniform_bool_copy(grp, "isObjectColor", is_object_color); DRW_shgroup_uniform_bool_copy(grp, "isRandomColor", is_random_color); - DRW_shgroup_stencil_mask(grp, stencil_mask); pd->wires_all_grp[xray][use_coloring] = grp = DRW_shgroup_create(wires_sh, pass); DRW_shgroup_uniform_float_copy(grp, "wireStepParam", 1.0f); - DRW_shgroup_stencil_mask(grp, stencil_mask); } pd->wires_sculpt_grp[xray] = grp = DRW_shgroup_create(wires_sh, pass); DRW_shgroup_uniform_float_copy(grp, "wireStepParam", 10.0f); DRW_shgroup_uniform_bool_copy(grp, "useColoring", false); - DRW_shgroup_stencil_mask(grp, stencil_mask); } if (is_material_shmode) { diff --git a/source/blender/draw/engines/workbench/shaders/workbench_cavity_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_cavity_frag.glsl deleted file mode 100644 index a0e04f252e2..00000000000 --- a/source/blender/draw/engines/workbench/shaders/workbench_cavity_frag.glsl +++ /dev/null @@ -1,81 +0,0 @@ -out vec4 fragColor; - -uniform sampler2D depthBuffer; -uniform sampler2D colorBuffer; -uniform sampler2D normalBuffer; -uniform usampler2D objectId; - -uniform vec2 invertedViewportSize; -uniform mat4 WinMatrix; /* inverse WinMatrix */ - -uniform vec4 viewvecs[3]; -uniform vec4 ssao_params; -uniform vec4 ssao_settings; -uniform vec2 curvature_settings; -uniform sampler2D ssao_jitter; - -layout(std140) uniform samples_block -{ - vec4 ssao_samples[500]; -}; - -#define ssao_samples_num ssao_params.x -#define jitter_tilling ssao_params.yz -#define ssao_iteration ssao_params.w - -#define ssao_distance ssao_settings.x -#define ssao_factor_cavity ssao_settings.y -#define ssao_factor_edge ssao_settings.z -#define ssao_attenuation ssao_settings.w - -vec3 get_view_space_from_depth(in vec2 uvcoords, in float depth) -{ - if (WinMatrix[3][3] == 0.0) { - /* Perspective */ - float d = 2.0 * depth - 1.0; - - float zview = -WinMatrix[3][2] / (d + WinMatrix[2][2]); - - return zview * (viewvecs[0].xyz + vec3(uvcoords, 0.0) * viewvecs[1].xyz); - } - else { - /* Orthographic */ - vec3 offset = vec3(uvcoords, depth); - - return viewvecs[0].xyz + offset * viewvecs[1].xyz; - } -} - -/* forward declaration */ -void ssao_factors(in float depth, - in vec3 normal, - in vec3 position, - in vec2 screenco, - out float cavities, - out float edges); - -void main() -{ - vec2 screenco = vec2(gl_FragCoord.xy) * invertedViewportSize; - ivec2 texel = ivec2(gl_FragCoord.xy); - - float cavity = 0.0, edges = 0.0, curvature = 0.0; - -#ifdef USE_CAVITY - float depth = texelFetch(depthBuffer, texel, 0).x; - vec3 position = get_view_space_from_depth(screenco, depth); - vec3 normal_viewport = workbench_normal_decode(texelFetch(normalBuffer, texel, 0).rg); - - ssao_factors(depth, normal_viewport, position, screenco, cavity, edges); -#endif - -#ifdef USE_CURVATURE - curvature = calculate_curvature( - objectId, normalBuffer, texel, curvature_settings.x, curvature_settings.y); -#endif - - float final_cavity_factor = clamp((1.0 - cavity) * (1.0 + edges) * (1.0 + curvature), 0.0, 4.0); - - /* Using UNORM render target so compress the range. */ - fragColor = vec4(final_cavity_factor / CAVITY_BUFFER_RANGE); -} diff --git a/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl index 376b19cdd1b..87d04144cde 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_cavity_lib.glsl @@ -1,77 +1,87 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_data_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_common_lib.glsl) -/* from The Alchemy screen-space ambient obscurance algorithm +layout(std140) uniform samples_block +{ + vec4 samples_coords[512]; +}; + +uniform sampler2D cavityJitter; + +/* From The Alchemy screen-space ambient obscurance algorithm * http://graphics.cs.williams.edu/papers/AlchemyHPG11/VV11AlchemyAO.pdf */ -void ssao_factors(in float depth, - in vec3 normal, - in vec3 position, - in vec2 screenco, - out float cavities, - out float edges) +void cavity_compute(vec2 screenco, + sampler2D depthBuffer, + sampler2D normalBuffer, + out float cavities, + out float edges) { cavities = edges = 0.0; - /* early out if there is no need for SSAO */ - if (ssao_factor_cavity == 0.0 && ssao_factor_edge == 0.0) { + + float depth = texture(depthBuffer, screenco).x; + + /* Early out if background and infront. */ + if (depth == 1.0 || depth == 0.0) { return; } - /* take the normalized ray direction here */ - vec3 noise = texture(ssao_jitter, screenco.xy * jitter_tilling).rgb; + vec3 position = view_position_from_depth(screenco, depth, world_data.viewvecs, ProjectionMatrix); + vec3 normal = workbench_normal_decode(texture(normalBuffer, screenco)); + + vec2 jitter_co = (screenco * world_data.viewport_size.xy) * world_data.cavity_jitter_scale; + vec3 noise = texture(cavityJitter, jitter_co).rgb; /* find the offset in screen space by multiplying a point * in camera space at the depth of the point by the projection matrix. */ vec2 offset; - float homcoord = WinMatrix[2][3] * position.z + WinMatrix[3][3]; - offset.x = WinMatrix[0][0] * ssao_distance / homcoord; - offset.y = WinMatrix[1][1] * ssao_distance / homcoord; + float homcoord = ProjectionMatrix[2][3] * position.z + ProjectionMatrix[3][3]; + offset.x = ProjectionMatrix[0][0] * world_data.cavity_distance / homcoord; + offset.y = ProjectionMatrix[1][1] * world_data.cavity_distance / homcoord; /* convert from -1.0...1.0 range to 0.0..1.0 for easy use with texture coordinates */ offset *= 0.5; - int num_samples = int(ssao_samples_num); - /* Note. Putting noise usage here to put some ALU after texture fetch. */ vec2 rotX = noise.rg; vec2 rotY = vec2(-rotX.y, rotX.x); - for (int x = 0; x < num_samples; x++) { - int sample_index = x + (int(ssao_iteration) * num_samples); - if (sample_index > 500) { - continue; - } - /* ssao_samples[x].xy is sample direction (normalized). - * ssao_samples[x].z is sample distance from disk center. */ - + int sample_start = world_data.cavity_sample_start; + int sample_end = world_data.cavity_sample_end; + for (int i = sample_start; i < sample_end && i < 512; i++) { + /* sample_coord.xy is sample direction (normalized). + * sample_coord.z is sample distance from disk center. */ + vec3 sample_coord = samples_coords[i].xyz; /* Rotate with random direction to get jittered result. */ - vec2 dir_jittered = vec2(dot(ssao_samples[sample_index].xy, rotX), - dot(ssao_samples[sample_index].xy, rotY)); - dir_jittered.xy *= ssao_samples[sample_index].z + noise.b; + vec2 dir_jittered = vec2(dot(sample_coord.xy, rotX), dot(sample_coord.xy, rotY)); + dir_jittered.xy *= sample_coord.z + noise.b; - vec2 uvcoords = screenco.xy + dir_jittered * offset; - - if (uvcoords.x > 1.0 || uvcoords.x < 0.0 || uvcoords.y > 1.0 || uvcoords.y < 0.0) { + vec2 uvcoords = screenco + dir_jittered * offset; + /* Out of screen case. */ + if (any(greaterThan(abs(uvcoords - 0.5), vec2(0.5)))) { continue; } - - float depth_new = texture(depthBuffer, uvcoords).r; - + /* Sample depth. */ + float s_depth = texture(depthBuffer, uvcoords).r; /* Handle Background case */ - bool is_background = (depth_new == 1.0); - + bool is_background = (s_depth == 1.0); /* This trick provide good edge effect even if no neighbor is found. */ - vec3 pos_new = get_view_space_from_depth(uvcoords, (is_background) ? depth : depth_new); + s_depth = (is_background) ? depth : s_depth; + vec3 s_pos = view_position_from_depth( + uvcoords, s_depth, world_data.viewvecs, ProjectionMatrix); if (is_background) { - pos_new.z -= ssao_distance; + s_pos.z -= world_data.cavity_distance; } - vec3 dir = pos_new - position; + vec3 dir = s_pos - position; float len = length(dir); float f_cavities = dot(dir, normal); float f_edge = -f_cavities; float f_bias = 0.05 * len + 0.0001; - float attenuation = 1.0 / (len * (1.0 + len * len * ssao_attenuation)); + float attenuation = 1.0 / (len * (1.0 + len * len * world_data.cavity_attenuation)); /* use minor bias here to avoid self shadowing */ if (f_cavities > -f_bias) { @@ -82,11 +92,10 @@ void ssao_factors(in float depth, edges += f_edge * attenuation; } } - - cavities /= ssao_samples_num; - edges /= ssao_samples_num; + cavities *= world_data.cavity_sample_count_inv; + edges *= world_data.cavity_sample_count_inv; /* don't let cavity wash out the surface appearance */ - cavities = clamp(cavities * ssao_factor_cavity, 0.0, 1.0); - edges = edges * ssao_factor_edge; + cavities = clamp(cavities * world_data.cavity_valley_factor, 0.0, 1.0); + edges = edges * world_data.cavity_ridge_factor; } diff --git a/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl index c0d7719180b..25eaf003e07 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl @@ -1,30 +1,16 @@ -#define NO_OBJECT_ID uint(0) + #define EPSILON 0.00001 #define M_PI 3.14159265358979323846 #define CAVITY_BUFFER_RANGE 4.0 -/* 4x4 bayer matrix prepared for 8bit UNORM precision error. */ -#define P(x) (((x + 0.5) * (1.0 / 16.0) - 0.5) * (1.0 / 255.0)) -const vec4 dither_mat4x4[4] = vec4[4](vec4(P(0.0), P(8.0), P(2.0), P(10.0)), - vec4(P(12.0), P(4.0), P(14.0), P(6.0)), - vec4(P(3.0), P(11.0), P(1.0), P(9.0)), - vec4(P(15.0), P(7.0), P(13.0), P(5.0))); - -float bayer_dither_noise() -{ - ivec2 tx1 = ivec2(gl_FragCoord.xy) % 4; - ivec2 tx2 = ivec2(gl_FragCoord.xy) % 2; - return dither_mat4x4[tx1.x][tx1.y]; -} - #ifdef WORKBENCH_ENCODE_NORMALS # define WB_Normal vec2 /* From http://aras-p.info/texts/CompactNormalStorage.html * Using Method #4: Spheremap Transform */ -vec3 workbench_normal_decode(WB_Normal enc) +vec3 workbench_normal_decode(vec4 enc) { vec2 fenc = enc.xy * 4.0 - 2.0; float f = dot(fenc, fenc); @@ -37,8 +23,9 @@ vec3 workbench_normal_decode(WB_Normal enc) /* From http://aras-p.info/texts/CompactNormalStorage.html * Using Method #4: Spheremap Transform */ -WB_Normal workbench_normal_encode(vec3 n) +WB_Normal workbench_normal_encode(bool front_face, vec3 n) { + n = normalize(front_face ? n : -n); float p = sqrt(n.z * 8.0 + 8.0); n.xy = clamp(n.xy / p + 0.5, 0.0, 1.0); return n.xy; @@ -47,161 +34,64 @@ WB_Normal workbench_normal_encode(vec3 n) #else # define WB_Normal vec3 /* Well just do nothing... */ -# define workbench_normal_encode(a) (a) -# define workbench_normal_decode(a) (a) +# define workbench_normal_encode(f, a) (a) +# define workbench_normal_decode(a) (a.xyz) #endif /* WORKBENCH_ENCODE_NORMALS */ -/* Encoding into the alpha of a RGBA8 UNORM texture. */ +/* Encoding into the alpha of a RGBA16F texture. (10bit mantissa) */ #define TARGET_BITCOUNT 8u #define METALLIC_BITS 3u /* Metallic channel is less important. */ #define ROUGHNESS_BITS (TARGET_BITCOUNT - METALLIC_BITS) -#define TOTAL_BITS (METALLIC_BITS + ROUGHNESS_BITS) /* Encode 2 float into 1 with the desired precision. */ float workbench_float_pair_encode(float v1, float v2) { - // const uint total_mask = ~(0xFFFFFFFFu << TOTAL_BITS); // const uint v1_mask = ~(0xFFFFFFFFu << ROUGHNESS_BITS); // const uint v2_mask = ~(0xFFFFFFFFu << METALLIC_BITS); /* Same as above because some compiler are dumb af. and think we use mediump int. */ - const int total_mask = 0xFF; const int v1_mask = 0x1F; const int v2_mask = 0x7; int iv1 = int(v1 * float(v1_mask)); int iv2 = int(v2 * float(v2_mask)) << int(ROUGHNESS_BITS); - return float(iv1 | iv2) * (1.0 / float(total_mask)); + return float(iv1 | iv2); } void workbench_float_pair_decode(float data, out float v1, out float v2) { - // const uint total_mask = ~(0xFFFFFFFFu << TOTAL_BITS); // const uint v1_mask = ~(0xFFFFFFFFu << ROUGHNESS_BITS); // const uint v2_mask = ~(0xFFFFFFFFu << METALLIC_BITS); /* Same as above because some compiler are dumb af. and think we use mediump int. */ - const int total_mask = 0xFF; const int v1_mask = 0x1F; const int v2_mask = 0x7; - int idata = int(data * float(total_mask)); + int idata = int(data); v1 = float(idata & v1_mask) * (1.0 / float(v1_mask)); v2 = float(idata >> int(ROUGHNESS_BITS)) * (1.0 / float(v2_mask)); } -float calculate_transparent_weight(float z, float alpha) -{ -#if 0 - /* Eq 10 : Good for surfaces with varying opacity (like particles) */ - float a = min(1.0, alpha * 10.0) + 0.01; - float b = -gl_FragCoord.z * 0.95 + 1.0; - float w = a * a * a * 3e2 * b * b * b; -#else - /* Eq 7 put more emphasis on surfaces closer to the view. */ - // float w = 10.0 / (1e-5 + pow(abs(z) / 5.0, 2.0) + pow(abs(z) / 200.0, 6.0)); /* Eq 7 */ - // float w = 10.0 / (1e-5 + pow(abs(z) / 10.0, 3.0) + pow(abs(z) / 200.0, 6.0)); /* Eq 8 */ - // float w = 10.0 / (1e-5 + pow(abs(z) / 200.0, 4.0)); /* Eq 9 */ - /* Same as eq 7, but optimized. */ - float a = abs(z) / 5.0; - float b = abs(z) / 200.0; - b *= b; - float w = 10.0 / ((1e-5 + a * a) + b * (b * b)); /* Eq 7 */ -#endif - return alpha * clamp(w, 1e-2, 3e2); -} - -/* Special function only to be used with calculate_transparent_weight(). */ -float linear_zdepth(float depth, vec4 viewvecs[3], mat4 proj_mat) +vec3 view_vector_from_screen_uv(vec2 uv, vec4 viewvecs[3], mat4 proj_mat) { if (proj_mat[3][3] == 0.0) { - float d = 2.0 * depth - 1.0; - return -proj_mat[3][2] / (d + proj_mat[2][2]); + return normalize(viewvecs[0].xyz + vec3(uv, 0.0) * viewvecs[1].xyz); } else { - /* Return depth from near plane. */ - return depth * viewvecs[1].z; - } -} - -vec3 view_vector_from_screen_uv(vec2 uv, vec4 viewvecs[3], mat4 proj_mat) -{ - return (proj_mat[3][3] == 0.0) ? normalize(viewvecs[0].xyz + vec3(uv, 0.0) * viewvecs[1].xyz) : - vec3(0.0, 0.0, 1.0); -} - -vec2 matcap_uv_compute(vec3 I, vec3 N, bool flipped) -{ - /* Quick creation of an orthonormal basis */ - float a = 1.0 / (1.0 + I.z); - float b = -I.x * I.y * a; - vec3 b1 = vec3(1.0 - I.x * I.x * a, b, -I.x); - vec3 b2 = vec3(b, 1.0 - I.y * I.y * a, -I.y); - vec2 matcap_uv = vec2(dot(b1, N), dot(b2, N)); - if (flipped) { - matcap_uv.x = -matcap_uv.x; + return vec3(0.0, 0.0, 1.0); } - return matcap_uv * 0.496 + 0.5; } -bool node_tex_tile_lookup(inout vec3 co, sampler2DArray ima, sampler1DArray map) +vec3 view_position_from_depth(vec2 uvcoords, float depth, vec4 viewvecs[3], mat4 proj_mat) { - vec2 tile_pos = floor(co.xy); - - if (tile_pos.x < 0 || tile_pos.y < 0 || tile_pos.x >= 10) - return false; - - float tile = 10.0 * tile_pos.y + tile_pos.x; - if (tile >= textureSize(map, 0).x) - return false; - - /* Fetch tile information. */ - float tile_layer = texelFetch(map, ivec2(tile, 0), 0).x; - if (tile_layer < 0.0) - return false; - - vec4 tile_info = texelFetch(map, ivec2(tile, 1), 0); - - co = vec3(((co.xy - tile_pos) * tile_info.zw) + tile_info.xy, tile_layer); - return true; -} + if (proj_mat[3][3] == 0.0) { + /* Perspective */ + float d = 2.0 * depth - 1.0; -vec4 workbench_sample_texture(sampler2D image, - vec2 coord, - bool nearest_sampling, - bool premultiplied) -{ - vec2 tex_size = vec2(textureSize(image, 0).xy); - /* TODO(fclem) We could do the same with sampler objects. - * But this is a quick workaround instead of messing with the GPUTexture itself. */ - vec2 uv = nearest_sampling ? (floor(coord * tex_size) + 0.5) / tex_size : coord; - vec4 color = texture(image, uv); + float zview = -proj_mat[3][2] / (d + proj_mat[2][2]); - /* Unpremultiply if stored multiplied, since straight alpha is expected by shaders. */ - if (premultiplied && !(color.a == 0.0 || color.a == 1.0)) { - color.rgb = color.rgb / color.a; + return zview * (viewvecs[0].xyz + vec3(uvcoords, 0.0) * viewvecs[1].xyz); } + else { + /* Orthographic */ + vec3 offset = vec3(uvcoords, depth); - return color; -} - -vec4 workbench_sample_texture_array(sampler2DArray tile_array, - sampler1DArray tile_data, - vec2 coord, - bool nearest_sampling, - bool premultiplied) -{ - vec2 tex_size = vec2(textureSize(tile_array, 0).xy); - - vec3 uv = vec3(coord, 0); - if (!node_tex_tile_lookup(uv, tile_array, tile_data)) - return vec4(1.0, 0.0, 1.0, 1.0); - - /* TODO(fclem) We could do the same with sampler objects. - * But this is a quick workaround instead of messing with the GPUTexture itself. */ - uv.xy = nearest_sampling ? (floor(uv.xy * tex_size) + 0.5) / tex_size : uv.xy; - vec4 color = texture(tile_array, uv); - - /* Unpremultiply if stored multiplied, since straight alpha is expected by shaders. */ - if (premultiplied && !(color.a == 0.0 || color.a == 1.0)) { - color.rgb = color.rgb / color.a; + return viewvecs[0].xyz + offset * viewvecs[1].xyz; } - - return color; } diff --git a/source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl new file mode 100644 index 00000000000..cdb9823096c --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/workbench_composite_frag.glsl @@ -0,0 +1,44 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_common_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_matcap_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_world_light_lib.glsl) + +uniform sampler2D materialBuffer; +uniform sampler2D normalBuffer; + +in vec4 uvcoordsvar; + +out vec4 fragColor; + +void main() +{ + /* Normal and Incident vector are in viewspace. Lighting is evaluated in viewspace. */ + vec3 I = view_vector_from_screen_uv(uvcoordsvar.st, world_data.viewvecs, ProjectionMatrix); + vec3 N = workbench_normal_decode(texture(normalBuffer, uvcoordsvar.st)); + vec4 mat_data = texture(materialBuffer, uvcoordsvar.st); + + vec3 base_color = mat_data.rgb; + + float roughness, metallic; + workbench_float_pair_decode(mat_data.a, roughness, metallic); + +#ifdef V3D_LIGHTING_MATCAP + /* When using matcaps, mat_data.a is the backface sign. */ + N = (mat_data.a > 0.0) ? N : -N; + + fragColor.rgb = get_matcap_lighting(base_color, N, I); +#endif + +#ifdef V3D_LIGHTING_STUDIO + fragColor.rgb = get_world_lighting(base_color, roughness, metallic, N, I); +#endif + +#ifdef V3D_LIGHTING_FLAT + fragColor.rgb = base_color; +#endif + + fragColor.rgb *= get_shadow(N); + + fragColor.a = 1.0; +} diff --git a/source/blender/draw/engines/workbench/shaders/workbench_curvature_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_curvature_lib.glsl index 22dc906be83..e6bc4c7bbc6 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_curvature_lib.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_curvature_lib.glsl @@ -1,6 +1,5 @@ -#ifndef CURVATURE_OFFSET -# define CURVATURE_OFFSET 1 -#endif + +#pragma BLENDER_REQUIRE(workbench_data_lib.glsl) float curvature_soft_clamp(float curvature, float control) { @@ -10,33 +9,35 @@ float curvature_soft_clamp(float curvature, float control) return 0.25 / control; } -float calculate_curvature( - usampler2D objectId, sampler2D normalBuffer, ivec2 texel, float ridge, float valley) +void curvature_compute(vec2 uv, + usampler2D objectIdBuffer, + sampler2D normalBuffer, + out float curvature) { - uint object_up = texelFetchOffset(objectId, texel, 0, ivec2(0, CURVATURE_OFFSET)).r; - uint object_down = texelFetchOffset(objectId, texel, 0, ivec2(0, -CURVATURE_OFFSET)).r; - uint object_left = texelFetchOffset(objectId, texel, 0, ivec2(-CURVATURE_OFFSET, 0)).r; - uint object_right = texelFetchOffset(objectId, texel, 0, ivec2(CURVATURE_OFFSET, 0)).r; + curvature = 0.0; + + vec3 offset = vec3(world_data.viewport_size_inv, 0.0) * world_data.ui_scale; + uint object_up = texture(objectIdBuffer, uv + offset.zy).r; + uint object_down = texture(objectIdBuffer, uv - offset.zy).r; + uint object_right = texture(objectIdBuffer, uv + offset.xz).r; + uint object_left = texture(objectIdBuffer, uv - offset.xz).r; + /* Remove object outlines. */ if ((object_up != object_down) || (object_right != object_left)) { - return 0.0; + return; } - vec2 normal_up = texelFetchOffset(normalBuffer, texel, 0, ivec2(0, CURVATURE_OFFSET)).rg; - vec2 normal_down = texelFetchOffset(normalBuffer, texel, 0, ivec2(0, -CURVATURE_OFFSET)).rg; - vec2 normal_left = texelFetchOffset(normalBuffer, texel, 0, ivec2(-CURVATURE_OFFSET, 0)).rg; - vec2 normal_right = texelFetchOffset(normalBuffer, texel, 0, ivec2(CURVATURE_OFFSET, 0)).rg; - - normal_up = workbench_normal_decode(normal_up).rg; - normal_down = workbench_normal_decode(normal_down).rg; - normal_left = workbench_normal_decode(normal_left).rg; - normal_right = workbench_normal_decode(normal_right).rg; + float normal_up = workbench_normal_decode(texture(normalBuffer, uv + offset.zy)).g; + float normal_down = workbench_normal_decode(texture(normalBuffer, uv - offset.zy)).g; + float normal_right = workbench_normal_decode(texture(normalBuffer, uv + offset.xz)).r; + float normal_left = workbench_normal_decode(texture(normalBuffer, uv - offset.xz)).r; - float normal_diff = ((normal_up.g - normal_down.g) + (normal_right.r - normal_left.r)); + float normal_diff = (normal_up - normal_down) + (normal_right - normal_left); if (normal_diff < 0) { - return -2.0 * curvature_soft_clamp(-normal_diff, valley); + curvature = -2.0 * curvature_soft_clamp(-normal_diff, world_data.curvature_valley); + } + else { + curvature = 2.0 * curvature_soft_clamp(normal_diff, world_data.curvature_ridge); } - - return 2.0 * curvature_soft_clamp(normal_diff, ridge); } diff --git a/source/blender/draw/engines/workbench/shaders/workbench_data_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_data_lib.glsl index 0c984b094d3..5f3283e1643 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_data_lib.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_data_lib.glsl @@ -5,12 +5,42 @@ struct LightData { }; struct WorldData { + vec4 viewvecs[3]; + vec4 viewport_size; vec4 object_outline_color; vec4 shadow_direction_vs; + float shadow_focus; + float shadow_shift; + float shadow_mul; + float shadow_add; + /* - 16 bytes alignment- */ LightData lights[4]; vec4 ambient_color; - int num_lights; - int matcap_orientation; + + int cavity_sample_start; + int cavity_sample_end; + float cavity_sample_count_inv; + float cavity_jitter_scale; + + float cavity_valley_factor; + float cavity_ridge_factor; + float cavity_attenuation; + float cavity_distance; + float curvature_ridge; float curvature_valley; + float ui_scale; + float _pad0; + + int matcap_orientation; + bool use_specular; + int _pad1; + int _pad2; +}; + +#define viewport_size_inv viewport_size.zw + +layout(std140) uniform world_block +{ + WorldData world_data; }; diff --git a/source/blender/draw/engines/workbench/shaders/workbench_deferred_background_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_deferred_background_frag.glsl deleted file mode 100644 index 22fa2babbbf..00000000000 --- a/source/blender/draw/engines/workbench/shaders/workbench_deferred_background_frag.glsl +++ /dev/null @@ -1,30 +0,0 @@ - -uniform usampler2D objectId; - -uniform vec2 invertedViewportSize; - -out vec4 fragColor; - -layout(std140) uniform world_block -{ - WorldData world_data; -}; - -void main() -{ - vec2 uv_viewport = gl_FragCoord.xy * invertedViewportSize; - -#ifndef V3D_SHADING_OBJECT_OUTLINE - - fragColor = vec4(0.0); - -#else /* !V3D_SHADING_OBJECT_OUTLINE */ - - ivec2 texel = ivec2(gl_FragCoord.xy); - uint object_id = texelFetch(objectId, texel, 0).r; - float object_outline = calculate_object_outline(objectId, texel, object_id); - - fragColor = vec4(world_data.object_outline_color.rgb, 1.0) * (1.0 - object_outline); - -#endif /* !V3D_SHADING_OBJECT_OUTLINE */ -} diff --git a/source/blender/draw/engines/workbench/shaders/workbench_deferred_composite_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_deferred_composite_frag.glsl deleted file mode 100644 index fd4cea4279a..00000000000 --- a/source/blender/draw/engines/workbench/shaders/workbench_deferred_composite_frag.glsl +++ /dev/null @@ -1,103 +0,0 @@ -out vec4 fragColor; - -uniform usampler2D objectId; -uniform sampler2D materialBuffer; -uniform sampler2D normalBuffer; -/* normalBuffer contains viewport normals */ -uniform sampler2D cavityBuffer; -uniform sampler2D matcapDiffuseImage; -uniform sampler2D matcapSpecularImage; - -uniform vec2 invertedViewportSize; -uniform vec4 viewvecs[3]; -uniform float shadowMultiplier; -uniform float lightMultiplier; -uniform float shadowShift = 0.1; -uniform float shadowFocus = 1.0; - -uniform vec3 materialSingleColor; - -layout(std140) uniform world_block -{ - WorldData world_data; -}; - -void main() -{ - ivec2 texel = ivec2(gl_FragCoord.xy); - vec2 uv_viewport = gl_FragCoord.xy * invertedViewportSize; - - float roughness, metallic; - vec3 base_color; - -#ifndef MATDATA_PASS_ENABLED - base_color = materialSingleColor; - metallic = 0.0; - roughness = 0.5; -#else - vec4 material_data = texelFetch(materialBuffer, texel, 0); - base_color = material_data.rgb; - workbench_float_pair_decode(material_data.a, roughness, metallic); -#endif - -/* Do we need normals */ -#ifdef NORMAL_VIEWPORT_PASS_ENABLED - vec3 normal_viewport = workbench_normal_decode(texelFetch(normalBuffer, texel, 0).rg); -#endif - - vec3 I_vs = view_vector_from_screen_uv(uv_viewport, viewvecs, ProjectionMatrix); - - /* -------- SHADING --------- */ -#ifdef V3D_LIGHTING_FLAT - vec3 shaded_color = base_color; - -#elif defined(V3D_LIGHTING_MATCAP) - /* When using matcaps, the metallic is the backface sign. */ - normal_viewport = (metallic > 0.0) ? normal_viewport : -normal_viewport; - bool flipped = world_data.matcap_orientation != 0; - vec2 matcap_uv = matcap_uv_compute(I_vs, normal_viewport, flipped); - vec3 matcap_diffuse = textureLod(matcapDiffuseImage, matcap_uv, 0.0).rgb; - -# ifdef V3D_SHADING_SPECULAR_HIGHLIGHT - vec3 matcap_specular = textureLod(matcapSpecularImage, matcap_uv, 0.0).rgb; -# else - vec3 matcap_specular = vec3(0.0); -# endif - - vec3 shaded_color = matcap_diffuse * base_color + matcap_specular; - -#elif defined(V3D_LIGHTING_STUDIO) - -# ifdef V3D_SHADING_SPECULAR_HIGHLIGHT - vec3 specular_color = mix(vec3(0.05), base_color, metallic); - vec3 diffuse_color = mix(base_color, vec3(0.0), metallic); -# else - roughness = 0.0; - vec3 specular_color = vec3(0.0); - vec3 diffuse_color = base_color; -# endif - - vec3 shaded_color = get_world_lighting( - world_data, diffuse_color, specular_color, roughness, normal_viewport, I_vs); -#endif - - /* -------- POST EFFECTS --------- */ -#ifdef WB_CAVITY - /* Using UNORM texture so decompress the range */ - shaded_color *= texelFetch(cavityBuffer, texel, 0).r * CAVITY_BUFFER_RANGE; -#endif - -#ifdef V3D_SHADING_SHADOW - float light_factor = -dot(normal_viewport, world_data.shadow_direction_vs.xyz); - float shadow_mix = smoothstep(shadowFocus, shadowShift, light_factor); - shaded_color *= mix(lightMultiplier, shadowMultiplier, shadow_mix); -#endif - -#ifdef V3D_SHADING_OBJECT_OUTLINE - uint object_id = texelFetch(objectId, texel, 0).r; - float object_outline = calculate_object_outline(objectId, texel, object_id); - shaded_color = mix(world_data.object_outline_color.rgb, shaded_color, object_outline); -#endif - - fragColor = vec4(shaded_color, 1.0); -} diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_cavity_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_cavity_frag.glsl new file mode 100644 index 00000000000..328d50e69e0 --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_cavity_frag.glsl @@ -0,0 +1,31 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_common_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_cavity_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_curvature_lib.glsl) + +uniform sampler2D depthBuffer; +uniform sampler2D normalBuffer; +uniform usampler2D objectIdBuffer; + +in vec4 uvcoordsvar; + +out vec4 fragColor; + +void main() +{ + float cavity = 0.0, edges = 0.0, curvature = 0.0; + +#ifdef USE_CAVITY + cavity_compute(uvcoordsvar.st, depthBuffer, normalBuffer, cavity, edges); +#endif + +#ifdef USE_CURVATURE + curvature_compute(uvcoordsvar.st, objectIdBuffer, normalBuffer, curvature); +#endif + + float final_cavity_factor = clamp((1.0 - cavity) * (1.0 + edges) * (1.0 + curvature), 0.0, 4.0); + + fragColor.rgb = vec3(final_cavity_factor); + fragColor.a = 1.0; +} diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_fxaa_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_fxaa_frag.glsl deleted file mode 100644 index 95ca2c0c297..00000000000 --- a/source/blender/draw/engines/workbench/shaders/workbench_effect_fxaa_frag.glsl +++ /dev/null @@ -1,14 +0,0 @@ - -in vec4 uvcoordsvar; - -out vec4 FragColor; - -uniform sampler2D colorBuffer; -uniform vec2 invertedViewportSize; - -void main() -{ - ivec2 texel = ivec2(gl_FragCoord.xy); - FragColor = FxaaPixelShader( - uvcoordsvar.st, colorBuffer, invertedViewportSize, 1.0, 0.166, 0.0833); -} diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_outline_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_outline_frag.glsl new file mode 100644 index 00000000000..fb6fdb93462 --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_outline_frag.glsl @@ -0,0 +1,24 @@ + +#pragma BLENDER_REQUIRE(workbench_data_lib.glsl) + +uniform usampler2D objectIdBuffer; + +in vec4 uvcoordsvar; + +out vec4 fragColor; + +void main() +{ + vec3 offset = vec3(world_data.viewport_size_inv, 0.0) * world_data.ui_scale; + vec2 uv = uvcoordsvar.st; + + uint center_id = texture(objectIdBuffer, uv).r; + uvec4 adjacent_ids = uvec4(texture(objectIdBuffer, uv + offset.zy).r, + texture(objectIdBuffer, uv - offset.zy).r, + texture(objectIdBuffer, uv + offset.xz).r, + texture(objectIdBuffer, uv - offset.xz).r); + + float outline_opacity = 1.0 - dot(vec4(equal(uvec4(center_id), adjacent_ids)), vec4(0.25)); + + fragColor = world_data.object_outline_color * outline_opacity; +} diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_frag.glsl new file mode 100644 index 00000000000..2dea2fc4883 --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_frag.glsl @@ -0,0 +1,44 @@ + +uniform sampler2D edgesTex; +uniform sampler2D areaTex; +uniform sampler2D searchTex; +uniform sampler2D blendTex; +uniform sampler2D colorTex; +uniform float mixFactor; +uniform float taaSampleCountInv; + +in vec2 uvs; +in vec2 pixcoord; +in vec4 offset[3]; + +#if SMAA_STAGE == 0 +out vec2 fragColor; +#else +out vec4 fragColor; +#endif + +void main() +{ +#if SMAA_STAGE == 0 + /* Detect edges in color and revealage buffer. */ + fragColor = SMAALumaEdgeDetectionPS(uvs, offset, colorTex); + /* Discard if there is no edge. */ + if (dot(fragColor, float2(1.0, 1.0)) == 0.0) { + discard; + } + +#elif SMAA_STAGE == 1 + fragColor = SMAABlendingWeightCalculationPS( + uvs, pixcoord, offset, edgesTex, areaTex, searchTex, vec4(0)); + +#elif SMAA_STAGE == 2 + fragColor = vec4(0.0); + if (mixFactor > 0.0) { + fragColor += SMAANeighborhoodBlendingPS(uvs, offset[0], colorTex, blendTex) * mixFactor; + } + if (mixFactor < 1.0) { + fragColor += texture(colorTex, uvs) * (1.0 - mixFactor); + } + fragColor *= taaSampleCountInv; +#endif +} diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_vert.glsl new file mode 100644 index 00000000000..07734d19972 --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_smaa_vert.glsl @@ -0,0 +1,21 @@ + +out vec2 uvs; +out vec2 pixcoord; +out vec4 offset[3]; + +void main() +{ + int v = gl_VertexID % 3; + float x = -1.0 + float((v & 1) << 2); + float y = -1.0 + float((v & 2) << 1); + gl_Position = vec4(x, y, 1.0, 1.0); + uvs = (gl_Position.xy + 1.0) * 0.5; + +#if SMAA_STAGE == 0 + SMAAEdgeDetectionVS(uvs, offset); +#elif SMAA_STAGE == 1 + SMAABlendingWeightCalculationVS(uvs, pixcoord, offset); +#elif SMAA_STAGE == 2 + SMAANeighborhoodBlendingVS(uvs, offset[0]); +#endif +} diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_taa_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_taa_frag.glsl index 5795268f794..b877c2c3f76 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_effect_taa_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_taa_frag.glsl @@ -1,14 +1,11 @@ -uniform sampler2D historyBuffer; + uniform sampler2D colorBuffer; -out vec4 colorOutput; +in vec4 uvcoordsvar; -uniform float mixFactor; +out vec4 fragColor; void main() { - ivec2 texel = ivec2(gl_FragCoord.xy); - vec4 color_buffer = texelFetch(colorBuffer, texel, 0); - vec4 history_buffer = texelFetch(historyBuffer, texel, 0); - colorOutput = mix(history_buffer, color_buffer, mixFactor); + fragColor = texture(colorBuffer, uvcoordsvar.st); } diff --git a/source/blender/draw/engines/workbench/shaders/workbench_forward_composite_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_forward_composite_frag.glsl deleted file mode 100644 index 0a4d64b37ad..00000000000 --- a/source/blender/draw/engines/workbench/shaders/workbench_forward_composite_frag.glsl +++ /dev/null @@ -1,36 +0,0 @@ -out vec4 fragColor; - -uniform usampler2D objectId; -uniform sampler2D transparentAccum; -uniform sampler2D transparentRevealage; -uniform vec2 invertedViewportSize; - -#ifndef ALPHA_COMPOSITE -layout(std140) uniform world_block -{ - WorldData world_data; -}; -#endif - -/* TODO: Bypass the whole shader if there is no xray pass and no outline pass. */ -void main() -{ - ivec2 texel = ivec2(gl_FragCoord.xy); - vec2 uv_viewport = gl_FragCoord.xy * invertedViewportSize; - - /* Listing 4 */ - vec4 trans_accum = texelFetch(transparentAccum, texel, 0); - float trans_revealage = trans_accum.a; - trans_accum.a = texelFetch(transparentRevealage, texel, 0).r; - - vec3 trans_color = trans_accum.rgb / clamp(trans_accum.a, 1e-4, 5e4); - - fragColor.a = 1.0 - trans_revealage; - fragColor.rgb = trans_color * fragColor.a; - -#ifdef V3D_SHADING_OBJECT_OUTLINE - uint object_id = texelFetch(objectId, texel, 0).r; - float outline = calculate_object_outline(objectId, texel, object_id); - fragColor = mix(vec4(world_data.object_outline_color.rgb, 1.0), fragColor, outline); -#endif -} diff --git a/source/blender/draw/engines/workbench/shaders/workbench_forward_depth_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_forward_depth_frag.glsl deleted file mode 100644 index abd8c1f6579..00000000000 --- a/source/blender/draw/engines/workbench/shaders/workbench_forward_depth_frag.glsl +++ /dev/null @@ -1,20 +0,0 @@ - -layout(location = 0) out uint objectId; - -uniform float ImageTransparencyCutoff = 0.1; -#ifdef V3D_SHADING_TEXTURE_COLOR -uniform sampler2D image; - -in vec2 uv_interp; -#endif - -void main() -{ -#ifdef V3D_SHADING_TEXTURE_COLOR - if (texture(image, uv_interp).a < ImageTransparencyCutoff) { - discard; - } -#endif - - objectId = uint(resource_id + 1) & 0xFFu; -} diff --git a/source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl deleted file mode 100644 index 559dc07c107..00000000000 --- a/source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl +++ /dev/null @@ -1,118 +0,0 @@ - -uniform float ImageTransparencyCutoff = 0.1; -#ifdef TEXTURE_IMAGE_ARRAY -uniform sampler2DArray image_tile_array; -uniform sampler1DArray image_tile_data; -#else -uniform sampler2D image; -#endif -uniform bool imageNearest; -uniform bool imagePremultiplied; - -uniform float alpha = 0.5; -uniform vec2 invertedViewportSize; -uniform vec4 viewvecs[3]; - -uniform vec4 materialColorAndMetal; -uniform float materialRoughness; - -uniform float shadowMultiplier = 0.5; -uniform float lightMultiplier = 1.0; -uniform float shadowShift = 0.1; -uniform float shadowFocus = 1.0; - -#ifdef NORMAL_VIEWPORT_PASS_ENABLED -in vec3 normal_viewport; -#endif /* NORMAL_VIEWPORT_PASS_ENABLED */ -#ifdef V3D_SHADING_TEXTURE_COLOR -in vec2 uv_interp; -#endif -#ifdef V3D_SHADING_VERTEX_COLOR -in vec3 vertexColor; -#endif -#ifdef V3D_LIGHTING_MATCAP -uniform sampler2D matcapDiffuseImage; -uniform sampler2D matcapSpecularImage; -#endif - -layout(std140) uniform world_block -{ - WorldData world_data; -}; - -layout(location = 0) out vec4 transparentAccum; -layout(location = 1) out - float revealageAccum; /* revealage actually stored in transparentAccum.a */ - -void main() -{ - vec4 base_color; - -#if defined(V3D_SHADING_TEXTURE_COLOR) -# ifdef TEXTURE_IMAGE_ARRAY - base_color = workbench_sample_texture_array( - image_tile_array, image_tile_data, uv_interp, imageNearest, imagePremultiplied); -# else - base_color = workbench_sample_texture(image, uv_interp, imageNearest, imagePremultiplied); -# endif - if (base_color.a < ImageTransparencyCutoff) { - discard; - } -#elif defined(V3D_SHADING_VERTEX_COLOR) - base_color.rgb = vertexColor; -#else - base_color.rgb = materialColorAndMetal.rgb; -#endif /* V3D_SHADING_TEXTURE_COLOR */ - - vec2 uv_viewport = gl_FragCoord.xy * invertedViewportSize; - vec3 I_vs = view_vector_from_screen_uv(uv_viewport, viewvecs, ProjectionMatrix); - -#ifdef NORMAL_VIEWPORT_PASS_ENABLED - vec3 nor = normalize(normal_viewport); -#endif - - /* -------- SHADING --------- */ -#ifdef V3D_LIGHTING_FLAT - vec3 shaded_color = base_color.rgb; - -#elif defined(V3D_LIGHTING_MATCAP) - bool flipped = world_data.matcap_orientation != 0; - vec2 matcap_uv = matcap_uv_compute(I_vs, nor, flipped); - vec3 matcap_diffuse = textureLod(matcapDiffuseImage, matcap_uv, 0.0).rgb; -# ifdef V3D_SHADING_SPECULAR_HIGHLIGHT - vec3 matcap_specular = textureLod(matcapSpecularImage, matcap_uv, 0.0).rgb; -# else - vec3 matcap_specular = vec3(0.0); -# endif - vec3 shaded_color = matcap_diffuse * base_color.rgb + matcap_specular; - -#elif defined(V3D_LIGHTING_STUDIO) -# ifdef V3D_SHADING_SPECULAR_HIGHLIGHT - float metallic = materialColorAndMetal.a; - vec3 specular_color = mix(vec3(0.05), base_color.rgb, metallic); - vec3 diffuse_color = mix(base_color.rgb, vec3(0.0), metallic); -# else - vec3 specular_color = vec3(0.0); - vec3 diffuse_color = base_color.rgb; -# endif - - vec3 shaded_color = get_world_lighting( - world_data, diffuse_color, specular_color, materialRoughness, nor, I_vs); -#endif - -#ifdef V3D_SHADING_SHADOW - float light_factor = -dot(nor, world_data.shadow_direction_vs.xyz); - float shadow_mix = smoothstep(shadowFocus, shadowShift, light_factor); - shaded_color *= mix(lightMultiplier, shadowMultiplier, shadow_mix); -#endif - - /* Based on : - * McGuire and Bavoil, Weighted Blended Order-Independent Transparency, Journal of - * Computer Graphics Techniques (JCGT), vol. 2, no. 2, 122–141, 2013 - */ - /* Listing 4 */ - float z = linear_zdepth(gl_FragCoord.z, viewvecs, ProjectionMatrix); - float weight = calculate_transparent_weight(z, alpha); - transparentAccum = vec4(shaded_color * weight, alpha); - revealageAccum = weight; -} diff --git a/source/blender/draw/engines/workbench/shaders/workbench_ghost_resolve_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_ghost_resolve_frag.glsl deleted file mode 100644 index d223a7650c5..00000000000 --- a/source/blender/draw/engines/workbench/shaders/workbench_ghost_resolve_frag.glsl +++ /dev/null @@ -1,13 +0,0 @@ -uniform sampler2D depthBuffer; - -void main(void) -{ - float depth = texelFetch(depthBuffer, ivec2(gl_FragCoord.xy), 0).r; - - /* background, discard */ - if (depth >= 1.0) { - discard; - } - - gl_FragDepth = depth; -} diff --git a/source/blender/draw/engines/workbench/shaders/workbench_image_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_image_lib.glsl new file mode 100644 index 00000000000..6f99739f259 --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/workbench_image_lib.glsl @@ -0,0 +1,83 @@ + +/* TODO(fclem) deduplicate code. */ +bool node_tex_tile_lookup(inout vec3 co, sampler2DArray ima, sampler1DArray map) +{ + vec2 tile_pos = floor(co.xy); + + if (tile_pos.x < 0 || tile_pos.y < 0 || tile_pos.x >= 10) + return false; + + float tile = 10.0 * tile_pos.y + tile_pos.x; + if (tile >= textureSize(map, 0).x) + return false; + + /* Fetch tile information. */ + float tile_layer = texelFetch(map, ivec2(tile, 0), 0).x; + if (tile_layer < 0.0) + return false; + + vec4 tile_info = texelFetch(map, ivec2(tile, 1), 0); + + co = vec3(((co.xy - tile_pos) * tile_info.zw) + tile_info.xy, tile_layer); + return true; +} + +vec4 workbench_sample_texture(sampler2D image, vec2 coord, bool nearest_sampling) +{ + vec2 tex_size = vec2(textureSize(image, 0).xy); + /* TODO(fclem) We could do the same with sampler objects. + * But this is a quick workaround instead of messing with the GPUTexture itself. */ + vec2 uv = nearest_sampling ? (floor(coord * tex_size) + 0.5) / tex_size : coord; + return texture(image, uv); +} + +vec4 workbench_sample_texture_array(sampler2DArray tile_array, + sampler1DArray tile_data, + vec2 coord, + bool nearest_sampling) +{ + vec2 tex_size = vec2(textureSize(tile_array, 0).xy); + + vec3 uv = vec3(coord, 0); + if (!node_tex_tile_lookup(uv, tile_array, tile_data)) + return vec4(1.0, 0.0, 1.0, 1.0); + + /* TODO(fclem) We could do the same with sampler objects. + * But this is a quick workaround instead of messing with the GPUTexture itself. */ + uv.xy = nearest_sampling ? (floor(uv.xy * tex_size) + 0.5) / tex_size : uv.xy; + return texture(tile_array, uv); +} + +uniform sampler2DArray imageTileArray; +uniform sampler1DArray imageTileData; +uniform sampler2D imageTexture; + +uniform float imageTransparencyCutoff = 0.1; +uniform bool imageNearest; +uniform bool imagePremult; + +vec3 workbench_image_color(vec2 uvs) +{ +#ifdef V3D_SHADING_TEXTURE_COLOR +# ifdef TEXTURE_IMAGE_ARRAY + vec4 color = workbench_sample_texture_array(imageTileArray, imageTileData, uvs, imageNearest); +# else + vec4 color = workbench_sample_texture(imageTexture, uvs, imageNearest); +# endif + + /* Unpremultiply if stored multiplied, since straight alpha is expected by shaders. */ + if (imagePremult && !(color.a == 0.0 || color.a == 1.0)) { + color.rgb /= color.a; + } + +# ifdef GPU_FRAGMENT_SHADER + if (color.a < imageTransparencyCutoff) { + discard; + } +# endif + + return color.rgb; +#else + return vec3(1.0); +#endif +} diff --git a/source/blender/draw/engines/workbench/shaders/workbench_matcap_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_matcap_lib.glsl new file mode 100644 index 00000000000..2d18cc1b014 --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/workbench_matcap_lib.glsl @@ -0,0 +1,30 @@ + +#pragma BLENDER_REQUIRE(workbench_data_lib.glsl) + +vec2 matcap_uv_compute(vec3 I, vec3 N, bool flipped) +{ + /* Quick creation of an orthonormal basis */ + float a = 1.0 / (1.0 + I.z); + float b = -I.x * I.y * a; + vec3 b1 = vec3(1.0 - I.x * I.x * a, b, -I.x); + vec3 b2 = vec3(b, 1.0 - I.y * I.y * a, -I.y); + vec2 matcap_uv = vec2(dot(b1, N), dot(b2, N)); + if (flipped) { + matcap_uv.x = -matcap_uv.x; + } + return matcap_uv * 0.496 + 0.5; +} + +uniform sampler2D matcapDiffuseImage; +uniform sampler2D matcapSpecularImage; + +vec3 get_matcap_lighting(vec3 base_color, vec3 N, vec3 I) +{ + bool flipped = world_data.matcap_orientation != 0; + vec2 uv = matcap_uv_compute(I, N, flipped); + + vec3 diffuse = textureLod(matcapDiffuseImage, uv, 0.0).rgb; + vec3 specular = textureLod(matcapSpecularImage, uv, 0.0).rgb; + + return diffuse * base_color + specular * float(world_data.use_specular); +} diff --git a/source/blender/draw/engines/workbench/shaders/workbench_material_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_material_lib.glsl new file mode 100644 index 00000000000..1d8950e34b3 --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/workbench_material_lib.glsl @@ -0,0 +1,21 @@ + +layout(std140) uniform material_block +{ + vec4 mat_data[4096]; +}; + +/* If set to -1, the resource handle is used instead. */ +uniform int materialIndex; + +void workbench_material_data_get( + int handle, out vec3 color, out float alpha, out float roughness, out float metallic) +{ + handle = (materialIndex != -1) ? materialIndex : handle; + vec4 data = mat_data[uint(handle) & 0xFFFu]; + color = data.rgb; + + uint encoded_data = floatBitsToUint(data.w); + alpha = float((encoded_data >> 16u) & 0xFFu) * (1.0 / 255.0); + roughness = float((encoded_data >> 8u) & 0xFFu) * (1.0 / 255.0); + metallic = float(encoded_data & 0xFFu) * (1.0 / 255.0); +} diff --git a/source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl new file mode 100644 index 00000000000..58becb03290 --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl @@ -0,0 +1,18 @@ + +uniform sampler2D depthBuffer; + +in vec4 uvcoordsvar; + +out vec4 fragColor; + +void main() +{ + float depth = texture(depthBuffer, uvcoordsvar.st).r; + /* Discard background pixels. */ + if (depth == 1.0) { + discard; + } + /* Make this fragment occlude any fragment that will try to + * render over it in the normal passes. */ + gl_FragDepth = 0.0; +} \ No newline at end of file diff --git a/source/blender/draw/engines/workbench/shaders/workbench_object_outline_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_object_outline_lib.glsl deleted file mode 100644 index a4a5d9c31a3..00000000000 --- a/source/blender/draw/engines/workbench/shaders/workbench_object_outline_lib.glsl +++ /dev/null @@ -1,12 +0,0 @@ -#define OBJECT_OUTLINE_OFFSET 1 - -float calculate_object_outline(usampler2D objectId, ivec2 texel, uint object_id) -{ - uvec4 oid_offset = uvec4( - texelFetchOffset(objectId, texel, 0, ivec2(0, OBJECT_OUTLINE_OFFSET)).r, - texelFetchOffset(objectId, texel, 0, ivec2(0, -OBJECT_OUTLINE_OFFSET)).r, - texelFetchOffset(objectId, texel, 0, ivec2(-OBJECT_OUTLINE_OFFSET, 0)).r, - texelFetchOffset(objectId, texel, 0, ivec2(OBJECT_OUTLINE_OFFSET, 0)).r); - - return dot(vec4(equal(uvec4(object_id), oid_offset)), vec4(0.25)); -} diff --git a/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl index 94e41b4bcd4..6d24b001d4d 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl @@ -1,92 +1,29 @@ -uniform vec4 materialColorAndMetal; -uniform float materialRoughness; +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_shader_interface_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_common_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_image_lib.glsl) -#ifdef TEXTURE_IMAGE_ARRAY -uniform sampler2DArray image_tile_array; -uniform sampler1DArray image_tile_data; -#else -uniform sampler2D image; -#endif -uniform float ImageTransparencyCutoff = 0.1; -uniform bool imageNearest; -uniform bool imagePremultiplied; - -#ifdef NORMAL_VIEWPORT_PASS_ENABLED -in vec3 normal_viewport; -#endif - -#ifdef V3D_SHADING_TEXTURE_COLOR -in vec2 uv_interp; -#endif -#ifdef V3D_SHADING_VERTEX_COLOR -in vec3 vertexColor; -#endif - -#ifdef HAIR_SHADER -flat in float hair_rand; -#endif - -#ifdef MATDATA_PASS_ENABLED layout(location = 0) out vec4 materialData; -#endif -#ifdef OBJECT_ID_PASS_ENABLED -layout(location = 1) out uint objectId; -#endif -#ifdef NORMAL_VIEWPORT_PASS_ENABLED -layout(location = 2) out WB_Normal normalViewport; -#endif +layout(location = 1) out WB_Normal normalData; +layout(location = 2) out uint objectId; + +uniform bool useMatcap = false; void main() { -#ifdef MATDATA_PASS_ENABLED - float metallic, roughness; - vec4 color; - -# if defined(V3D_SHADING_TEXTURE_COLOR) -# ifdef TEXTURE_IMAGE_ARRAY - color = workbench_sample_texture_array( - image_tile_array, image_tile_data, uv_interp, imageNearest, imagePremultiplied); -# else - color = workbench_sample_texture(image, uv_interp, imageNearest, imagePremultiplied); -# endif - if (color.a < ImageTransparencyCutoff) { - discard; - } -# elif defined(V3D_SHADING_VERTEX_COLOR) - color.rgb = vertexColor; -# else - color.rgb = materialColorAndMetal.rgb; -# endif - -# ifdef V3D_LIGHTING_MATCAP - /* Encode front facing in metallic channel. */ - metallic = float(gl_FrontFacing); - roughness = 0.0; -# else - metallic = materialColorAndMetal.a; - roughness = materialRoughness; -# endif + normalData = workbench_normal_encode(gl_FrontFacing, normal_interp); -# ifdef HAIR_SHADER - /* Add some variation to the hairs to avoid uniform look. */ - float hair_variation = hair_rand * 0.1; - color = clamp(color - hair_variation, 0.0, 1.0); - metallic = clamp(materialColorAndMetal.a - hair_variation, 0.0, 1.0); - roughness = clamp(materialRoughness - hair_variation, 0.0, 1.0); -# endif + materialData = vec4(color_interp, packed_rough_metal); - materialData.rgb = color.rgb; - materialData.a = workbench_float_pair_encode(roughness, metallic); -#endif /* MATDATA_PASS_ENABLED */ + objectId = uint(object_id); -#ifdef OBJECT_ID_PASS_ENABLED - objectId = uint(resource_id + 1) & 0xFFu; -#endif + if (useMatcap) { + /* For matcaps, save front facing in alpha channel. */ + materialData.a = float(gl_FrontFacing); + } -#ifdef NORMAL_VIEWPORT_PASS_ENABLED - vec3 n = (gl_FrontFacing) ? normal_viewport : -normal_viewport; - n = normalize(n); - normalViewport = workbench_normal_encode(n); +#ifdef V3D_SHADING_TEXTURE_COLOR + materialData.rgb = workbench_image_color(uv_interp); #endif } diff --git a/source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl new file mode 100644 index 00000000000..6a7bc185fe9 --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl @@ -0,0 +1,94 @@ +#pragma BLENDER_REQUIRE(common_hair_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_shader_interface_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_common_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_material_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_image_lib.glsl) + +uniform samplerBuffer ac; /* active color layer */ +uniform samplerBuffer au; /* active texture layer */ + +/* From http://libnoise.sourceforge.net/noisegen/index.html */ +float integer_noise(int n) +{ + n = (n >> 13) ^ n; + int nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff; + return (float(nn) / 1073741824.0); +} + +vec3 workbench_hair_random_normal(vec3 tan, vec3 binor, float rand) +{ + /* To "simulate" anisotropic shading, randomize hair normal per strand. */ + vec3 nor = cross(tan, binor); + nor = normalize(mix(nor, -tan, rand * 0.1)); + float cos_theta = (rand * 2.0 - 1.0) * 0.2; + float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta)); + nor = nor * sin_theta + binor * cos_theta; + return nor; +} + +void workbench_hair_random_material(float rand, + inout vec3 color, + inout float roughness, + inout float metallic) +{ + /* Center noise around 0. */ + rand -= 0.5; + rand *= 0.1; + /* Add some variation to the hairs to avoid uniform look. */ + metallic = clamp(metallic + rand, 0.0, 1.0); + roughness = clamp(roughness + rand, 0.0, 1.0); + /* Modulate by color intensity to reduce very high contrast when color is dark. */ + color = clamp(color + rand * (color + 0.05), 0.0, 1.0); +} + +void main() +{ + bool is_persp = (ProjectionMatrix[3][3] == 0.0); + float time, thick_time, thickness; + vec3 world_pos, tan, binor; + hair_get_pos_tan_binor_time(is_persp, + ModelMatrixInverse, + ViewMatrixInverse[3].xyz, + ViewMatrixInverse[2].xyz, + world_pos, + tan, + binor, + time, + thickness, + thick_time); + + gl_Position = point_world_to_ndc(world_pos); + + float hair_rand = integer_noise(hair_get_strand_id()); + vec3 nor = workbench_hair_random_normal(tan, binor, hair_rand); + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif + + uv_interp = hair_get_customdata_vec2(au); + + normal_interp = normalize(normal_world_to_view(nor)); + +#ifdef OPAQUE_MATERIAL + float metallic, roughness; +#endif + workbench_material_data_get(resource_handle, color_interp, alpha_interp, roughness, metallic); + + if (materialIndex == 0) { + color_interp = hair_get_customdata_vec3(ac); + } + + /* Hairs have lots of layer and can rapidly become the most prominent surface. + * So we lower their alpha artificially. */ + alpha_interp *= 0.3; + + workbench_hair_random_material(hair_rand, color_interp, roughness, metallic); + +#ifdef OPAQUE_MATERIAL + packed_rough_metal = workbench_float_pair_encode(roughness, metallic); +#endif + + object_id = int((uint(resource_id) + 1u) & 0xFFu); +} diff --git a/source/blender/draw/engines/workbench/shaders/workbench_prepass_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_prepass_vert.glsl index 0a3252f0b9b..31e298d1540 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_prepass_vert.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_prepass_vert.glsl @@ -1,110 +1,40 @@ -#ifndef HAIR_SHADER +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_shader_interface_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_common_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_material_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_image_lib.glsl) + in vec3 pos; in vec3 nor; -in vec2 au; /* active texture layer */ -# ifdef V3D_SHADING_VERTEX_COLOR in vec4 ac; /* active color */ -# endif -# define uv au -#else /* HAIR_SHADER */ - -# ifdef V3D_SHADING_TEXTURE_COLOR -uniform samplerBuffer au; /* active texture layer */ -# endif -# ifdef V3D_SHADING_VERTEX_COLOR -uniform samplerBuffer ac; /* active color layer */ -# endif - -flat out float hair_rand; -#endif /* HAIR_SHADER */ - -#ifdef NORMAL_VIEWPORT_PASS_ENABLED -out vec3 normal_viewport; -#endif - -#ifdef V3D_SHADING_TEXTURE_COLOR -out vec2 uv_interp; -#endif -#ifdef V3D_SHADING_VERTEX_COLOR -out vec3 vertexColor; -#endif - -#ifdef OBJECT_ID_PASS_ENABLED -RESOURCE_ID_VARYING -#endif - -/* From http://libnoise.sourceforge.net/noisegen/index.html */ -float integer_noise(int n) -{ - n = (n >> 13) ^ n; - int nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff; - return (float(nn) / 1073741824.0); -} - -vec3 workbench_hair_hair_normal(vec3 tan, vec3 binor, float rand) -{ - /* To "simulate" anisotropic shading, randomize hair normal per strand. */ - vec3 nor = cross(tan, binor); - nor = normalize(mix(nor, -tan, rand * 0.1)); - float cos_theta = (rand * 2.0 - 1.0) * 0.2; - float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta)); - nor = nor * sin_theta + binor * cos_theta; - return nor; -} +in vec2 au; /* active texture layer */ void main() { -#ifdef HAIR_SHADER -# ifdef V3D_SHADING_TEXTURE_COLOR - vec2 uv = hair_get_customdata_vec2(au); -# endif - float time, thick_time, thickness; - vec3 world_pos, tan, binor; - hair_get_pos_tan_binor_time((ProjectionMatrix[3][3] == 0.0), - ModelMatrixInverse, - ViewMatrixInverse[3].xyz, - ViewMatrixInverse[2].xyz, - world_pos, - tan, - binor, - time, - thickness, - thick_time); - - hair_rand = integer_noise(hair_get_strand_id()); - vec3 nor = workbench_hair_hair_normal(tan, binor, hair_rand); -#else vec3 world_pos = point_object_to_world(pos); -#endif gl_Position = point_world_to_ndc(world_pos); -#ifdef V3D_SHADING_TEXTURE_COLOR - uv_interp = uv; +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); #endif -#ifdef V3D_SHADING_VERTEX_COLOR -# ifndef HAIR_SHADER - vertexColor = ac.rgb; -# else - vertexColor = hair_get_customdata_vec4(ac).rgb; -# endif -#endif + uv_interp = au; -#ifdef NORMAL_VIEWPORT_PASS_ENABLED -# ifndef HAIR_SHADER - normal_viewport = normal_object_to_view(nor); - normal_viewport = normalize(normal_viewport); -# else - normal_viewport = normal_world_to_view(nor); -# endif -#endif + normal_interp = normalize(normal_object_to_view(nor)); -#ifdef OBJECT_ID_PASS_ENABLED - PASS_RESOURCE_ID +#ifdef OPAQUE_MATERIAL + float metallic, roughness; #endif + workbench_material_data_get(resource_handle, color_interp, alpha_interp, roughness, metallic); -#ifdef USE_WORLD_CLIP_PLANES - world_clip_planes_calc_clip_distance(world_pos); + if (materialIndex == 0) { + color_interp = ac.rgb; + } + +#ifdef OPAQUE_MATERIAL + packed_rough_metal = workbench_float_pair_encode(roughness, metallic); #endif + + object_id = int((uint(resource_id) + 1u) & 0xFFu); } diff --git a/source/blender/draw/engines/workbench/shaders/workbench_shader_interface_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_shader_interface_lib.glsl new file mode 100644 index 00000000000..8e2f7ba4735 --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/workbench_shader_interface_lib.glsl @@ -0,0 +1,21 @@ + +#ifdef GPU_VERTEX_SHADER +# define IN_OUT out +#else +# define IN_OUT in +#endif + +IN_OUT ShaderStageInterface +{ + vec3 normal_interp; + vec3 color_interp; + float alpha_interp; + vec2 uv_interp; +#ifdef TRANSPARENT_MATERIAL + flat float roughness; + flat float metallic; +#else + flat float packed_rough_metal; +#endif + flat int object_id; +}; diff --git a/source/blender/draw/engines/workbench/shaders/workbench_shadow_debug_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_shadow_debug_frag.glsl index 6b0741b6d1b..6fa76510e6e 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_shadow_debug_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_shadow_debug_frag.glsl @@ -1,15 +1,19 @@ out vec4 fragColor; +layout(location = 0) out vec4 materialData; +layout(location = 1) out vec4 normalData; +layout(location = 2) out uint objectId; + void main() { - const float intensity = 0.25; + const float a = 0.25; #ifdef SHADOW_PASS - fragColor = vec4( - (gl_FrontFacing) ? vec3(intensity, -intensity, 0.0) : vec3(-intensity, intensity, 0.0), 1.0); + materialData.rgb = gl_FrontFacing ? vec3(a, -a, 0.0) : vec3(-a, a, 0.0); #else - fragColor = vec4((gl_FrontFacing) ? vec3(intensity, intensity, -intensity) : - vec3(-intensity, -intensity, intensity), - 1.0); + materialData.rgb = gl_FrontFacing ? vec3(a, a, -a) : vec3(-a, -a, a); #endif + materialData.a = 0.0; + normalData = vec4(0.0); + objectId = 0u; } diff --git a/source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl new file mode 100644 index 00000000000..3c2d1a9c0c7 --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/workbench_transparent_accum_frag.glsl @@ -0,0 +1,89 @@ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_shader_interface_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_common_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_image_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_matcap_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_world_light_lib.glsl) + +/* Revealage is actually stored in transparentAccum alpha channel. + * This is a workaround to older hardware not having separate blend equation per render target. */ +layout(location = 0) out vec4 transparentAccum; +layout(location = 1) out vec4 revealageAccum; + +/* Note: Blending will be skipped on objectId because output is a non-normalized integer buffer. */ +layout(location = 2) out uint objectId; + +/* Special function only to be used with calculate_transparent_weight(). */ +float linear_zdepth(float depth, vec4 viewvecs[3], mat4 proj_mat) +{ + if (proj_mat[3][3] == 0.0) { + float d = 2.0 * depth - 1.0; + return -proj_mat[3][2] / (d + proj_mat[2][2]); + } + else { + /* Return depth from near plane. */ + return depth * viewvecs[1].z; + } +} + +/* Based on : + * McGuire and Bavoil, Weighted Blended Order-Independent Transparency, Journal of + * Computer Graphics Techniques (JCGT), vol. 2, no. 2, 122–141, 2013 + */ +float calculate_transparent_weight(void) +{ + float z = linear_zdepth(gl_FragCoord.z, world_data.viewvecs, ProjectionMatrix); +#if 0 + /* Eq 10 : Good for surfaces with varying opacity (like particles) */ + float a = min(1.0, alpha * 10.0) + 0.01; + float b = -gl_FragCoord.z * 0.95 + 1.0; + float w = a * a * a * 3e2 * b * b * b; +#else + /* Eq 7 put more emphasis on surfaces closer to the view. */ + // float w = 10.0 / (1e-5 + pow(abs(z) / 5.0, 2.0) + pow(abs(z) / 200.0, 6.0)); /* Eq 7 */ + // float w = 10.0 / (1e-5 + pow(abs(z) / 10.0, 3.0) + pow(abs(z) / 200.0, 6.0)); /* Eq 8 */ + // float w = 10.0 / (1e-5 + pow(abs(z) / 200.0, 4.0)); /* Eq 9 */ + /* Same as eq 7, but optimized. */ + float a = abs(z) / 5.0; + float b = abs(z) / 200.0; + b *= b; + float w = 10.0 / ((1e-5 + a * a) + b * (b * b)); /* Eq 7 */ +#endif + return clamp(w, 1e-2, 3e2); +} + +void main() +{ + /* Normal and Incident vector are in viewspace. Lighting is evaluated in viewspace. */ + vec2 uv_viewport = gl_FragCoord.xy * world_data.viewport_size_inv; + vec3 I = view_vector_from_screen_uv(uv_viewport, world_data.viewvecs, ProjectionMatrix); + vec3 N = normalize(normal_interp); + + vec3 color = color_interp; + +#ifdef V3D_SHADING_TEXTURE_COLOR + color = workbench_image_color(uv_interp); +#endif + +#ifdef V3D_LIGHTING_MATCAP + vec3 shaded_color = get_matcap_lighting(color, N, I); +#endif + +#ifdef V3D_LIGHTING_STUDIO + vec3 shaded_color = get_world_lighting(color, roughness, metallic, N, I); +#endif + +#ifdef V3D_LIGHTING_FLAT + vec3 shaded_color = color; +#endif + + shaded_color *= get_shadow(N); + + /* Listing 4 */ + float weight = calculate_transparent_weight() * alpha_interp; + transparentAccum = vec4(shaded_color * weight, alpha_interp); + revealageAccum = vec4(weight); + + objectId = uint(object_id); +} diff --git a/source/blender/draw/engines/workbench/shaders/workbench_transparent_resolve_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_transparent_resolve_frag.glsl new file mode 100644 index 00000000000..d985737a35b --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/workbench_transparent_resolve_frag.glsl @@ -0,0 +1,26 @@ + +uniform sampler2D transparentAccum; +uniform sampler2D transparentRevealage; + +in vec4 uvcoordsvar; + +out vec4 fragColor; + +/* Based on : + * McGuire and Bavoil, Weighted Blended Order-Independent Transparency, Journal of + * Computer Graphics Techniques (JCGT), vol. 2, no. 2, 122–141, 2013 + */ + +void main() +{ + /* Revealage is actually stored in transparentAccum alpha channel. + * This is a workaround to older hardware not having separate blend equation per render target. + */ + vec4 trans_accum = texture(transparentAccum, uvcoordsvar.st); + float trans_weight = texture(transparentRevealage, uvcoordsvar.st).r; + float trans_reveal = trans_accum.a; + + /* Listing 4 */ + fragColor.rgb = trans_accum.rgb / clamp(trans_weight, 1e-4, 5e4); + fragColor.a = 1.0 - trans_reveal; +} diff --git a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl index 585e48ae7ec..e957f8bbe9c 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl @@ -1,4 +1,9 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_obinfos_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_data_lib.glsl) +#pragma BLENDER_REQUIRE(workbench_common_lib.glsl) + uniform sampler2D depthBuffer; uniform sampler3D densityTexture; @@ -8,10 +13,9 @@ uniform sampler1D flameColorTexture; uniform sampler1D transferTexture; uniform int samplesLen = 256; -uniform float noiseOfs = 0.0f; +uniform float noiseOfs = 0.0; uniform float stepLength; /* Step length in local space. */ uniform float densityScale; /* Simple Opacity multiplicator. */ -uniform vec4 viewvecs[3]; uniform vec3 activeColor; uniform float slicePosition; @@ -23,34 +27,11 @@ in vec3 localPos; out vec4 fragColor; -#define M_PI 3.1415926535897932 /* pi */ - float phase_function_isotropic() { return 1.0 / (4.0 * M_PI); } -float get_view_z_from_depth(float depth) -{ - if (ProjectionMatrix[3][3] == 0.0) { - float d = 2.0 * depth - 1.0; - return -ProjectionMatrix[3][2] / (d + ProjectionMatrix[2][2]); - } - else { - return viewvecs[0].z + depth * viewvecs[1].z; - } -} - -vec3 get_view_space_from_depth(vec2 uvcoords, float depth) -{ - if (ProjectionMatrix[3][3] == 0.0) { - return vec3(viewvecs[0].xy + uvcoords * viewvecs[1].xy, 1.0) * get_view_z_from_depth(depth); - } - else { - return viewvecs[0].xyz + vec3(uvcoords, depth) * viewvecs[1].xyz; - } -} - float max_v3(vec3 v) { return max(v.x, max(v.y, v.z)); @@ -209,8 +190,10 @@ void main() float depth = texelFetch(depthBuffer, ivec2(gl_FragCoord.xy), 0).r; float depth_end = min(depth, gl_FragCoord.z); - vec3 vs_ray_end = get_view_space_from_depth(screen_uv, depth_end); - vec3 vs_ray_ori = get_view_space_from_depth(screen_uv, 0.0); + vec3 vs_ray_end = view_position_from_depth( + screen_uv, depth_end, world_data.viewvecs, ProjectionMatrix); + vec3 vs_ray_ori = view_position_from_depth( + screen_uv, 0.0, world_data.viewvecs, ProjectionMatrix); vec3 vs_ray_dir = (is_persp) ? (vs_ray_end - vs_ray_ori) : vec3(0.0, 0.0, -1.0); vs_ray_dir /= abs(vs_ray_dir.z); diff --git a/source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl index 3542a1a91fc..1a32a202290 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl @@ -1,4 +1,7 @@ +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_obinfos_lib.glsl) + uniform float slicePosition; uniform int sliceAxis; /* -1 is no slice, 0 is X, 1 is Y, 2 is Z. */ diff --git a/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl index 690ce5d527f..81f6e651be0 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl @@ -1,4 +1,6 @@ +#pragma BLENDER_REQUIRE(workbench_data_lib.glsl) + /* [Drobot2014a] Low Level Optimizations for GCN */ vec4 fast_rcp(vec4 v) { @@ -41,9 +43,19 @@ vec4 wrapped_lighting(vec4 NL, vec4 w) return clamp((NL + w) * denom, 0.0, 1.0); } -vec3 get_world_lighting( - WorldData world_data, vec3 diffuse_color, vec3 specular_color, float roughness, vec3 N, vec3 I) +vec3 get_world_lighting(vec3 base_color, float roughness, float metallic, vec3 N, vec3 I) { + vec3 specular_color, diffuse_color; + + if (world_data.use_specular) { + diffuse_color = mix(base_color, vec3(0.0), metallic); + specular_color = mix(vec3(0.05), base_color, metallic); + } + else { + diffuse_color = base_color; + specular_color = vec3(0.0); + } + vec3 specular_light = world_data.ambient_color.rgb; vec3 diffuse_light = world_data.ambient_color.rgb; vec4 wrap = vec4(world_data.lights[0].diffuse_color_wrap.a, @@ -51,37 +63,37 @@ vec3 get_world_lighting( world_data.lights[2].diffuse_color_wrap.a, world_data.lights[3].diffuse_color_wrap.a); -#ifdef V3D_SHADING_SPECULAR_HIGHLIGHT - /* Prepare Specular computation. Eval 4 lights at once. */ - vec3 R = -reflect(I, N); - vec4 spec_angle, spec_NL, wrap_NL; - prep_specular(world_data.lights[0].direction.xyz, I, N, R, spec_NL.x, wrap_NL.x, spec_angle.x); - prep_specular(world_data.lights[1].direction.xyz, I, N, R, spec_NL.y, wrap_NL.y, spec_angle.y); - prep_specular(world_data.lights[2].direction.xyz, I, N, R, spec_NL.z, wrap_NL.z, spec_angle.z); - prep_specular(world_data.lights[3].direction.xyz, I, N, R, spec_NL.w, wrap_NL.w, spec_angle.w); - - vec4 gloss = vec4(1.0 - roughness); - /* Reduce gloss for smooth light. (simulate bigger light) */ - gloss *= 1.0 - wrap; - vec4 shininess = exp2(10.0 * gloss + 1.0); - - vec4 spec_light = blinn_specular(shininess, spec_angle, spec_NL); - - /* Simulate Env. light. */ - vec4 w = mix(wrap, vec4(1.0), roughness); - vec4 spec_env = wrapped_lighting(wrap_NL, w); - - spec_light = mix(spec_light, spec_env, wrap * wrap); - - /* Multiply result by lights specular colors. */ - specular_light += spec_light.x * world_data.lights[0].specular_color.rgb; - specular_light += spec_light.y * world_data.lights[1].specular_color.rgb; - specular_light += spec_light.z * world_data.lights[2].specular_color.rgb; - specular_light += spec_light.w * world_data.lights[3].specular_color.rgb; - - float NV = clamp(dot(N, I), 0.0, 1.0); - specular_color = brdf_approx(specular_color, roughness, NV); -#endif + if (world_data.use_specular) { + /* Prepare Specular computation. Eval 4 lights at once. */ + vec3 R = -reflect(I, N); + vec4 spec_angle, spec_NL, wrap_NL; + prep_specular(world_data.lights[0].direction.xyz, I, N, R, spec_NL.x, wrap_NL.x, spec_angle.x); + prep_specular(world_data.lights[1].direction.xyz, I, N, R, spec_NL.y, wrap_NL.y, spec_angle.y); + prep_specular(world_data.lights[2].direction.xyz, I, N, R, spec_NL.z, wrap_NL.z, spec_angle.z); + prep_specular(world_data.lights[3].direction.xyz, I, N, R, spec_NL.w, wrap_NL.w, spec_angle.w); + + vec4 gloss = vec4(1.0 - roughness); + /* Reduce gloss for smooth light. (simulate bigger light) */ + gloss *= 1.0 - wrap; + vec4 shininess = exp2(10.0 * gloss + 1.0); + + vec4 spec_light = blinn_specular(shininess, spec_angle, spec_NL); + + /* Simulate Env. light. */ + vec4 w = mix(wrap, vec4(1.0), roughness); + vec4 spec_env = wrapped_lighting(wrap_NL, w); + + spec_light = mix(spec_light, spec_env, wrap * wrap); + + /* Multiply result by lights specular colors. */ + specular_light += spec_light.x * world_data.lights[0].specular_color.rgb; + specular_light += spec_light.y * world_data.lights[1].specular_color.rgb; + specular_light += spec_light.z * world_data.lights[2].specular_color.rgb; + specular_light += spec_light.w * world_data.lights[3].specular_color.rgb; + + float NV = clamp(dot(N, I), 0.0, 1.0); + specular_color = brdf_approx(specular_color, roughness, NV); + } specular_light *= specular_color; /* Prepare diffuse computation. Eval 4 lights at once. */ @@ -107,3 +119,13 @@ vec3 get_world_lighting( return diffuse_light + specular_light; } + +uniform bool forceShadowing = false; + +float get_shadow(vec3 N) +{ + float light_factor = -dot(N, world_data.shadow_direction_vs.xyz); + float shadow_mix = smoothstep(world_data.shadow_shift, world_data.shadow_focus, light_factor); + shadow_mix *= forceShadowing ? 0.0 : world_data.shadow_mul; + return shadow_mix + world_data.shadow_add; +} \ No newline at end of file diff --git a/source/blender/draw/engines/workbench/solid_mode.c b/source/blender/draw/engines/workbench/solid_mode.c deleted file mode 100644 index fed7e230a86..00000000000 --- a/source/blender/draw/engines/workbench/solid_mode.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - * - * Simple engine for drawing color and/or depth. - * When we only need simple studio shaders. - */ - -#include "DRW_render.h" - -#include "GPU_shader.h" - -#include "RE_pipeline.h" - -#include "workbench_private.h" - -/* Functions */ - -static void workbench_solid_engine_init(void *vedata) -{ - WORKBENCH_Data *data = vedata; - workbench_deferred_engine_init(data); -} - -static void workbench_solid_cache_init(void *vedata) -{ - - WORKBENCH_Data *data = vedata; - workbench_deferred_cache_init(data); -} - -static void workbench_solid_cache_populate(void *vedata, Object *ob) -{ - WORKBENCH_Data *data = vedata; - workbench_deferred_solid_cache_populate(data, ob); -} - -static void workbench_solid_cache_finish(void *vedata) -{ - WORKBENCH_Data *data = vedata; - workbench_deferred_cache_finish(data); -} - -static void workbench_solid_draw_scene(void *vedata) -{ - WORKBENCH_Data *data = vedata; - const int num_samples = workbench_num_viewport_rendering_iterations(data); - - for (int sample = 0; sample < num_samples; sample++) { - workbench_deferred_draw_scene(data); - } - workbench_deferred_draw_finish(data); -} - -static void workbench_solid_engine_free(void) -{ - workbench_deferred_engine_free(); -} - -static void workbench_solid_view_update(void *vedata) -{ - WORKBENCH_Data *data = vedata; - workbench_taa_view_updated(data); -} - -static void workbench_solid_id_update(void *UNUSED(vedata), struct ID *id) -{ - if (GS(id->name) == ID_OB) { - WORKBENCH_ObjectData *oed = (WORKBENCH_ObjectData *)DRW_drawdata_get( - id, &draw_engine_workbench_solid); - if (oed != NULL && oed->dd.recalc != 0) { - oed->shadow_bbox_dirty = (oed->dd.recalc & ID_RECALC_ALL) != 0; - oed->dd.recalc = 0; - } - } -} - -static void workbench_render_to_image(void *vedata, - RenderEngine *engine, - RenderLayer *render_layer, - const rcti *rect) -{ - workbench_render(vedata, engine, render_layer, rect); -} - -static const DrawEngineDataSize workbench_data_size = DRW_VIEWPORT_DATA_SIZE(WORKBENCH_Data); - -DrawEngineType draw_engine_workbench_solid = { - NULL, - NULL, - N_("Workbench"), - &workbench_data_size, - &workbench_solid_engine_init, - &workbench_solid_engine_free, - &workbench_solid_cache_init, - &workbench_solid_cache_populate, - &workbench_solid_cache_finish, - &workbench_solid_draw_scene, - &workbench_solid_view_update, - &workbench_solid_id_update, - &workbench_render_to_image, -}; diff --git a/source/blender/draw/engines/workbench/transparent_mode.c b/source/blender/draw/engines/workbench/transparent_mode.c deleted file mode 100644 index fef1ffded8d..00000000000 --- a/source/blender/draw/engines/workbench/transparent_mode.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - * - * Simple engine for drawing color and/or depth. - * When we only need simple studio shaders. - */ - -#include "DRW_render.h" - -#include "GPU_shader.h" - -#include "workbench_private.h" - -/* Functions */ - -static void workbench_transparent_engine_init(void *vedata) -{ - WORKBENCH_Data *data = vedata; - workbench_forward_engine_init(data); -} - -static void workbench_transparent_cache_init(void *vedata) -{ - - WORKBENCH_Data *data = vedata; - workbench_forward_cache_init(data); -} - -static void workbench_transparent_cache_populate(void *vedata, Object *ob) -{ - WORKBENCH_Data *data = vedata; - workbench_forward_cache_populate(data, ob); -} - -static void workbench_transparent_cache_finish(void *vedata) -{ - WORKBENCH_Data *data = vedata; - workbench_forward_cache_finish(data); -} - -static void workbench_transparent_draw_scene(void *vedata) -{ - WORKBENCH_Data *data = vedata; - const int num_samples = workbench_num_viewport_rendering_iterations(data); - - for (int sample = 0; sample < num_samples; sample++) { - workbench_forward_draw_scene(data); - } - workbench_forward_draw_finish(data); -} - -static void workbench_transparent_engine_free(void) -{ - workbench_forward_engine_free(); -} - -static void workbench_transparent_view_update(void *vedata) -{ - WORKBENCH_Data *data = vedata; - workbench_taa_view_updated(data); -} - -static const DrawEngineDataSize workbench_data_size = DRW_VIEWPORT_DATA_SIZE(WORKBENCH_Data); - -DrawEngineType draw_engine_workbench_transparent = { - NULL, - NULL, - N_("Workbench"), - &workbench_data_size, - &workbench_transparent_engine_init, - &workbench_transparent_engine_free, - &workbench_transparent_cache_init, - &workbench_transparent_cache_populate, - &workbench_transparent_cache_finish, - &workbench_transparent_draw_scene, - &workbench_transparent_view_update, - NULL, - NULL, -}; diff --git a/source/blender/draw/engines/workbench/workbench_data.c b/source/blender/draw/engines/workbench/workbench_data.c index 623a3c0cb15..0ab67d620ee 100644 --- a/source/blender/draw/engines/workbench/workbench_data.c +++ b/source/blender/draw/engines/workbench/workbench_data.c @@ -22,133 +22,231 @@ #include "workbench_private.h" +#include "BLI_memblock.h" + #include "DNA_userdef_types.h" #include "ED_view3d.h" +#include "ED_screen.h" #include "UI_resources.h" -#include "GPU_batch.h" +#include "GPU_uniformbuffer.h" /* -------------------------------------------------------------------- */ /** \name World Data * \{ */ -static void workbench_world_data_free(DrawData *dd) +GPUUniformBuffer *workbench_material_ubo_alloc(WORKBENCH_PrivateData *wpd) { - WORKBENCH_WorldData *data = (WORKBENCH_WorldData *)dd; - DRW_UBO_FREE_SAFE(data->world_ubo); + struct GPUUniformBuffer **ubo = BLI_memblock_alloc(wpd->material_ubo); + if (*ubo == NULL) { + *ubo = GPU_uniformbuffer_create(sizeof(WORKBENCH_UBO_Material) * MAX_MATERIAL, NULL, NULL); + } + return *ubo; } -/* Ensure the availability of the world_ubo in the given WORKBENCH_PrivateData - * - * See T70167: Some platforms create threads to upload ubo's. - * - * Reuses the last previous created `world_ubo`. Due to limitations of - * DrawData it will only be reused when there is a world attached to the Scene. - * Future development: The best location would be to store it in the View3D. - * - * We don't cache the data itself as there was no indication that that lead to - * an improvement. - * - * This functions also sets the `WORKBENCH_PrivateData.is_world_ubo_owner` that must - * be respected. - */ -static void workbench_world_data_ubo_ensure(const Scene *scene, WORKBENCH_PrivateData *wpd) +static void workbench_ubo_free(void *elem) { - World *world = scene->world; - if (world) { - WORKBENCH_WorldData *engine_world_data = (WORKBENCH_WorldData *)DRW_drawdata_ensure( - &world->id, - &draw_engine_workbench_solid, - sizeof(WORKBENCH_WorldData), - NULL, - &workbench_world_data_free); - - if (engine_world_data->world_ubo == NULL) { - engine_world_data->world_ubo = DRW_uniformbuffer_create(sizeof(WORKBENCH_UBO_World), - &wpd->world_data); - } - else { - DRW_uniformbuffer_update(engine_world_data->world_ubo, &wpd->world_data); - } - - /* Borrow world data ubo */ - wpd->is_world_ubo_owner = false; - wpd->world_ubo = engine_world_data->world_ubo; - } - else { - /* there is no world so we cannot cache the UBO. */ - BLI_assert(!wpd->world_ubo || wpd->is_world_ubo_owner); - if (!wpd->world_ubo) { - wpd->is_world_ubo_owner = true; - wpd->world_ubo = DRW_uniformbuffer_create(sizeof(WORKBENCH_UBO_World), &wpd->world_data); - } - } + GPUUniformBuffer **ubo = elem; + DRW_UBO_FREE_SAFE(*ubo); } -static void workbench_world_data_update_shadow_direction_vs(WORKBENCH_PrivateData *wpd) +static void workbench_view_layer_data_free(void *storage) { - WORKBENCH_UBO_World *wd = &wpd->world_data; - float light_direction[3]; - float view_matrix[4][4]; - DRW_view_viewmat_get(NULL, view_matrix, false); + WORKBENCH_ViewLayerData *vldata = (WORKBENCH_ViewLayerData *)storage; + + DRW_UBO_FREE_SAFE(vldata->dof_sample_ubo); + DRW_UBO_FREE_SAFE(vldata->world_ubo); + DRW_UBO_FREE_SAFE(vldata->cavity_sample_ubo); + DRW_TEXTURE_FREE_SAFE(vldata->cavity_jitter_tx); + + BLI_memblock_destroy(vldata->material_ubo_data, NULL); + BLI_memblock_destroy(vldata->material_ubo, workbench_ubo_free); +} - workbench_private_data_get_light_direction(light_direction); +static WORKBENCH_ViewLayerData *workbench_view_layer_data_ensure_ex(struct ViewLayer *view_layer) +{ + WORKBENCH_ViewLayerData **vldata = (WORKBENCH_ViewLayerData **) + DRW_view_layer_engine_data_ensure_ex(view_layer, + (DrawEngineType *)&workbench_view_layer_data_ensure_ex, + &workbench_view_layer_data_free); + + if (*vldata == NULL) { + *vldata = MEM_callocN(sizeof(**vldata), "WORKBENCH_ViewLayerData"); + size_t matbuf_size = sizeof(WORKBENCH_UBO_Material) * MAX_MATERIAL; + (*vldata)->material_ubo_data = BLI_memblock_create_ex(matbuf_size, matbuf_size * 2); + (*vldata)->material_ubo = BLI_memblock_create_ex(sizeof(void *), sizeof(void *) * 8); + (*vldata)->world_ubo = DRW_uniformbuffer_create(sizeof(WORKBENCH_UBO_World), NULL); + } - /* Shadow direction. */ - mul_v3_mat3_m4v3(wd->shadow_direction_vs, view_matrix, light_direction); + return *vldata; } /* \} */ -void workbench_clear_color_get(float color[4]) +static void workbench_viewvecs_update(float r_viewvecs[3][4]) { - const DRWContextState *draw_ctx = DRW_context_state_get(); - const Scene *scene = draw_ctx->scene; + float invproj[4][4]; + const bool is_persp = DRW_view_is_persp_get(NULL); + DRW_view_winmat_get(NULL, invproj, true); + + /* view vectors for the corners of the view frustum. + * Can be used to recreate the world space position easily */ + copy_v4_fl4(r_viewvecs[0], -1.0f, -1.0f, -1.0f, 1.0f); + copy_v4_fl4(r_viewvecs[1], 1.0f, -1.0f, -1.0f, 1.0f); + copy_v4_fl4(r_viewvecs[2], -1.0f, 1.0f, -1.0f, 1.0f); + + /* convert the view vectors to view space */ + for (int i = 0; i < 3; i++) { + mul_m4_v4(invproj, r_viewvecs[i]); + /* normalized trick see: + * http://www.derschmale.com/2014/01/26/reconstructing-positions-from-the-depth-buffer */ + mul_v3_fl(r_viewvecs[i], 1.0f / r_viewvecs[i][3]); + if (is_persp) { + mul_v3_fl(r_viewvecs[i], 1.0f / r_viewvecs[i][2]); + } + r_viewvecs[i][3] = 1.0; + } + + /* we need to store the differences */ + r_viewvecs[1][0] -= r_viewvecs[0][0]; + r_viewvecs[1][1] = r_viewvecs[2][1] - r_viewvecs[0][1]; - if (!DRW_state_is_scene_render() || !DRW_state_draw_background()) { - zero_v4(color); + /* calculate a depth offset as well */ + if (!is_persp) { + float vec_far[] = {-1.0f, -1.0f, 1.0f, 1.0f}; + mul_m4_v4(invproj, vec_far); + mul_v3_fl(vec_far, 1.0f / vec_far[3]); + r_viewvecs[1][2] = vec_far[2] - r_viewvecs[0][2]; } - else if (scene->world) { - copy_v3_v3(color, &scene->world->horr); - color[3] = 1.0f; +} + +static void workbench_studiolight_data_update(WORKBENCH_PrivateData *wpd, WORKBENCH_UBO_World *wd) +{ + StudioLight *studiolight = wpd->studio_light; + float view_matrix[4][4], rot_matrix[4][4]; + DRW_view_viewmat_get(NULL, view_matrix, false); + + if (USE_WORLD_ORIENTATION(wpd)) { + axis_angle_to_mat4_single(rot_matrix, 'Z', -wpd->shading.studiolight_rot_z); + mul_m4_m4m4(rot_matrix, view_matrix, rot_matrix); + swap_v3_v3(rot_matrix[2], rot_matrix[1]); + negate_v3(rot_matrix[2]); } else { - zero_v3(color); - color[3] = 1.0f; + unit_m4(rot_matrix); } -} -void workbench_effect_info_init(WORKBENCH_EffectInfo *effect_info) -{ - effect_info->jitter_index = 0; - effect_info->view_updated = true; + if (U.edit_studio_light) { + studiolight = BKE_studiolight_studio_edit_get(); + } + + /* Studio Lights. */ + for (int i = 0; i < 4; i++) { + WORKBENCH_UBO_Light *light = &wd->lights[i]; + + SolidLight *sl = (studiolight) ? &studiolight->light[i] : NULL; + if (sl && sl->flag) { + copy_v3_v3(light->light_direction, sl->vec); + mul_mat3_m4_v3(rot_matrix, light->light_direction); + /* We should predivide the power by PI but that makes the lights really dim. */ + copy_v3_v3(light->specular_color, sl->spec); + copy_v3_v3(light->diffuse_color, sl->col); + light->wrapped = sl->smooth; + } + else { + copy_v3_fl3(light->light_direction, 1.0f, 0.0f, 0.0f); + copy_v3_fl(light->specular_color, 0.0f); + copy_v3_fl(light->diffuse_color, 0.0f); + } + } + + if (studiolight) { + copy_v3_v3(wd->ambient_color, studiolight->light_ambient); + } + else { + copy_v3_fl(wd->ambient_color, 1.0f); + } + + wd->use_specular = workbench_is_specular_highlight_enabled(wpd); } void workbench_private_data_init(WORKBENCH_PrivateData *wpd) { const DRWContextState *draw_ctx = DRW_context_state_get(); - const Scene *scene = draw_ctx->scene; - wpd->material_hash = BLI_ghash_ptr_new(__func__); - wpd->material_transp_hash = BLI_ghash_ptr_new(__func__); + RegionView3D *rv3d = draw_ctx->rv3d; + View3D *v3d = draw_ctx->v3d; + Scene *scene = draw_ctx->scene; + WORKBENCH_ViewLayerData *vldata = workbench_view_layer_data_ensure_ex(draw_ctx->view_layer); + + wpd->is_playback = DRW_state_is_playback(); + wpd->is_navigating = rv3d && (rv3d->rflag & (RV3D_NAVIGATING | RV3D_PAINTING)); + + wpd->ctx_mode = CTX_data_mode_enum_ex( + draw_ctx->object_edit, draw_ctx->obact, draw_ctx->object_mode); + wpd->preferences = &U; + wpd->scene = scene; + wpd->sh_cfg = draw_ctx->sh_cfg; + wpd->clip_state = RV3D_CLIPPING_ENABLED(v3d, rv3d) ? DRW_STATE_CLIP_PLANES : 0; + wpd->cull_state = CULL_BACKFACE_ENABLED(wpd) ? DRW_STATE_CULL_BACK : 0; + wpd->vldata = vldata; + wpd->world_ubo = vldata->world_ubo; - View3D *v3d = draw_ctx->v3d; - RegionView3D *rv3d = draw_ctx->rv3d; + wpd->taa_sample_len = workbench_antialiasing_sample_count_get(wpd); + + wpd->volumes_do = false; + BLI_listbase_clear(&wpd->smoke_domains); if (!v3d || (v3d->shading.type == OB_RENDER && BKE_scene_uses_blender_workbench(scene))) { + /* FIXME: This reproduce old behavior when workbench was separated in 2 engines. + * But this is a workaround for a missing update tagging from operators. */ + if (scene->display.shading.type != wpd->shading.type || + XRAY_ENABLED(v3d) != XRAY_ENABLED((&scene->display))) { + wpd->view_updated = true; + } + wpd->shading = scene->display.shading; - wpd->shading.xray_alpha = XRAY_ALPHA((&scene->display)); - wpd->use_color_render_settings = true; + if (XRAY_FLAG_ENABLED((&scene->display))) { + wpd->shading.xray_alpha = XRAY_ALPHA((&scene->display)); + } + else { + wpd->shading.xray_alpha = 1.0f; + } + + if (scene->r.alphamode == R_ALPHAPREMUL) { + copy_v4_fl(wpd->background_color, 0.0f); + } + else if (scene->world) { + World *wo = scene->world; + copy_v4_fl4(wpd->background_color, wo->horr, wo->horg, wo->horb, 1.0f); + } + else { + copy_v4_fl4(wpd->background_color, 0.0f, 0.0f, 0.0f, 1.0f); + } } else { + /* FIXME: This reproduce old behavior when workbench was separated in 2 engines. + * But this is a workaround for a missing update tagging from operators. */ + if (v3d->shading.type != wpd->shading.type || XRAY_ENABLED(v3d) != XRAY_ENABLED(wpd)) { + wpd->view_updated = true; + } + wpd->shading = v3d->shading; - wpd->shading.xray_alpha = XRAY_ALPHA(v3d); - wpd->use_color_render_settings = false; - } + if (wpd->shading.type < OB_SOLID) { + wpd->shading.xray_alpha = 0.0f; + } + else if (XRAY_ENABLED(v3d)) { + wpd->shading.xray_alpha = XRAY_ALPHA(v3d); + } + else { + wpd->shading.xray_alpha = 1.0f; + } - wpd->use_color_management = BKE_scene_check_color_management_enabled(scene); + /* No background. The overlays will draw the correct one. */ + copy_v4_fl(wpd->background_color, 0.0f); + } if (wpd->shading.light == V3D_LIGHTING_MATCAP) { wpd->studio_light = BKE_studiolight_find(wpd->shading.matcap, STUDIOLIGHT_TYPE_MATCAP); @@ -162,119 +260,56 @@ void workbench_private_data_init(WORKBENCH_PrivateData *wpd) wpd->studio_light = BKE_studiolight_find(wpd->shading.studio_light, STUDIOLIGHT_TYPE_STUDIO); } - float shadow_focus = scene->display.shadow_focus; - /* Clamp to avoid overshadowing and shading errors. */ - CLAMP(shadow_focus, 0.0001f, 0.99999f); - wpd->shadow_shift = scene->display.shadow_shift; - wpd->shadow_focus = 1.0f - shadow_focus * (1.0f - wpd->shadow_shift); - wpd->shadow_multiplier = 1.0 - wpd->shading.shadow_intensity; - - WORKBENCH_UBO_World *wd = &wpd->world_data; - wd->matcap_orientation = (wpd->shading.flag & V3D_SHADING_MATCAP_FLIP_X) != 0; - - studiolight_update_world(wpd, wpd->studio_light, wd); - - copy_v3_v3(wd->object_outline_color, wpd->shading.object_outline_color); - wd->object_outline_color[3] = 1.0f; - - wd->curvature_ridge = 0.5f / max_ff(square_f(wpd->shading.curvature_ridge_factor), 1e-4f); - wd->curvature_valley = 0.7f / max_ff(square_f(wpd->shading.curvature_valley_factor), 1e-4f); - - /* Will be NULL when rendering. */ - if (RV3D_CLIPPING_ENABLED(v3d, rv3d)) { - wpd->world_clip_planes = rv3d->clip; - } - else { - wpd->world_clip_planes = NULL; - } - - workbench_world_data_update_shadow_direction_vs(wpd); - workbench_world_data_ubo_ensure(scene, wpd); - - /* Cavity settings */ { - const int ssao_samples = scene->display.matcap_ssao_samples; - - float invproj[4][4]; - const bool is_persp = DRW_view_is_persp_get(NULL); - /* view vectors for the corners of the view frustum. - * Can be used to recreate the world space position easily */ - float viewvecs[3][4] = { - {-1.0f, -1.0f, -1.0f, 1.0f}, - {1.0f, -1.0f, -1.0f, 1.0f}, - {-1.0f, 1.0f, -1.0f, 1.0f}, - }; - int i; - const float *size = DRW_viewport_size_get(); - - wpd->ssao_params[0] = ssao_samples; - wpd->ssao_params[1] = size[0] / 64.0; - wpd->ssao_params[2] = size[1] / 64.0; - wpd->ssao_params[3] = 0; - - /* distance, factor, factor, attenuation */ - copy_v4_fl4(wpd->ssao_settings, - scene->display.matcap_ssao_distance, - wpd->shading.cavity_valley_factor, - wpd->shading.cavity_ridge_factor, - scene->display.matcap_ssao_attenuation); - - DRW_view_winmat_get(NULL, wpd->winmat, false); - DRW_view_winmat_get(NULL, invproj, true); - - /* convert the view vectors to view space */ - for (i = 0; i < 3; i++) { - mul_m4_v4(invproj, viewvecs[i]); - /* normalized trick see: - * http://www.derschmale.com/2014/01/26/reconstructing-positions-from-the-depth-buffer */ - mul_v3_fl(viewvecs[i], 1.0f / viewvecs[i][3]); - if (is_persp) { - mul_v3_fl(viewvecs[i], 1.0f / viewvecs[i][2]); - } - viewvecs[i][3] = 1.0; - - copy_v4_v4(wpd->viewvecs[i], viewvecs[i]); - } - - /* we need to store the differences */ - wpd->viewvecs[1][0] -= wpd->viewvecs[0][0]; - wpd->viewvecs[1][1] = wpd->viewvecs[2][1] - wpd->viewvecs[0][1]; - - /* calculate a depth offset as well */ - if (!is_persp) { - float vec_far[] = {-1.0f, -1.0f, 1.0f, 1.0f}; - mul_m4_v4(invproj, vec_far); - mul_v3_fl(vec_far, 1.0f / vec_far[3]); - wpd->viewvecs[1][2] = vec_far[2] - wpd->viewvecs[0][2]; - } + /* Material UBOs. */ + wpd->material_ubo_data = vldata->material_ubo_data; + wpd->material_ubo = vldata->material_ubo; + wpd->material_chunk_count = 1; + wpd->material_chunk_curr = 0; + wpd->material_index = 1; + /* Create default material ubo. */ + wpd->material_ubo_data_curr = BLI_memblock_alloc(wpd->material_ubo_data); + wpd->material_ubo_curr = workbench_material_ubo_alloc(wpd); + /* Init default material used by vertex color & texture. */ + workbench_material_ubo_data( + wpd, NULL, NULL, &wpd->material_ubo_data_curr[0], V3D_SHADING_MATERIAL_COLOR); } - - wpd->volumes_do = false; - BLI_listbase_clear(&wpd->smoke_domains); } -void workbench_private_data_get_light_direction(float r_light_direction[3]) +void workbench_update_world_ubo(WORKBENCH_PrivateData *wpd) { - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; + WORKBENCH_UBO_World wd; - copy_v3_v3(r_light_direction, scene->display.light_direction); - SWAP(float, r_light_direction[2], r_light_direction[1]); - r_light_direction[2] = -r_light_direction[2]; - r_light_direction[0] = -r_light_direction[0]; + copy_v2_v2(wd.viewport_size, DRW_viewport_size_get()); + copy_v2_v2(wd.viewport_size_inv, DRW_viewport_invert_size_get()); + copy_v3_v3(wd.object_outline_color, wpd->shading.object_outline_color); + wd.object_outline_color[3] = 1.0f; + wd.ui_scale = G_draw.block.sizePixel; + wd.matcap_orientation = (wpd->shading.flag & V3D_SHADING_MATCAP_FLIP_X) != 0; + + workbench_studiolight_data_update(wpd, &wd); + workbench_shadow_data_update(wpd, &wd); + workbench_cavity_data_update(wpd, &wd); + workbench_viewvecs_update(wd.viewvecs); + + DRW_uniformbuffer_update(wpd->world_ubo, &wd); } -void workbench_private_data_free(WORKBENCH_PrivateData *wpd) +void workbench_update_material_ubos(WORKBENCH_PrivateData *UNUSED(wpd)) { - BLI_ghash_free(wpd->material_hash, NULL, MEM_freeN); - BLI_ghash_free(wpd->material_transp_hash, NULL, MEM_freeN); - - if (wpd->is_world_ubo_owner) { - DRW_UBO_FREE_SAFE(wpd->world_ubo); - } - else { - wpd->world_ubo = NULL; + const DRWContextState *draw_ctx = DRW_context_state_get(); + WORKBENCH_ViewLayerData *vldata = workbench_view_layer_data_ensure_ex(draw_ctx->view_layer); + + BLI_memblock_iter iter, iter_data; + BLI_memblock_iternew(vldata->material_ubo, &iter); + BLI_memblock_iternew(vldata->material_ubo_data, &iter_data); + WORKBENCH_UBO_Material *matchunk; + while ((matchunk = BLI_memblock_iterstep(&iter_data))) { + GPUUniformBuffer **ubo = BLI_memblock_iterstep(&iter); + BLI_assert(*ubo != NULL); + GPU_uniformbuffer_update(*ubo, matchunk); } - DRW_UBO_FREE_SAFE(wpd->dof_ubo); + BLI_memblock_clear(vldata->material_ubo, workbench_ubo_free); + BLI_memblock_clear(vldata->material_ubo_data, NULL); } diff --git a/source/blender/draw/engines/workbench/workbench_deferred.c b/source/blender/draw/engines/workbench/workbench_deferred.c deleted file mode 100644 index df9a597faf9..00000000000 --- a/source/blender/draw/engines/workbench/workbench_deferred.c +++ /dev/null @@ -1,1415 +0,0 @@ -/* - * 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. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - */ - -#include "workbench_private.h" - -#include "BLI_alloca.h" -#include "BLI_dynstr.h" -#include "BLI_utildefines.h" -#include "BLI_rand.h" -#include "BLI_string_utils.h" - -#include "BKE_modifier.h" -#include "BKE_object.h" -#include "BKE_paint.h" -#include "BKE_particle.h" - -#include "DNA_image_types.h" -#include "DNA_fluid_types.h" -#include "DNA_mesh_types.h" -#include "DNA_modifier_types.h" -#include "DNA_node_types.h" - -#include "GPU_shader.h" -#include "GPU_texture.h" -#include "GPU_extensions.h" - -#include "../eevee/eevee_lut.h" /* TODO find somewhere to share blue noise Table */ - -/* *********** STATIC *********** */ - -/* #define DEBUG_SHADOW_VOLUME */ - -#ifdef DEBUG_SHADOW_VOLUME -# include "draw_debug.h" -#endif - -typedef struct WORKBENCH_DEFERRED_Shaders { - struct GPUShader *prepass_sh_cache[MAX_PREPASS_SHADERS]; -} WORKBENCH_DEFERRED_Shaders; - -static struct { - WORKBENCH_DEFERRED_Shaders sh_data[GPU_SHADER_CFG_LEN]; - - struct GPUShader *composite_sh_cache[MAX_COMPOSITE_SHADERS]; - struct GPUShader *cavity_sh[MAX_CAVITY_SHADERS]; - struct GPUShader *background_sh[2]; - struct GPUShader *ghost_resolve_sh; - struct GPUShader *shadow_fail_sh; - struct GPUShader *shadow_fail_manifold_sh; - struct GPUShader *shadow_pass_sh; - struct GPUShader *shadow_pass_manifold_sh; - struct GPUShader *shadow_caps_sh; - struct GPUShader *shadow_caps_manifold_sh; - struct GPUShader *oit_resolve_sh; - - /* TODO(fclem) move everything below to wpd and custom viewlayer data. */ - struct GPUTexture *oit_accum_tx; /* ref only, not alloced */ - struct GPUTexture *oit_revealage_tx; /* ref only, not alloced */ - struct GPUTexture *object_id_tx; /* ref only, not alloced */ - struct GPUTexture *color_buffer_tx; /* ref only, not alloced */ - struct GPUTexture *cavity_buffer_tx; /* ref only, not alloced */ - struct GPUTexture *normal_buffer_tx; /* ref only, not alloced */ - struct GPUTexture *composite_buffer_tx; /* ref only, not alloced */ - - SceneDisplay display; /* world light direction for shadows */ - - struct GPUUniformBuffer *sampling_ubo; - struct GPUTexture *jitter_tx; - int cached_sample_num; -} e_data = {{{{NULL}}}}; - -/* Shaders */ -extern char datatoc_common_hair_lib_glsl[]; -extern char datatoc_common_view_lib_glsl[]; - -extern char datatoc_workbench_prepass_vert_glsl[]; -extern char datatoc_workbench_prepass_frag_glsl[]; -extern char datatoc_workbench_cavity_frag_glsl[]; -extern char datatoc_workbench_forward_composite_frag_glsl[]; -extern char datatoc_workbench_deferred_composite_frag_glsl[]; -extern char datatoc_workbench_deferred_background_frag_glsl[]; -extern char datatoc_workbench_ghost_resolve_frag_glsl[]; - -extern char datatoc_workbench_shadow_vert_glsl[]; -extern char datatoc_workbench_shadow_geom_glsl[]; -extern char datatoc_workbench_shadow_caps_geom_glsl[]; -extern char datatoc_workbench_shadow_debug_frag_glsl[]; - -extern char datatoc_workbench_cavity_lib_glsl[]; -extern char datatoc_workbench_common_lib_glsl[]; -extern char datatoc_workbench_data_lib_glsl[]; -extern char datatoc_workbench_object_outline_lib_glsl[]; -extern char datatoc_workbench_curvature_lib_glsl[]; -extern char datatoc_workbench_world_light_lib_glsl[]; - -extern char datatoc_gpu_shader_depth_only_frag_glsl[]; - -static char *workbench_build_composite_frag(WORKBENCH_PrivateData *wpd) -{ - DynStr *ds = BLI_dynstr_new(); - - BLI_dynstr_append(ds, datatoc_common_view_lib_glsl); - BLI_dynstr_append(ds, datatoc_workbench_data_lib_glsl); - BLI_dynstr_append(ds, datatoc_workbench_common_lib_glsl); - - if (!FLAT_ENABLED(wpd)) { - BLI_dynstr_append(ds, datatoc_workbench_world_light_lib_glsl); - } - if (OBJECT_OUTLINE_ENABLED(wpd)) { - BLI_dynstr_append(ds, datatoc_workbench_object_outline_lib_glsl); - } - if (CURVATURE_ENABLED(wpd)) { - BLI_dynstr_append(ds, datatoc_workbench_curvature_lib_glsl); - } - - BLI_dynstr_append(ds, datatoc_workbench_deferred_composite_frag_glsl); - - char *str = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - return str; -} - -static char *workbench_build_prepass_frag(void) -{ - DynStr *ds = BLI_dynstr_new(); - - BLI_dynstr_append(ds, datatoc_common_view_lib_glsl); - BLI_dynstr_append(ds, datatoc_workbench_data_lib_glsl); - BLI_dynstr_append(ds, datatoc_workbench_common_lib_glsl); - BLI_dynstr_append(ds, datatoc_workbench_prepass_frag_glsl); - - char *str = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - return str; -} - -static char *workbench_build_prepass_vert(bool is_hair) -{ - DynStr *ds = BLI_dynstr_new(); - if (is_hair) { - BLI_dynstr_append(ds, datatoc_common_hair_lib_glsl); - } - BLI_dynstr_append(ds, datatoc_common_view_lib_glsl); - BLI_dynstr_append(ds, datatoc_workbench_prepass_vert_glsl); - char *str = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - return str; -} - -static char *workbench_build_cavity_frag(bool cavity, bool curvature, bool high_dpi) -{ - DynStr *ds = BLI_dynstr_new(); - - if (cavity) { - BLI_dynstr_append(ds, "#define USE_CAVITY\n"); - } - if (curvature) { - BLI_dynstr_append(ds, "#define USE_CURVATURE\n"); - } - if (high_dpi) { - BLI_dynstr_append(ds, "#define CURVATURE_OFFSET 2\n"); - } - if (NORMAL_ENCODING_ENABLED()) { - BLI_dynstr_append(ds, "#define WORKBENCH_ENCODE_NORMALS\n"); - } - BLI_dynstr_append(ds, datatoc_workbench_common_lib_glsl); - BLI_dynstr_append(ds, datatoc_workbench_curvature_lib_glsl); - BLI_dynstr_append(ds, datatoc_workbench_cavity_frag_glsl); - BLI_dynstr_append(ds, datatoc_workbench_cavity_lib_glsl); - - char *str = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - return str; -} - -static GPUShader *workbench_cavity_shader_get(bool cavity, bool curvature) -{ - const bool high_dpi = (U.pixelsize > 1.5f); - int index = 0; - SET_FLAG_FROM_TEST(index, cavity, 1 << 0); - SET_FLAG_FROM_TEST(index, curvature, 1 << 1); - SET_FLAG_FROM_TEST(index, high_dpi, 1 << 2); - - GPUShader **sh = &e_data.cavity_sh[index]; - if (*sh == NULL) { - char *cavity_frag = workbench_build_cavity_frag(cavity, curvature, high_dpi); - *sh = DRW_shader_create_fullscreen(cavity_frag, NULL); - MEM_freeN(cavity_frag); - } - return *sh; -} - -static GPUShader *ensure_deferred_prepass_shader(WORKBENCH_PrivateData *wpd, - bool is_uniform_color, - bool is_hair, - bool is_tiled, - const WORKBENCH_ColorOverride color_override, - eGPUShaderConfig sh_cfg) -{ - WORKBENCH_DEFERRED_Shaders *sh_data = &e_data.sh_data[sh_cfg]; - int index = workbench_material_get_prepass_shader_index( - wpd, is_uniform_color, is_hair, is_tiled, color_override); - if (sh_data->prepass_sh_cache[index] == NULL) { - const GPUShaderConfigData *sh_cfg_data = &GPU_shader_cfg_data[sh_cfg]; - char *defines = workbench_material_build_defines( - wpd, is_uniform_color, is_hair, is_tiled, color_override); - char *prepass_vert = workbench_build_prepass_vert(is_hair); - char *prepass_frag = workbench_build_prepass_frag(); - sh_data->prepass_sh_cache[index] = GPU_shader_create_from_arrays({ - .vert = (const char *[]){sh_cfg_data->lib, prepass_vert, NULL}, - .frag = (const char *[]){prepass_frag, NULL}, - .defs = (const char *[]){sh_cfg_data->def, defines, NULL}, - }); - MEM_freeN(prepass_vert); - MEM_freeN(prepass_frag); - MEM_freeN(defines); - } - return sh_data->prepass_sh_cache[index]; -} - -static GPUShader *ensure_deferred_composite_shader(WORKBENCH_PrivateData *wpd) -{ - int index = workbench_material_get_composite_shader_index(wpd); - if (e_data.composite_sh_cache[index] == NULL) { - char *defines = workbench_material_build_defines( - wpd, false, false, false, WORKBENCH_COLOR_OVERRIDE_OFF); - char *composite_frag = workbench_build_composite_frag(wpd); - e_data.composite_sh_cache[index] = DRW_shader_create_fullscreen(composite_frag, defines); - MEM_freeN(composite_frag); - MEM_freeN(defines); - } - return e_data.composite_sh_cache[index]; -} - -static GPUShader *ensure_background_shader(WORKBENCH_PrivateData *wpd) -{ - const int index = OBJECT_OUTLINE_ENABLED(wpd) ? 1 : 0; - if (e_data.background_sh[index] == NULL) { - const char *defines = (index) ? "#define V3D_SHADING_OBJECT_OUTLINE\n" : NULL; - char *frag = BLI_string_joinN(datatoc_workbench_data_lib_glsl, - datatoc_workbench_common_lib_glsl, - datatoc_workbench_object_outline_lib_glsl, - datatoc_workbench_deferred_background_frag_glsl); - e_data.background_sh[index] = DRW_shader_create_fullscreen(frag, defines); - MEM_freeN(frag); - } - return e_data.background_sh[index]; -} - -static void select_deferred_shaders(WORKBENCH_PrivateData *wpd, eGPUShaderConfig sh_cfg) -{ - wpd->prepass_sh = ensure_deferred_prepass_shader( - wpd, false, false, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); - wpd->prepass_hair_sh = ensure_deferred_prepass_shader( - wpd, false, true, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); - wpd->prepass_uniform_sh = ensure_deferred_prepass_shader( - wpd, true, false, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); - wpd->prepass_uniform_hair_sh = ensure_deferred_prepass_shader( - wpd, true, true, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); - wpd->prepass_textured_sh = ensure_deferred_prepass_shader( - wpd, false, false, false, WORKBENCH_COLOR_OVERRIDE_TEXTURE, sh_cfg); - wpd->prepass_textured_array_sh = ensure_deferred_prepass_shader( - wpd, false, false, true, WORKBENCH_COLOR_OVERRIDE_TEXTURE, sh_cfg); - wpd->prepass_vertex_sh = ensure_deferred_prepass_shader( - wpd, false, false, false, WORKBENCH_COLOR_OVERRIDE_VERTEX, sh_cfg); - wpd->composite_sh = ensure_deferred_composite_shader(wpd); - wpd->background_sh = ensure_background_shader(wpd); -} - -/* Using Hammersley distribution */ -static float *create_disk_samples(int num_samples, int num_iterations) -{ - /* vec4 to ensure memory alignment. */ - const int total_samples = num_samples * num_iterations; - float(*texels)[4] = MEM_mallocN(sizeof(float[4]) * total_samples, __func__); - const float num_samples_inv = 1.0f / num_samples; - - for (int i = 0; i < total_samples; i++) { - float it_add = (i / num_samples) * 0.499f; - float r = fmodf((i + 0.5f + it_add) * num_samples_inv, 1.0f); - double dphi; - BLI_hammersley_1d(i, &dphi); - - float phi = (float)dphi * 2.0f * M_PI + it_add; - texels[i][0] = cosf(phi); - texels[i][1] = sinf(phi); - /* This deliberately distribute more samples - * at the center of the disk (and thus the shadow). */ - texels[i][2] = r; - } - - return (float *)texels; -} - -static struct GPUTexture *create_jitter_texture(int num_samples) -{ - float jitter[64 * 64][4]; - const float num_samples_inv = 1.0f / num_samples; - - for (int i = 0; i < 64 * 64; i++) { - float phi = blue_noise[i][0] * 2.0f * M_PI; - /* This rotate the sample per pixels */ - jitter[i][0] = cosf(phi); - jitter[i][1] = sinf(phi); - /* This offset the sample along it's direction axis (reduce banding) */ - float bn = blue_noise[i][1] - 0.5f; - CLAMP(bn, -0.499f, 0.499f); /* fix fireflies */ - jitter[i][2] = bn * num_samples_inv; - jitter[i][3] = blue_noise[i][1]; - } - - UNUSED_VARS(bsdf_split_sum_ggx, btdf_split_sum_ggx, ltc_mag_ggx, ltc_mat_ggx, ltc_disk_integral); - - return DRW_texture_create_2d(64, 64, GPU_RGBA16F, DRW_TEX_FILTER | DRW_TEX_WRAP, &jitter[0][0]); -} -/* Functions */ - -static void workbench_init_object_data(DrawData *dd) -{ - WORKBENCH_ObjectData *data = (WORKBENCH_ObjectData *)dd; - data->shadow_bbox_dirty = true; -} - -static void workbench_init_oit_framebuffer(WORKBENCH_FramebufferList *fbl, - DefaultTextureList *dtxl) -{ - const float *size = DRW_viewport_size_get(); - e_data.oit_accum_tx = DRW_texture_pool_query_2d( - size[0], size[1], GPU_RGBA16F, &draw_engine_workbench_solid); - e_data.oit_revealage_tx = DRW_texture_pool_query_2d( - size[0], size[1], GPU_R16F, &draw_engine_workbench_solid); - - GPU_framebuffer_ensure_config(&fbl->transparent_accum_fb, - { - GPU_ATTACHMENT_TEXTURE(dtxl->depth), - GPU_ATTACHMENT_TEXTURE(e_data.oit_accum_tx), - GPU_ATTACHMENT_TEXTURE(e_data.oit_revealage_tx), - }); -} - -void workbench_deferred_engine_init(WORKBENCH_Data *vedata) -{ - WORKBENCH_FramebufferList *fbl = vedata->fbl; - WORKBENCH_StorageList *stl = vedata->stl; - 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; - Scene *scene = draw_ctx->scene; - Object *camera; - - if (v3d && rv3d) { - camera = (rv3d->persp == RV3D_CAMOB) ? v3d->camera : NULL; - } - else { - camera = scene->camera; - } - - if (!stl->g_data) { - /* Alloc transient pointers */ - stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__); - } - if (!stl->effects) { - stl->effects = MEM_callocN(sizeof(*stl->effects), __func__); - workbench_effect_info_init(stl->effects); - } - - if (!e_data.shadow_pass_sh) { - WORKBENCH_DEFERRED_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg]; - memset(sh_data->prepass_sh_cache, 0, sizeof(sh_data->prepass_sh_cache)); - memset(e_data.composite_sh_cache, 0, sizeof(e_data.composite_sh_cache)); -#ifdef DEBUG_SHADOW_VOLUME - const char *shadow_frag = datatoc_workbench_shadow_debug_frag_glsl; -#else - const char *shadow_frag = datatoc_gpu_shader_depth_only_frag_glsl; -#endif - /* TODO only compile on demand */ - e_data.shadow_pass_sh = GPU_shader_create_from_arrays({ - .vert = (const char *[]){datatoc_common_view_lib_glsl, - datatoc_workbench_shadow_vert_glsl, - NULL}, - .geom = (const char *[]){datatoc_workbench_shadow_geom_glsl, NULL}, - .frag = (const char *[]){shadow_frag, NULL}, - .defs = (const char *[]){"#define SHADOW_PASS\n" - "#define DOUBLE_MANIFOLD\n", - NULL}, - }); - e_data.shadow_pass_manifold_sh = GPU_shader_create_from_arrays({ - .vert = (const char *[]){datatoc_common_view_lib_glsl, - datatoc_workbench_shadow_vert_glsl, - NULL}, - .geom = (const char *[]){datatoc_workbench_shadow_geom_glsl, NULL}, - .frag = (const char *[]){shadow_frag, NULL}, - .defs = (const char *[]){"#define SHADOW_PASS\n", NULL}, - }); - e_data.shadow_fail_sh = GPU_shader_create_from_arrays({ - .vert = (const char *[]){datatoc_common_view_lib_glsl, - datatoc_workbench_shadow_vert_glsl, - NULL}, - .geom = (const char *[]){datatoc_workbench_shadow_geom_glsl, NULL}, - .frag = (const char *[]){shadow_frag, NULL}, - .defs = (const char *[]){"#define SHADOW_FAIL\n" - "#define DOUBLE_MANIFOLD\n", - NULL}, - }); - e_data.shadow_fail_manifold_sh = GPU_shader_create_from_arrays({ - .vert = (const char *[]){datatoc_common_view_lib_glsl, - datatoc_workbench_shadow_vert_glsl, - NULL}, - .geom = (const char *[]){datatoc_workbench_shadow_geom_glsl, NULL}, - .frag = (const char *[]){shadow_frag, NULL}, - .defs = (const char *[]){"#define SHADOW_FAIL\n", NULL}, - }); - e_data.shadow_caps_sh = GPU_shader_create_from_arrays({ - .vert = (const char *[]){datatoc_common_view_lib_glsl, - datatoc_workbench_shadow_vert_glsl, - NULL}, - .geom = (const char *[]){datatoc_workbench_shadow_caps_geom_glsl, NULL}, - .frag = (const char *[]){shadow_frag, NULL}, - .defs = (const char *[]){"#define SHADOW_FAIL\n" - "#define DOUBLE_MANIFOLD\n", - NULL}, - }); - e_data.shadow_caps_manifold_sh = GPU_shader_create_from_arrays({ - .vert = (const char *[]){datatoc_common_view_lib_glsl, - datatoc_workbench_shadow_vert_glsl, - NULL}, - .geom = (const char *[]){datatoc_workbench_shadow_caps_geom_glsl, NULL}, - .frag = (const char *[]){shadow_frag, NULL}, - .defs = (const char *[]){"#define SHADOW_FAIL\n", NULL}, - }); - - e_data.ghost_resolve_sh = DRW_shader_create_fullscreen( - datatoc_workbench_ghost_resolve_frag_glsl, NULL); - } - workbench_volume_engine_init(); - workbench_fxaa_engine_init(); - workbench_taa_engine_init(vedata); - - WORKBENCH_PrivateData *wpd = stl->g_data; - workbench_private_data_init(wpd); - - wpd->shading.xray_alpha = 1.0f; - - workbench_dof_engine_init(vedata, camera); - - if (OIT_ENABLED(wpd)) { - if (e_data.oit_resolve_sh == NULL) { - e_data.oit_resolve_sh = DRW_shader_create_fullscreen( - datatoc_workbench_forward_composite_frag_glsl, "#define ALPHA_COMPOSITE\n"); - } - - workbench_forward_choose_shaders(wpd, draw_ctx->sh_cfg); - workbench_forward_outline_shaders_ensure(wpd, draw_ctx->sh_cfg); - } - - { - const float *viewport_size = DRW_viewport_size_get(); - const int size[2] = {(int)viewport_size[0], (int)viewport_size[1]}; - const eGPUTextureFormat nor_tex_format = NORMAL_ENCODING_ENABLED() ? GPU_RG16 : GPU_RGBA32F; - const eGPUTextureFormat comp_tex_format = GPU_RGBA16F; - const eGPUTextureFormat col_tex_format = workbench_color_texture_format(wpd); - const eGPUTextureFormat id_tex_format = OBJECT_ID_PASS_ENABLED(wpd) ? GPU_R32UI : GPU_R8; - - e_data.object_id_tx = NULL; - e_data.color_buffer_tx = NULL; - e_data.composite_buffer_tx = NULL; - e_data.normal_buffer_tx = NULL; - e_data.cavity_buffer_tx = NULL; - - e_data.composite_buffer_tx = DRW_texture_pool_query_2d( - size[0], size[1], comp_tex_format, &draw_engine_workbench_solid); - - if (workbench_is_matdata_pass_enabled(wpd) || GPU_unused_fb_slot_workaround()) { - e_data.color_buffer_tx = DRW_texture_pool_query_2d( - size[0], size[1], col_tex_format, &draw_engine_workbench_solid); - } - if (OBJECT_ID_PASS_ENABLED(wpd) || GPU_unused_fb_slot_workaround()) { - e_data.object_id_tx = DRW_texture_pool_query_2d( - size[0], size[1], id_tex_format, &draw_engine_workbench_solid); - } - if (NORMAL_VIEWPORT_PASS_ENABLED(wpd)) { - e_data.normal_buffer_tx = DRW_texture_pool_query_2d( - size[0], size[1], nor_tex_format, &draw_engine_workbench_solid); - } - if (CAVITY_ENABLED(wpd)) { - e_data.cavity_buffer_tx = DRW_texture_pool_query_2d( - size[0], size[1], GPU_R16, &draw_engine_workbench_solid); - } - - GPU_framebuffer_ensure_config(&fbl->prepass_fb, - { - GPU_ATTACHMENT_TEXTURE(dtxl->depth), - GPU_ATTACHMENT_TEXTURE(e_data.color_buffer_tx), - GPU_ATTACHMENT_TEXTURE(e_data.object_id_tx), - GPU_ATTACHMENT_TEXTURE(e_data.normal_buffer_tx), - }); - GPU_framebuffer_ensure_config(&fbl->cavity_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(e_data.cavity_buffer_tx), - }); - GPU_framebuffer_ensure_config(&fbl->composite_fb, - { - GPU_ATTACHMENT_TEXTURE(dtxl->depth), - GPU_ATTACHMENT_TEXTURE(e_data.composite_buffer_tx), - }); - GPU_framebuffer_ensure_config(&fbl->color_only_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(e_data.composite_buffer_tx), - }); - - if (!workbench_is_matdata_pass_enabled(wpd) && !GPU_unused_fb_slot_workaround()) { - e_data.color_buffer_tx = DRW_texture_pool_query_2d( - size[0], size[1], col_tex_format, &draw_engine_workbench_solid); - } - - GPU_framebuffer_ensure_config(&fbl->effect_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(e_data.color_buffer_tx), - }); - - if (OBJECT_ID_PASS_ENABLED(wpd)) { - GPU_framebuffer_ensure_config(&fbl->id_clear_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(e_data.object_id_tx), - }); - } - } - - { - /* AO Samples Tex */ - int num_iterations = workbench_taa_calculate_num_iterations(vedata); - - const int ssao_samples_single_iteration = scene->display.matcap_ssao_samples; - const int ssao_samples = MIN2(num_iterations * ssao_samples_single_iteration, 500); - - if (e_data.sampling_ubo && (e_data.cached_sample_num != ssao_samples)) { - DRW_UBO_FREE_SAFE(e_data.sampling_ubo); - DRW_TEXTURE_FREE_SAFE(e_data.jitter_tx); - } - - if (e_data.sampling_ubo == NULL) { - float *samples = create_disk_samples(ssao_samples_single_iteration, num_iterations); - e_data.jitter_tx = create_jitter_texture(ssao_samples); - e_data.sampling_ubo = DRW_uniformbuffer_create(sizeof(float[4]) * ssao_samples, samples); - e_data.cached_sample_num = ssao_samples; - MEM_freeN(samples); - } - } - - /* Prepass */ - { - DRWShadingGroup *grp; - DRWState clip_state = WORLD_CLIPPING_ENABLED(wpd) ? DRW_STATE_CLIP_PLANES : 0; - DRWState cull_state = CULL_BACKFACE_ENABLED(wpd) ? DRW_STATE_CULL_BACK : 0; - DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; - - psl->prepass_pass = DRW_pass_create("Prepass", state | cull_state | clip_state); - psl->prepass_hair_pass = DRW_pass_create("Prepass", state | clip_state); - - psl->ghost_prepass_pass = DRW_pass_create("Prepass Ghost", state | cull_state | clip_state); - psl->ghost_prepass_hair_pass = DRW_pass_create("Prepass Ghost", state | clip_state); - - psl->ghost_resolve_pass = DRW_pass_create("Resolve Ghost Depth", - DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS); - grp = DRW_shgroup_create(e_data.ghost_resolve_sh, psl->ghost_resolve_pass); - DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth_in_front); - DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); - } - - { - workbench_aa_create_pass(vedata, &e_data.color_buffer_tx); - } - - { - workbench_dof_create_pass(vedata, &e_data.composite_buffer_tx, e_data.jitter_tx); - } - - if (CAVITY_ENABLED(wpd)) { - int state = DRW_STATE_WRITE_COLOR; - GPUShader *shader = workbench_cavity_shader_get(SSAO_ENABLED(wpd), CURVATURE_ENABLED(wpd)); - psl->cavity_pass = DRW_pass_create("Cavity", state); - DRWShadingGroup *grp = DRW_shgroup_create(shader, psl->cavity_pass); - DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &e_data.normal_buffer_tx); - DRW_shgroup_uniform_block(grp, "samples_block", e_data.sampling_ubo); - - if (SSAO_ENABLED(wpd)) { - DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth); - DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1); - DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)wpd->viewvecs, 3); - DRW_shgroup_uniform_vec4(grp, "ssao_params", wpd->ssao_params, 1); - DRW_shgroup_uniform_vec4(grp, "ssao_settings", wpd->ssao_settings, 1); - DRW_shgroup_uniform_mat4(grp, "WinMatrix", wpd->winmat); - DRW_shgroup_uniform_texture(grp, "ssao_jitter", e_data.jitter_tx); - } - - if (CURVATURE_ENABLED(wpd)) { - DRW_shgroup_uniform_texture_ref(grp, "objectId", &e_data.object_id_tx); - DRW_shgroup_uniform_vec2(grp, "curvature_settings", &wpd->world_data.curvature_ridge, 1); - } - - DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); - } -} - -void workbench_deferred_engine_free(void) -{ - for (int sh_data_index = 0; sh_data_index < ARRAY_SIZE(e_data.sh_data); sh_data_index++) { - WORKBENCH_DEFERRED_Shaders *sh_data = &e_data.sh_data[sh_data_index]; - for (int index = 0; index < MAX_PREPASS_SHADERS; index++) { - DRW_SHADER_FREE_SAFE(sh_data->prepass_sh_cache[index]); - } - } - for (int index = 0; index < MAX_COMPOSITE_SHADERS; index++) { - DRW_SHADER_FREE_SAFE(e_data.composite_sh_cache[index]); - } - for (int index = 0; index < MAX_CAVITY_SHADERS; index++) { - DRW_SHADER_FREE_SAFE(e_data.cavity_sh[index]); - } - DRW_SHADER_FREE_SAFE(e_data.ghost_resolve_sh); - DRW_UBO_FREE_SAFE(e_data.sampling_ubo); - DRW_TEXTURE_FREE_SAFE(e_data.jitter_tx); - DRW_SHADER_FREE_SAFE(e_data.background_sh[0]); - DRW_SHADER_FREE_SAFE(e_data.background_sh[1]); - - DRW_SHADER_FREE_SAFE(e_data.oit_resolve_sh); - - DRW_SHADER_FREE_SAFE(e_data.shadow_pass_sh); - DRW_SHADER_FREE_SAFE(e_data.shadow_pass_manifold_sh); - DRW_SHADER_FREE_SAFE(e_data.shadow_fail_sh); - DRW_SHADER_FREE_SAFE(e_data.shadow_fail_manifold_sh); - DRW_SHADER_FREE_SAFE(e_data.shadow_caps_sh); - DRW_SHADER_FREE_SAFE(e_data.shadow_caps_manifold_sh); - - 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) -{ - DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo); - if (workbench_is_matdata_pass_enabled(wpd)) { - DRW_shgroup_uniform_texture_ref(grp, "materialBuffer", &e_data.color_buffer_tx); - } - else { - DRW_shgroup_uniform_vec3(grp, "materialSingleColor", wpd->shading.single_color, 1); - } - if (OBJECT_OUTLINE_ENABLED(wpd)) { - DRW_shgroup_uniform_texture_ref(grp, "objectId", &e_data.object_id_tx); - } - if (NORMAL_VIEWPORT_COMP_PASS_ENABLED(wpd)) { - DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &e_data.normal_buffer_tx); - } - if (CAVITY_ENABLED(wpd)) { - DRW_shgroup_uniform_texture_ref(grp, "cavityBuffer", &e_data.cavity_buffer_tx); - } - if (workbench_is_specular_highlight_enabled(wpd) || STUDIOLIGHT_TYPE_MATCAP_ENABLED(wpd)) { - DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)wpd->viewvecs, 3); - } - if (workbench_is_specular_highlight_enabled(wpd) || STUDIOLIGHT_TYPE_MATCAP_ENABLED(wpd)) { - DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1); - } - if (STUDIOLIGHT_TYPE_MATCAP_ENABLED(wpd)) { - BKE_studiolight_ensure_flag(wpd->studio_light, - STUDIOLIGHT_MATCAP_DIFFUSE_GPUTEXTURE | - STUDIOLIGHT_MATCAP_SPECULAR_GPUTEXTURE); - DRW_shgroup_uniform_texture( - grp, "matcapDiffuseImage", wpd->studio_light->matcap_diffuse.gputexture); - if (workbench_is_specular_highlight_enabled(wpd)) { - DRW_shgroup_uniform_texture( - grp, "matcapSpecularImage", wpd->studio_light->matcap_specular.gputexture); - } - } -} - -void workbench_deferred_cache_init(WORKBENCH_Data *vedata) -{ - WORKBENCH_StorageList *stl = vedata->stl; - WORKBENCH_PassList *psl = vedata->psl; - WORKBENCH_PrivateData *wpd = stl->g_data; - DRWShadingGroup *grp; - const DRWContextState *draw_ctx = DRW_context_state_get(); - - Scene *scene = draw_ctx->scene; - - workbench_volume_cache_init(vedata); - select_deferred_shaders(wpd, draw_ctx->sh_cfg); - - /* Background Pass */ - { - DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL; - if (DRW_state_is_scene_render()) { - /* Composite the scene over cleared background. */ - state |= DRW_STATE_BLEND_ALPHA_PREMUL; - } - psl->background_pass = DRW_pass_create("Background", state); - grp = DRW_shgroup_create(wpd->background_sh, psl->background_pass); - DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo); - DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1); - if (OBJECT_OUTLINE_ENABLED(wpd)) { - DRW_shgroup_uniform_texture_ref(grp, "objectId", &e_data.object_id_tx); - } - DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); - } - - /* Deferred Mix Pass */ - { - workbench_private_data_get_light_direction(e_data.display.light_direction); - studiolight_update_light(wpd, e_data.display.light_direction); - - if (SHADOW_ENABLED(wpd)) { - psl->composite_pass = DRW_pass_create( - "Composite", DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_EQUAL | DRW_STATE_DEPTH_GREATER); - grp = DRW_shgroup_create(wpd->composite_sh, psl->composite_pass); - workbench_composite_uniforms(wpd, grp); - DRW_shgroup_stencil_mask(grp, 0x00); - DRW_shgroup_uniform_float_copy(grp, "lightMultiplier", 1.0f); - DRW_shgroup_uniform_float(grp, "shadowMultiplier", &wpd->shadow_multiplier, 1); - DRW_shgroup_uniform_float_copy(grp, "shadowShift", scene->display.shadow_shift); - DRW_shgroup_uniform_float_copy(grp, "shadowFocus", wpd->shadow_focus); - DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); - - /* Stencil Shadow passes. */ -#ifdef DEBUG_SHADOW_VOLUME - DRWState depth_pass_state = DRW_STATE_DEPTH_LESS | DRW_STATE_WRITE_COLOR | - DRW_STATE_BLEND_ADD; - DRWState depth_fail_state = DRW_STATE_DEPTH_GREATER_EQUAL | DRW_STATE_WRITE_COLOR | - DRW_STATE_BLEND_ADD; -#else - DRWState depth_pass_state = DRW_STATE_DEPTH_LESS | DRW_STATE_WRITE_STENCIL_SHADOW_PASS | - DRW_STATE_STENCIL_ALWAYS; - DRWState depth_fail_state = DRW_STATE_DEPTH_LESS | DRW_STATE_WRITE_STENCIL_SHADOW_FAIL | - DRW_STATE_STENCIL_ALWAYS; -#endif - psl->shadow_depth_pass_pass = DRW_pass_create("Shadow Pass", depth_pass_state); - psl->shadow_depth_pass_mani_pass = DRW_pass_create("Shadow Pass Mani", depth_pass_state); - psl->shadow_depth_fail_pass = DRW_pass_create("Shadow Fail", depth_fail_state); - psl->shadow_depth_fail_mani_pass = DRW_pass_create("Shadow Fail Mani", depth_fail_state); - psl->shadow_depth_fail_caps_pass = DRW_pass_create("Shadow Fail Caps", depth_fail_state); - psl->shadow_depth_fail_caps_mani_pass = DRW_pass_create("Shadow Fail Caps Mani", - depth_fail_state); - -#ifndef DEBUG_SHADOW_VOLUME - grp = DRW_shgroup_create(e_data.shadow_pass_sh, psl->shadow_depth_pass_pass); - DRW_shgroup_stencil_mask(grp, 0xFF); - grp = DRW_shgroup_create(e_data.shadow_pass_manifold_sh, psl->shadow_depth_pass_mani_pass); - DRW_shgroup_stencil_mask(grp, 0xFF); - grp = DRW_shgroup_create(e_data.shadow_fail_sh, psl->shadow_depth_fail_pass); - DRW_shgroup_stencil_mask(grp, 0xFF); - grp = DRW_shgroup_create(e_data.shadow_fail_manifold_sh, psl->shadow_depth_fail_mani_pass); - DRW_shgroup_stencil_mask(grp, 0xFF); - grp = DRW_shgroup_create(e_data.shadow_caps_sh, psl->shadow_depth_fail_caps_pass); - DRW_shgroup_stencil_mask(grp, 0xFF); - grp = DRW_shgroup_create(e_data.shadow_caps_manifold_sh, - psl->shadow_depth_fail_caps_mani_pass); - DRW_shgroup_stencil_mask(grp, 0xFF); - - psl->composite_shadow_pass = DRW_pass_create( - "Composite Shadow", - DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_NEQUAL | DRW_STATE_DEPTH_GREATER); - grp = DRW_shgroup_create(wpd->composite_sh, psl->composite_shadow_pass); - DRW_shgroup_stencil_mask(grp, 0x00); - workbench_composite_uniforms(wpd, grp); - DRW_shgroup_uniform_float(grp, "lightMultiplier", &wpd->shadow_multiplier, 1); - DRW_shgroup_uniform_float(grp, "shadowMultiplier", &wpd->shadow_multiplier, 1); - DRW_shgroup_uniform_float_copy(grp, "shadowShift", scene->display.shadow_shift); - DRW_shgroup_uniform_float_copy(grp, "shadowFocus", wpd->shadow_focus); - DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); -#endif - } - else { - psl->composite_pass = DRW_pass_create("Composite", - DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_GREATER); - grp = DRW_shgroup_create(wpd->composite_sh, psl->composite_pass); - workbench_composite_uniforms(wpd, grp); - DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); - } - } - - /** - * Order Independent Transparency. - * Similar to workbench forward. Duplicated code to avoid - * spaghetti with workbench forward. It would be great if we unify - * this in a clean way. - */ - if (OIT_ENABLED(wpd)) { - DRWState clip_state = WORLD_CLIPPING_ENABLED(wpd) ? DRW_STATE_CLIP_PLANES : 0; - DRWState cull_state = CULL_BACKFACE_ENABLED(wpd) ? DRW_STATE_CULL_BACK : 0; - /* Transparency Accum */ - { - /* Same as forward but here we use depth test to - * not bleed through other solid objects. */ - int state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS | DRW_STATE_BLEND_OIT | cull_state | - clip_state; - psl->transparent_accum_pass = DRW_pass_create("Transparent Accum", state); - } - /* Depth */ - { - int state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | cull_state | clip_state; - psl->object_outline_pass = DRW_pass_create("Transparent Depth", state); - } - /* OIT Composite */ - { - int state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA_PREMUL; - psl->oit_composite_pass = DRW_pass_create("OIT Composite", state); - - grp = DRW_shgroup_create(e_data.oit_resolve_sh, psl->oit_composite_pass); - DRW_shgroup_uniform_texture_ref(grp, "transparentAccum", &e_data.oit_accum_tx); - DRW_shgroup_uniform_texture_ref(grp, "transparentRevealage", &e_data.oit_revealage_tx); - DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1); - DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); - } - } -} - -static WORKBENCH_MaterialData *get_or_create_material_data(WORKBENCH_Data *vedata, - Object *ob, - Material *mat, - Image *ima, - ImageUser *iuser, - eV3DShadingColorType color_type, - int interp) -{ - WORKBENCH_StorageList *stl = vedata->stl; - WORKBENCH_PassList *psl = vedata->psl; - WORKBENCH_PrivateData *wpd = stl->g_data; - WORKBENCH_MaterialData *material; - WORKBENCH_MaterialData material_template; - const bool is_ghost = (ob->dtx & OB_DRAWXRAY); - - /* Solid */ - workbench_material_update_data(wpd, ob, mat, &material_template, color_type); - material_template.color_type = color_type; - material_template.ima = ima; - material_template.iuser = iuser; - material_template.interp = interp; - uint hash = workbench_material_get_hash(&material_template, is_ghost); - - material = BLI_ghash_lookup(wpd->material_hash, POINTER_FROM_UINT(hash)); - if (material == NULL) { - material = MEM_mallocN(sizeof(WORKBENCH_MaterialData), __func__); - /* select the correct prepass shader */ - GPUShader *shader = (wpd->shading.color_type == color_type) ? wpd->prepass_sh : - wpd->prepass_uniform_sh; - const bool is_tiled = (ima && ima->source == IMA_SRC_TILED); - if (color_type == V3D_SHADING_TEXTURE_COLOR) { - shader = is_tiled ? wpd->prepass_textured_array_sh : wpd->prepass_textured_sh; - } - if (color_type == V3D_SHADING_VERTEX_COLOR) { - shader = wpd->prepass_vertex_sh; - } - material->shgrp = DRW_shgroup_create( - shader, (ob->dtx & OB_DRAWXRAY) ? psl->ghost_prepass_pass : psl->prepass_pass); - workbench_material_copy(material, &material_template); - DRW_shgroup_stencil_mask(material->shgrp, (ob->dtx & OB_DRAWXRAY) ? 0x00 : 0xFF); - workbench_material_shgroup_uniform(wpd, material->shgrp, material, ob, true, is_tiled, interp); - BLI_ghash_insert(wpd->material_hash, POINTER_FROM_UINT(hash), material); - } - return material; -} - -static void workbench_cache_populate_particles(WORKBENCH_Data *vedata, Object *ob) -{ - WORKBENCH_StorageList *stl = vedata->stl; - WORKBENCH_PassList *psl = vedata->psl; - WORKBENCH_PrivateData *wpd = stl->g_data; - - for (ModifierData *md = ob->modifiers.first; md; md = md->next) { - if (md->type != eModifierType_ParticleSystem) { - continue; - } - ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys; - if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) { - continue; - } - ParticleSettings *part = psys->part; - const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as; - - if (draw_as == PART_DRAW_PATH) { - Material *mat; - Image *image; - ImageUser *iuser; - int interp; - workbench_material_get_image_and_mat(ob, part->omat, &image, &iuser, &interp, &mat); - eV3DShadingColorType color_type = workbench_material_determine_color_type( - wpd, image, ob, false); - WORKBENCH_MaterialData *material = get_or_create_material_data( - vedata, ob, mat, image, iuser, color_type, interp); - - struct GPUShader *shader = (wpd->shading.color_type == color_type) ? - wpd->prepass_hair_sh : - wpd->prepass_uniform_hair_sh; - DRWShadingGroup *shgrp = DRW_shgroup_hair_create( - ob, - psys, - md, - (ob->dtx & OB_DRAWXRAY) ? psl->ghost_prepass_hair_pass : psl->prepass_hair_pass, - shader); - DRW_shgroup_stencil_mask(shgrp, (ob->dtx & OB_DRAWXRAY) ? 0x00 : 0xFF); - workbench_material_shgroup_uniform(wpd, shgrp, material, ob, true, false, interp); - } - } -} - -static void workbench_cache_populate_texture_paint_mode(WORKBENCH_Data *vedata, Object *ob) -{ - WORKBENCH_StorageList *stl = vedata->stl; - WORKBENCH_PrivateData *wpd = stl->g_data; - const DRWContextState *draw_ctx = DRW_context_state_get(); - - Scene *scene = draw_ctx->scene; - const bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) && - !DRW_state_is_image_render(); - WORKBENCH_MaterialData *material; - - /* Force workbench to render active object textured when in texture paint mode */ - const ImagePaintSettings *imapaint = &scene->toolsettings->imapaint; - - /* Single Image mode */ - if (imapaint->mode == IMAGEPAINT_MODE_IMAGE) { - Image *image = imapaint->canvas; - int interp = (imapaint->interp == IMAGEPAINT_INTERP_LINEAR) ? SHD_INTERP_LINEAR : - SHD_INTERP_CLOSEST; - eV3DShadingColorType color_type = workbench_material_determine_color_type( - wpd, image, ob, use_sculpt_pbvh); - struct GPUBatch *geom = DRW_cache_mesh_surface_texpaint_single_get(ob); - material = get_or_create_material_data(vedata, ob, NULL, image, NULL, color_type, interp); - - DRW_shgroup_call(material->shgrp, geom, ob); - } - else { - /* IMAGEPAINT_MODE_MATERIAL */ - const int materials_len = DRW_cache_object_material_count_get(ob); - struct GPUBatch **geom_array = DRW_cache_mesh_surface_texpaint_get(ob); - for (int i = 0; i < materials_len; i++) { - if (geom_array != NULL && geom_array[i] != NULL) { - Material *mat; - Image *image; - ImageUser *iuser; - int interp; - workbench_material_get_image_and_mat(ob, i + 1, &image, &iuser, &interp, &mat); - eV3DShadingColorType color_type = workbench_material_determine_color_type( - wpd, image, ob, use_sculpt_pbvh); - material = get_or_create_material_data(vedata, ob, mat, image, iuser, color_type, interp); - DRW_shgroup_call(material->shgrp, geom_array[i], ob); - } - } - } -} - -static void workbench_cache_populate_vertex_paint_mode(WORKBENCH_Data *vedata, Object *ob) -{ - WORKBENCH_StorageList *stl = vedata->stl; - WORKBENCH_PrivateData *wpd = stl->g_data; - const DRWContextState *draw_ctx = DRW_context_state_get(); - - const bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) && - !DRW_state_is_image_render(); - WORKBENCH_MaterialData *material; - - eV3DShadingColorType color_type = workbench_material_determine_color_type( - wpd, NULL, ob, use_sculpt_pbvh); - struct GPUBatch *geom = DRW_cache_mesh_surface_vertpaint_get(ob); - material = get_or_create_material_data(vedata, ob, NULL, NULL, NULL, color_type, false); - DRW_shgroup_call(material->shgrp, geom, ob); -} - -void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob) -{ - WORKBENCH_StorageList *stl = vedata->stl; - WORKBENCH_PassList *psl = vedata->psl; - WORKBENCH_PrivateData *wpd = stl->g_data; - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - - if (!DRW_object_is_renderable(ob)) { - return; - } - - if (ob->type == OB_MESH) { - workbench_cache_populate_particles(vedata, ob); - } - - ModifierData *md; - if (((ob->base_flag & BASE_FROM_DUPLI) == 0) && - (md = modifiers_findByType(ob, eModifierType_Fluid)) && - (modifier_isEnabled(scene, md, eModifierMode_Realtime)) && - (((FluidModifierData *)md)->domain != NULL) && - (((FluidModifierData *)md)->domain->type == FLUID_DOMAIN_TYPE_GAS)) { - workbench_volume_cache_populate(vedata, scene, ob, md); - return; /* Do not draw solid in this case. */ - } - - if (!(DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF)) { - return; - } - if ((ob->dt < OB_SOLID) && !DRW_state_is_scene_render()) { - return; - } - - WORKBENCH_MaterialData *material; - if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) { - const bool is_active = (ob == draw_ctx->obact); - const bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) && - !DRW_state_is_image_render(); - const bool use_hide = is_active && DRW_object_use_hide_faces(ob); - const int materials_len = DRW_cache_object_material_count_get(ob); - const Mesh *me = (ob->type == OB_MESH) ? ob->data : NULL; - bool has_transp_mat = false; - const WORKBENCH_ColorOverride color_override = workbench_object_color_override_get(ob); - const bool use_texture_paint_drawing = !(DRW_state_is_image_render() && - draw_ctx->v3d == NULL) && - (color_override == WORKBENCH_COLOR_OVERRIDE_TEXTURE) && - me && me->mloopuv; - const bool use_vertex_paint_drawing = !(DRW_state_is_image_render() && - draw_ctx->v3d == NULL) && - (color_override == WORKBENCH_COLOR_OVERRIDE_VERTEX) && - me && me->mloopcol; - - if (use_texture_paint_drawing) { - workbench_cache_populate_texture_paint_mode(vedata, ob); - } - else if (use_vertex_paint_drawing) { - workbench_cache_populate_vertex_paint_mode(vedata, ob); - } - else if (!use_sculpt_pbvh && TEXTURE_DRAWING_ENABLED(wpd) && me && me->mloopuv) { - /* Draw textured */ - struct GPUBatch **geom_array = DRW_cache_mesh_surface_texpaint_get(ob); - for (int i = 0; i < materials_len; i++) { - if (geom_array != NULL && geom_array[i] != NULL) { - Material *mat; - Image *image; - ImageUser *iuser; - int interp; - workbench_material_get_image_and_mat(ob, i + 1, &image, &iuser, &interp, &mat); - eV3DShadingColorType color_type = workbench_material_determine_color_type( - wpd, image, ob, use_sculpt_pbvh); - if (color_type == V3D_SHADING_MATERIAL_COLOR && mat && mat->a < 1.0) { - material = workbench_forward_get_or_create_material_data( - vedata, ob, mat, image, iuser, color_type, 0); - has_transp_mat = true; - } - else { - material = get_or_create_material_data( - vedata, ob, mat, image, iuser, color_type, interp); - } - DRW_shgroup_call(material->shgrp, geom_array[i], ob); - } - } - } - else if (ELEM(wpd->shading.color_type, - V3D_SHADING_SINGLE_COLOR, - V3D_SHADING_OBJECT_COLOR, - V3D_SHADING_RANDOM_COLOR, - V3D_SHADING_VERTEX_COLOR)) { - eV3DShadingColorType color_type = workbench_material_determine_color_type( - wpd, NULL, ob, use_sculpt_pbvh); - - if ((ob->color[3] < 1.0f) && (color_type == V3D_SHADING_OBJECT_COLOR)) { - material = workbench_forward_get_or_create_material_data( - vedata, ob, NULL, NULL, NULL, color_type, 0); - has_transp_mat = true; - } - else { - /* Draw solid color */ - material = get_or_create_material_data(vedata, ob, NULL, NULL, NULL, color_type, 0); - } - - if (use_sculpt_pbvh) { - bool use_vcol = (color_type == V3D_SHADING_VERTEX_COLOR); - DRW_shgroup_call_sculpt(material->shgrp, ob, false, false, use_vcol); - } - else { - struct GPUBatch *geom; - if (color_type == V3D_SHADING_VERTEX_COLOR) { - geom = DRW_cache_mesh_surface_vertpaint_get(ob); - } - else { - geom = DRW_cache_object_surface_get(ob); - } - - if (geom) { - DRW_shgroup_call(material->shgrp, geom, ob); - } - } - } - else { - /* Draw material color */ - if (use_sculpt_pbvh) { - struct DRWShadingGroup **shgrps = BLI_array_alloca(shgrps, materials_len); - - for (int i = 0; i < materials_len; i++) { - struct Material *mat = BKE_object_material_get(ob, i + 1); - if (mat != NULL && mat->a < 1.0f) { - material = workbench_forward_get_or_create_material_data( - vedata, ob, mat, NULL, NULL, V3D_SHADING_MATERIAL_COLOR, 0); - has_transp_mat = true; - } - else { - material = get_or_create_material_data( - vedata, ob, mat, NULL, NULL, V3D_SHADING_MATERIAL_COLOR, 0); - } - shgrps[i] = material->shgrp; - } - DRW_shgroup_call_sculpt_with_materials(shgrps, ob, false); - } - else { - struct GPUBatch **geoms; - struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len); - memset(gpumat_array, 0, sizeof(*gpumat_array) * materials_len); - - geoms = DRW_cache_object_surface_material_get(ob, gpumat_array, materials_len); - for (int i = 0; i < materials_len; i++) { - if (geoms != NULL && geoms[i] != NULL) { - Material *mat = BKE_object_material_get(ob, i + 1); - if (mat != NULL && mat->a < 1.0f) { - material = workbench_forward_get_or_create_material_data( - vedata, ob, mat, NULL, NULL, V3D_SHADING_MATERIAL_COLOR, 0); - has_transp_mat = true; - } - else { - material = get_or_create_material_data( - vedata, ob, mat, NULL, NULL, V3D_SHADING_MATERIAL_COLOR, 0); - } - DRW_shgroup_call(material->shgrp, geoms[i], ob); - } - } - } - } - - if (SHADOW_ENABLED(wpd) && !(ob->dtx & OB_DRAW_NO_SHADOW_CAST)) { - bool is_manifold; - struct GPUBatch *geom_shadow = DRW_cache_object_edge_detection_get(ob, &is_manifold); - if (geom_shadow) { - if (use_sculpt_pbvh || use_hide) { - /* Currently unsupported in sculpt mode. We could revert to the slow - * method in this case but I'm not sure if it's a good idea given that - * sculpted meshes are heavy to begin with. */ - // DRW_shgroup_call_sculpt(wpd->shadow_shgrp, ob, ob->obmat); - } - else { - WORKBENCH_ObjectData *engine_object_data = (WORKBENCH_ObjectData *)DRW_drawdata_ensure( - &ob->id, - &draw_engine_workbench_solid, - sizeof(WORKBENCH_ObjectData), - &workbench_init_object_data, - NULL); - - if (studiolight_object_cast_visible_shadow(wpd, ob, engine_object_data)) { - - mul_v3_mat3_m4v3( - engine_object_data->shadow_dir, ob->imat, e_data.display.light_direction); - - DRWShadingGroup *grp; - bool use_shadow_pass_technique = !studiolight_camera_in_object_shadow( - wpd, ob, engine_object_data); - - if (use_shadow_pass_technique && !has_transp_mat) { - if (is_manifold) { - grp = DRW_shgroup_create(e_data.shadow_pass_manifold_sh, - psl->shadow_depth_pass_mani_pass); - } - else { - grp = DRW_shgroup_create(e_data.shadow_pass_sh, psl->shadow_depth_pass_pass); - } - DRW_shgroup_uniform_vec3(grp, "lightDirection", engine_object_data->shadow_dir, 1); - DRW_shgroup_uniform_float_copy(grp, "lightDistance", 1e5f); - DRW_shgroup_call_no_cull(grp, geom_shadow, ob); -#ifdef DEBUG_SHADOW_VOLUME - DRW_debug_bbox(&engine_object_data->shadow_bbox, (float[4]){1.0f, 0.0f, 0.0f, 1.0f}); -#endif - } - else { - float extrude_distance = studiolight_object_shadow_distance( - wpd, ob, engine_object_data); - - /* TODO(fclem): only use caps if they are in the view frustum. */ - const bool need_caps = true; - if (need_caps) { - if (is_manifold) { - grp = DRW_shgroup_create(e_data.shadow_caps_manifold_sh, - psl->shadow_depth_fail_caps_mani_pass); - } - else { - grp = DRW_shgroup_create(e_data.shadow_caps_sh, - psl->shadow_depth_fail_caps_pass); - } - DRW_shgroup_uniform_vec3(grp, "lightDirection", engine_object_data->shadow_dir, 1); - DRW_shgroup_uniform_float_copy(grp, "lightDistance", extrude_distance); - DRW_shgroup_call_no_cull(grp, DRW_cache_object_surface_get(ob), ob); - } - - if (is_manifold) { - grp = DRW_shgroup_create(e_data.shadow_fail_manifold_sh, - psl->shadow_depth_fail_mani_pass); - } - else { - grp = DRW_shgroup_create(e_data.shadow_fail_sh, psl->shadow_depth_fail_pass); - } - DRW_shgroup_uniform_vec3(grp, "lightDirection", engine_object_data->shadow_dir, 1); - DRW_shgroup_uniform_float_copy(grp, "lightDistance", extrude_distance); - DRW_shgroup_call_no_cull(grp, geom_shadow, ob); -#ifdef DEBUG_SHADOW_VOLUME - DRW_debug_bbox(&engine_object_data->shadow_bbox, (float[4]){0.0f, 1.0f, 0.0f, 1.0f}); -#endif - } - } - } - } - } - } -} - -void workbench_deferred_cache_finish(WORKBENCH_Data *vedata) -{ - WORKBENCH_PassList *psl = vedata->psl; - WORKBENCH_FramebufferList *fbl = vedata->fbl; - - if (GHOST_ENABLED(psl)) { - /* HACK we allocate the infront depth here to avoid the overhead when if is not needed. */ - DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - - DRW_texture_ensure_fullscreen_2d(&dtxl->depth_in_front, GPU_DEPTH24_STENCIL8, 0); - - GPU_framebuffer_ensure_config( - &dfbl->default_fb, - {GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(dtxl->color)}); - GPU_framebuffer_ensure_config( - &dfbl->in_front_fb, - {GPU_ATTACHMENT_TEXTURE(dtxl->depth_in_front), GPU_ATTACHMENT_TEXTURE(dtxl->color)}); - - GPU_framebuffer_ensure_config(&fbl->ghost_prepass_fb, - { - GPU_ATTACHMENT_TEXTURE(dtxl->depth_in_front), - GPU_ATTACHMENT_TEXTURE(e_data.color_buffer_tx), - GPU_ATTACHMENT_TEXTURE(e_data.object_id_tx), - GPU_ATTACHMENT_TEXTURE(e_data.normal_buffer_tx), - }); - } -} - -void workbench_deferred_draw_scene(WORKBENCH_Data *vedata) -{ - WORKBENCH_PassList *psl = vedata->psl; - WORKBENCH_StorageList *stl = vedata->stl; - WORKBENCH_FramebufferList *fbl = vedata->fbl; - WORKBENCH_PrivateData *wpd = stl->g_data; - DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - - if (workbench_is_taa_enabled(wpd)) { - workbench_taa_draw_scene_start(vedata); - } - - const float clear_depth = 1.0f; - const float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - uint clear_stencil = 0x00; - int clear_bits = GPU_DEPTH_BIT; - SET_FLAG_FROM_TEST(clear_bits, SHADOW_ENABLED(wpd), GPU_STENCIL_BIT); - - if (OBJECT_ID_PASS_ENABLED(wpd)) { - /* From all the color buffers, only object id needs to be cleared. */ - GPU_framebuffer_bind(fbl->id_clear_fb); - GPU_framebuffer_clear_color(fbl->id_clear_fb, clear_col); - } - - GPU_framebuffer_bind(fbl->prepass_fb); - GPU_framebuffer_clear(fbl->prepass_fb, clear_bits, clear_col, clear_depth, clear_stencil); - - DRW_draw_pass(psl->prepass_pass); - DRW_draw_pass(psl->prepass_hair_pass); - - if (fbl->ghost_prepass_fb) { - GPU_framebuffer_bind(fbl->ghost_prepass_fb); - GPU_framebuffer_clear_depth(fbl->ghost_prepass_fb, 1.0f); - } - else if (dtxl->depth_in_front) { - /* TODO(fclem) This clear should be done in a global place. */ - GPU_framebuffer_bind(dfbl->in_front_fb); - GPU_framebuffer_clear_depth(dfbl->in_front_fb, 1.0f); - } - - if (GHOST_ENABLED(psl)) { - DRW_draw_pass(psl->ghost_prepass_pass); - DRW_draw_pass(psl->ghost_prepass_hair_pass); - - GPU_framebuffer_bind(dfbl->depth_only_fb); - DRW_draw_pass(psl->ghost_resolve_pass); - } - - if (CAVITY_ENABLED(wpd)) { - GPU_framebuffer_bind(fbl->cavity_fb); - DRW_draw_pass(psl->cavity_pass); - } - - if (DRW_state_is_scene_render()) { - float clear_color[4]; - workbench_clear_color_get(clear_color); - GPU_framebuffer_bind(fbl->composite_fb); - GPU_framebuffer_clear_color(fbl->composite_fb, clear_color); - } - - if (SHADOW_ENABLED(wpd)) { -#ifdef DEBUG_SHADOW_VOLUME - GPU_framebuffer_bind(fbl->composite_fb); - DRW_draw_pass(psl->composite_pass); -#else - GPU_framebuffer_bind(dfbl->depth_only_fb); -#endif - DRW_draw_pass(psl->shadow_depth_pass_pass); - DRW_draw_pass(psl->shadow_depth_pass_mani_pass); - DRW_draw_pass(psl->shadow_depth_fail_pass); - DRW_draw_pass(psl->shadow_depth_fail_mani_pass); - DRW_draw_pass(psl->shadow_depth_fail_caps_pass); - DRW_draw_pass(psl->shadow_depth_fail_caps_mani_pass); - - if (GHOST_ENABLED(psl)) { - /* We need to set the stencil buffer to 0 where Ghost objects are - * else they will get shadow and even badly shadowed. */ - DRWState state = DRW_STATE_DEPTH_EQUAL | DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_ALWAYS; - DRW_pass_state_set(psl->ghost_prepass_pass, state); - DRW_pass_state_set(psl->ghost_prepass_hair_pass, state); - - DRW_draw_pass(psl->ghost_prepass_pass); - DRW_draw_pass(psl->ghost_prepass_hair_pass); - } -#ifndef DEBUG_SHADOW_VOLUME - GPU_framebuffer_bind(fbl->composite_fb); - DRW_draw_pass(psl->composite_pass); - DRW_draw_pass(psl->composite_shadow_pass); -#endif - } - else { - GPU_framebuffer_bind(fbl->composite_fb); - DRW_draw_pass(psl->composite_pass); - } - - /* In order to not draw on top of ghost objects, we clear the stencil - * to 0xFF and the ghost object to 0x00 and only draw overlays on top if - * stencil is not 0. */ - /* TODO(fclem) Remove this hack. */ - GPU_framebuffer_bind(dfbl->depth_only_fb); - GPU_framebuffer_clear_stencil(dfbl->depth_only_fb, 0xFF); - - /* TODO(fclem): only enable when needed (when there is overlays). */ - if (GHOST_ENABLED(psl)) { - DRWState state = DRW_STATE_DEPTH_EQUAL | DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_ALWAYS; - DRW_pass_state_set(psl->ghost_prepass_pass, state); - DRW_pass_state_set(psl->ghost_prepass_hair_pass, state); - - DRW_draw_pass(psl->ghost_prepass_pass); - DRW_draw_pass(psl->ghost_prepass_hair_pass); - } - - GPU_framebuffer_bind(fbl->composite_fb); - DRW_draw_pass(psl->background_pass); - - if (OIT_ENABLED(wpd) && !DRW_pass_is_empty(psl->transparent_accum_pass)) { - /* meh, late init to not request buffers we won't use. */ - workbench_init_oit_framebuffer(fbl, dtxl); - - const float clear_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; - GPU_framebuffer_bind(fbl->transparent_accum_fb); - GPU_framebuffer_clear_color(fbl->transparent_accum_fb, clear_color); - DRW_draw_pass(psl->transparent_accum_pass); - - GPU_framebuffer_bind(fbl->composite_fb); - DRW_draw_pass(psl->oit_composite_pass); - } - - if (wpd->volumes_do) { - 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); -} - -void workbench_deferred_draw_finish(WORKBENCH_Data *vedata) -{ - WORKBENCH_StorageList *stl = vedata->stl; - WORKBENCH_PrivateData *wpd = stl->g_data; - - /* XXX TODO(fclem) do not discard UBOS after drawing! Store them per viewport. */ - workbench_private_data_free(wpd); - workbench_volume_smoke_textures_free(wpd); -} diff --git a/source/blender/draw/engines/workbench/workbench_effect_aa.c b/source/blender/draw/engines/workbench/workbench_effect_aa.c deleted file mode 100644 index c03fe5cd5c8..00000000000 --- a/source/blender/draw/engines/workbench/workbench_effect_aa.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - */ - -#include "ED_screen.h" - -#include "draw_color_management.h" - -#include "workbench_private.h" - -void workbench_aa_create_pass(WORKBENCH_Data *vedata, GPUTexture **tx) -{ - WORKBENCH_StorageList *stl = vedata->stl; - WORKBENCH_PrivateData *wpd = stl->g_data; - WORKBENCH_PassList *psl = vedata->psl; - WORKBENCH_EffectInfo *effect_info = stl->effects; - const DRWContextState *draw_ctx = DRW_context_state_get(); - - if (draw_ctx->evil_C != NULL) { - struct wmWindowManager *wm = CTX_wm_manager(draw_ctx->evil_C); - wpd->is_playback = ED_screen_animation_playing(wm) != NULL; - } - else { - wpd->is_playback = false; - } - - if (workbench_is_taa_enabled(wpd)) { - psl->effect_aa_pass = workbench_taa_create_pass(vedata, tx); - } - else if (workbench_is_fxaa_enabled(wpd)) { - psl->effect_aa_pass = workbench_fxaa_create_pass(tx); - effect_info->jitter_index = 0; - } - else { - psl->effect_aa_pass = NULL; - effect_info->jitter_index = 0; - } -} - -static void workspace_aa_draw_transform(GPUTexture *tx, WORKBENCH_PrivateData *UNUSED(wpd)) -{ - DRW_transform_none(tx); -} - -void workbench_aa_draw_pass(WORKBENCH_Data *vedata, GPUTexture *tx) -{ - WORKBENCH_StorageList *stl = vedata->stl; - WORKBENCH_PrivateData *wpd = stl->g_data; - WORKBENCH_FramebufferList *fbl = vedata->fbl; - WORKBENCH_PassList *psl = vedata->psl; - WORKBENCH_EffectInfo *effect_info = stl->effects; - - DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); - if (workbench_is_fxaa_enabled(wpd)) { - GPU_framebuffer_bind(fbl->effect_fb); - workspace_aa_draw_transform(tx, wpd); - GPU_framebuffer_bind(dfbl->color_only_fb); - DRW_draw_pass(psl->effect_aa_pass); - } - else if (workbench_is_taa_enabled(wpd)) { - /* - * when drawing the first TAA frame, we transform directly to the - * color_only_fb as the TAA shader is just performing a direct copy. - * the workbench_taa_draw_screen_end will fill the history buffer - * for the other iterations. - */ - if (effect_info->jitter_index == 1) { - GPU_framebuffer_bind(dfbl->color_only_fb); - workspace_aa_draw_transform(tx, wpd); - } - else { - GPU_framebuffer_bind(fbl->effect_fb); - workspace_aa_draw_transform(tx, wpd); - GPU_framebuffer_bind(dfbl->color_only_fb); - DRW_draw_pass(psl->effect_aa_pass); - } - workbench_taa_draw_scene_end(vedata); - } - else { - GPU_framebuffer_bind(dfbl->color_only_fb); - workspace_aa_draw_transform(tx, wpd); - } -} diff --git a/source/blender/draw/engines/workbench/workbench_effect_antialiasing.c b/source/blender/draw/engines/workbench/workbench_effect_antialiasing.c new file mode 100644 index 00000000000..6a8f77699bc --- /dev/null +++ b/source/blender/draw/engines/workbench/workbench_effect_antialiasing.c @@ -0,0 +1,421 @@ +/* + * 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. + * + * Copyright 2020, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + * + * Anti-aliasing: + * + * We use SMAA (Smart Morphological Anti-Aliasing) as a fast antialiasing solution. + * + * If the viewport stays static, the engine ask for multiple redraw and will progressively + * converge to a much more accurate image without aliasing. + * We call this one TAA (Temporal Anti-Aliasing). + * + * This is done using an accumulation buffer and a final pass that will output the final color + * to the scene buffer. We softly blend between SMAA and TAA to avoid really harsh transitions. + */ + +#include "ED_screen.h" + +#include "BLI_jitter_2d.h" + +#include "smaa_textures.h" + +#include "workbench_private.h" + +static struct { + bool init; + float jitter_5[5][2]; + float jitter_8[8][2]; + float jitter_11[11][2]; + float jitter_16[16][2]; + float jitter_32[32][2]; +} e_data = {false}; + +static void workbench_taa_jitter_init_order(float (*table)[2], int num) +{ + BLI_jitter_init(table, num); + + /* find closest element to center */ + int closest_index = 0; + float closest_squared_distance = 1.0f; + + for (int index = 0; index < num; index++) { + const float squared_dist = square_f(table[index][0]) + square_f(table[index][1]); + if (squared_dist < closest_squared_distance) { + closest_squared_distance = squared_dist; + closest_index = index; + } + } + + /* move jitter table so that closest sample is in center */ + for (int index = 0; index < num; index++) { + sub_v2_v2(table[index], table[closest_index]); + mul_v2_fl(table[index], 2.0f); + } + + /* swap center sample to the start of the table */ + if (closest_index != 0) { + swap_v2_v2(table[0], table[closest_index]); + } + + /* sort list based on furtest distance with previous */ + for (int i = 0; i < num - 2; i++) { + float f_squared_dist = 0.0; + int f_index = i; + for (int j = i + 1; j < num; j++) { + const float squared_dist = square_f(table[i][0] - table[j][0]) + + square_f(table[i][1] - table[j][1]); + if (squared_dist > f_squared_dist) { + f_squared_dist = squared_dist; + f_index = j; + } + } + swap_v2_v2(table[i + 1], table[f_index]); + } +} + +static void workbench_taa_jitter_init(void) +{ + if (e_data.init == false) { + e_data.init = true; + workbench_taa_jitter_init_order(e_data.jitter_5, 5); + workbench_taa_jitter_init_order(e_data.jitter_8, 8); + workbench_taa_jitter_init_order(e_data.jitter_11, 11); + workbench_taa_jitter_init_order(e_data.jitter_16, 16); + workbench_taa_jitter_init_order(e_data.jitter_32, 32); + } +} + +int workbench_antialiasing_sample_count_get(WORKBENCH_PrivateData *wpd) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + const Scene *scene = draw_ctx->scene; + + if (wpd->is_navigating || wpd->is_playback) { + /* Only draw using SMAA or no AA when navigating. */ + return min_ii(wpd->preferences->viewport_aa, 1); + } + else if (DRW_state_is_image_render()) { + if (draw_ctx->v3d) { + return scene->display.viewport_aa; + } + else { + return scene->display.render_aa; + } + } + else { + return wpd->preferences->viewport_aa; + } +} + +void workbench_antialiasing_view_updated(WORKBENCH_Data *vedata) +{ + WORKBENCH_StorageList *stl = vedata->stl; + if (stl && stl->wpd) { + stl->wpd->view_updated = true; + } +} + +void workbench_antialiasing_engine_init(WORKBENCH_Data *vedata) +{ + WORKBENCH_FramebufferList *fbl = vedata->fbl; + WORKBENCH_TextureList *txl = vedata->txl; + WORKBENCH_PrivateData *wpd = vedata->stl->wpd; + DrawEngineType *owner = (DrawEngineType *)&workbench_antialiasing_engine_init; + + wpd->view = NULL; + + /* reset complete drawing when navigating. */ + if (wpd->taa_sample != 0) { + if (wpd->is_navigating) { + wpd->taa_sample = 0; + } + } + + if (wpd->view_updated) { + wpd->taa_sample = 0; + wpd->view_updated = false; + } + + { + float persmat[4][4]; + DRW_view_persmat_get(NULL, persmat, false); + if (!equals_m4m4(persmat, wpd->last_mat)) { + copy_m4_m4(wpd->last_mat, persmat); + wpd->taa_sample = 0; + } + } + + if (wpd->taa_sample_len > 0) { + workbench_taa_jitter_init(); + + DRW_texture_ensure_fullscreen_2d(&txl->history_buffer_tx, GPU_RGBA16F, DRW_TEX_FILTER); + DRW_texture_ensure_fullscreen_2d(&txl->depth_buffer_tx, GPU_DEPTH24_STENCIL8, 0); + + wpd->smaa_edge_tx = DRW_texture_pool_query_fullscreen(GPU_RG8, owner); + wpd->smaa_weight_tx = DRW_texture_pool_query_fullscreen(GPU_RGBA8, owner); + + GPU_framebuffer_ensure_config(&fbl->antialiasing_fb, + { + GPU_ATTACHMENT_TEXTURE(txl->depth_buffer_tx), + GPU_ATTACHMENT_TEXTURE(txl->history_buffer_tx), + }); + + GPU_framebuffer_ensure_config(&fbl->smaa_edge_fb, + { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(wpd->smaa_edge_tx), + }); + + GPU_framebuffer_ensure_config(&fbl->smaa_weight_fb, + { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(wpd->smaa_weight_tx), + }); + + /* TODO could be shared for all viewports. */ + if (txl->smaa_search_tx == NULL) { + txl->smaa_search_tx = GPU_texture_create_nD(SEARCHTEX_WIDTH, + SEARCHTEX_HEIGHT, + 0, + 2, + searchTexBytes, + GPU_R8, + GPU_DATA_UNSIGNED_BYTE, + 0, + false, + NULL); + + txl->smaa_area_tx = GPU_texture_create_nD(AREATEX_WIDTH, + AREATEX_HEIGHT, + 0, + 2, + areaTexBytes, + GPU_RG8, + GPU_DATA_UNSIGNED_BYTE, + 0, + false, + NULL); + + GPU_texture_bind(txl->smaa_search_tx, 0); + GPU_texture_filter_mode(txl->smaa_search_tx, true); + GPU_texture_unbind(txl->smaa_search_tx); + + GPU_texture_bind(txl->smaa_area_tx, 0); + GPU_texture_filter_mode(txl->smaa_area_tx, true); + GPU_texture_unbind(txl->smaa_area_tx); + } + } + else { + /* Cleanup */ + DRW_TEXTURE_FREE_SAFE(txl->history_buffer_tx); + DRW_TEXTURE_FREE_SAFE(txl->depth_buffer_tx); + DRW_TEXTURE_FREE_SAFE(txl->smaa_search_tx); + DRW_TEXTURE_FREE_SAFE(txl->smaa_area_tx); + } +} + +void workbench_antialiasing_cache_init(WORKBENCH_Data *vedata) +{ + WORKBENCH_TextureList *txl = vedata->txl; + WORKBENCH_PrivateData *wpd = vedata->stl->wpd; + WORKBENCH_PassList *psl = vedata->psl; + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + DRWShadingGroup *grp = NULL; + + if (wpd->taa_sample_len == 0) { + return; + } + + { + DRW_PASS_CREATE(psl->aa_accum_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL); + + GPUShader *shader = workbench_shader_antialiasing_accumulation_get(); + grp = DRW_shgroup_create(shader, psl->aa_accum_ps); + DRW_shgroup_uniform_texture(grp, "colorBuffer", dtxl->color); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } + + const float *size = DRW_viewport_size_get(); + const float *sizeinv = DRW_viewport_invert_size_get(); + float metrics[4] = {sizeinv[0], sizeinv[1], size[0], size[1]}; + + { + /* Stage 1: Edge detection. */ + DRW_PASS_CREATE(psl->aa_edge_ps, DRW_STATE_WRITE_COLOR); + + GPUShader *sh = workbench_shader_antialiasing_get(0); + grp = DRW_shgroup_create(sh, psl->aa_edge_ps); + DRW_shgroup_uniform_texture(grp, "colorTex", txl->history_buffer_tx); + DRW_shgroup_uniform_vec4_copy(grp, "viewportMetrics", metrics); + + DRW_shgroup_clear_framebuffer(grp, GPU_COLOR_BIT, 0, 0, 0, 0, 0.0f, 0x0); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } + { + /* Stage 2: Blend Weight/Coord. */ + DRW_PASS_CREATE(psl->aa_weight_ps, DRW_STATE_WRITE_COLOR); + + GPUShader *sh = workbench_shader_antialiasing_get(1); + grp = DRW_shgroup_create(sh, psl->aa_weight_ps); + DRW_shgroup_uniform_texture(grp, "edgesTex", wpd->smaa_edge_tx); + DRW_shgroup_uniform_texture(grp, "areaTex", txl->smaa_area_tx); + DRW_shgroup_uniform_texture(grp, "searchTex", txl->smaa_search_tx); + DRW_shgroup_uniform_vec4_copy(grp, "viewportMetrics", metrics); + + DRW_shgroup_clear_framebuffer(grp, GPU_COLOR_BIT, 0, 0, 0, 0, 0.0f, 0x0); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } + { + /* Stage 3: Resolve. */ + DRW_PASS_CREATE(psl->aa_resolve_ps, DRW_STATE_WRITE_COLOR); + + GPUShader *sh = workbench_shader_antialiasing_get(2); + grp = DRW_shgroup_create(sh, psl->aa_resolve_ps); + DRW_shgroup_uniform_texture(grp, "blendTex", wpd->smaa_weight_tx); + DRW_shgroup_uniform_texture(grp, "colorTex", txl->history_buffer_tx); + DRW_shgroup_uniform_vec4_copy(grp, "viewportMetrics", metrics); + DRW_shgroup_uniform_float(grp, "mixFactor", &wpd->smaa_mix_factor, 1); + DRW_shgroup_uniform_float(grp, "taaSampleCountInv", &wpd->taa_sample_inv, 1); + + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } +} + +/* Return true if render is not cached. */ +bool workbench_antialiasing_setup(WORKBENCH_Data *vedata) +{ + WORKBENCH_PrivateData *wpd = vedata->stl->wpd; + + if (wpd->taa_sample_len == 0) { + /* AA disabled. */ + return true; + } + + if (wpd->taa_sample >= wpd->taa_sample_len) { + /* TAA accumulation has finish. Just copy the result back */ + return false; + } + else { + const float *viewport_size = DRW_viewport_size_get(); + const DRWView *default_view = DRW_view_default_get(); + float *transform_offset; + + switch (wpd->taa_sample_len) { + default: + case 5: + transform_offset = e_data.jitter_5[min_ii(wpd->taa_sample, 5)]; + break; + case 8: + transform_offset = e_data.jitter_8[min_ii(wpd->taa_sample, 8)]; + break; + case 11: + transform_offset = e_data.jitter_11[min_ii(wpd->taa_sample, 11)]; + break; + case 16: + transform_offset = e_data.jitter_16[min_ii(wpd->taa_sample, 16)]; + break; + case 32: + transform_offset = e_data.jitter_32[min_ii(wpd->taa_sample, 32)]; + break; + } + + /* construct new matrices from transform delta */ + float winmat[4][4], viewmat[4][4], persmat[4][4]; + DRW_view_winmat_get(default_view, winmat, false); + DRW_view_viewmat_get(default_view, viewmat, false); + DRW_view_persmat_get(default_view, persmat, false); + + window_translate_m4(winmat, + persmat, + transform_offset[0] / viewport_size[0], + transform_offset[1] / viewport_size[1]); + + if (wpd->view) { + /* When rendering just update the view. This avoids recomputing the culling. */ + DRW_view_update_sub(wpd->view, viewmat, winmat); + } + else { + /* TAA is not making a big change to the matrices. + * Reuse the main view culling by creating a subview. */ + wpd->view = DRW_view_create_sub(default_view, viewmat, winmat); + } + DRW_view_set_active(wpd->view); + return true; + } +} + +void workbench_antialiasing_draw_pass(WORKBENCH_Data *vedata) +{ + WORKBENCH_PrivateData *wpd = vedata->stl->wpd; + WORKBENCH_FramebufferList *fbl = vedata->fbl; + WORKBENCH_PassList *psl = vedata->psl; + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + + if (wpd->taa_sample_len == 0) { + /* AA disabled. */ + /* Just set sample to 1 to avoid rendering indefinitely. */ + wpd->taa_sample = 1; + return; + } + + /** + * We always do SMAA on top of TAA accumulation, unless the number of samples of TAA is already + * high. This ensure a smoother transition. + * If TAA accumulation is finished, we only blit the result. + */ + + if (wpd->taa_sample == 0) { + /* In playback mode, we are sure the next redraw will not use the same viewmatrix. + * In this case no need to save the depth buffer. */ + eGPUFrameBufferBits bits = GPU_COLOR_BIT | (!wpd->is_playback ? GPU_DEPTH_BIT : 0); + GPU_framebuffer_blit(dfbl->default_fb, 0, fbl->antialiasing_fb, 0, bits); + } + else { + /* Accumulate result to the TAA buffer. */ + GPU_framebuffer_bind(fbl->antialiasing_fb); + DRW_draw_pass(psl->aa_accum_ps); + /* Copy back the saved depth buffer for correct overlays. */ + GPU_framebuffer_blit(fbl->antialiasing_fb, 0, dfbl->default_fb, 0, GPU_DEPTH_BIT); + } + + if (!DRW_state_is_image_render() || wpd->taa_sample + 1 == wpd->taa_sample_len) { + /* After a certain point SMAA is no longer necessary. */ + wpd->smaa_mix_factor = 1.0f - clamp_f(wpd->taa_sample / 4.0f, 0.0f, 1.0f); + wpd->taa_sample_inv = 1.0f / (wpd->taa_sample + 1); + + if (wpd->smaa_mix_factor > 0.0f) { + GPU_framebuffer_bind(fbl->smaa_edge_fb); + DRW_draw_pass(psl->aa_edge_ps); + + GPU_framebuffer_bind(fbl->smaa_weight_fb); + DRW_draw_pass(psl->aa_weight_ps); + } + + GPU_framebuffer_bind(dfbl->default_fb); + DRW_draw_pass(psl->aa_resolve_ps); + } + + wpd->taa_sample++; + + if (!DRW_state_is_image_render() && wpd->taa_sample < wpd->taa_sample_len) { + DRW_viewport_request_redraw(); + } +} diff --git a/source/blender/draw/engines/workbench/workbench_effect_cavity.c b/source/blender/draw/engines/workbench/workbench_effect_cavity.c new file mode 100644 index 00000000000..cdf8a93fc57 --- /dev/null +++ b/source/blender/draw/engines/workbench/workbench_effect_cavity.c @@ -0,0 +1,182 @@ +/* + * 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. + * + * Copyright 2020, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + * + * Cavity Effect: + * + * We use Screen Space Ambient Occlusion (SSAO) to enhance geometric details of the surfaces. + * We also use a Curvature effect computed only using the surface normals. + * + * This is done after the opaque pass. It only affects the opaque surfaces. + */ + +#include "DRW_render.h" + +#include "BLI_rand.h" + +#include "../eevee/eevee_lut.h" /* TODO find somewhere to share blue noise Table */ + +#include "workbench_engine.h" +#include "workbench_private.h" + +#define JITTER_TEX_SIZE 64 +#define CAVITY_MAX_SAMPLES 512 + +/* Using Hammersley distribution */ +static float *create_disk_samples(int num_samples, int num_iterations) +{ + const int total_samples = num_samples * num_iterations; + const float num_samples_inv = 1.0f / num_samples; + /* vec4 to ensure memory alignment. */ + float(*texels)[4] = MEM_callocN(sizeof(float[4]) * CAVITY_MAX_SAMPLES, __func__); + + for (int i = 0; i < total_samples; i++) { + float it_add = (i / num_samples) * 0.499f; + float r = fmodf((i + 0.5f + it_add) * num_samples_inv, 1.0f); + double dphi; + BLI_hammersley_1d(i, &dphi); + + float phi = (float)dphi * 2.0f * M_PI + it_add; + texels[i][0] = cosf(phi); + texels[i][1] = sinf(phi); + /* This deliberately distribute more samples + * at the center of the disk (and thus the shadow). */ + texels[i][2] = r; + } + + return (float *)texels; +} + +static struct GPUTexture *create_jitter_texture(int num_samples) +{ + float jitter[64 * 64][4]; + const float num_samples_inv = 1.0f / num_samples; + + for (int i = 0; i < 64 * 64; i++) { + float phi = blue_noise[i][0] * 2.0f * M_PI; + /* This rotate the sample per pixels */ + jitter[i][0] = cosf(phi); + jitter[i][1] = sinf(phi); + /* This offset the sample along it's direction axis (reduce banding) */ + float bn = blue_noise[i][1] - 0.5f; + CLAMP(bn, -0.499f, 0.499f); /* fix fireflies */ + jitter[i][2] = bn * num_samples_inv; + jitter[i][3] = blue_noise[i][1]; + } + + UNUSED_VARS(bsdf_split_sum_ggx, btdf_split_sum_ggx, ltc_mag_ggx, ltc_mat_ggx, ltc_disk_integral); + + return DRW_texture_create_2d(64, 64, GPU_RGBA16F, DRW_TEX_WRAP, &jitter[0][0]); +} + +BLI_INLINE int workbench_cavity_total_sample_count(const WORKBENCH_PrivateData *wpd, + const Scene *scene) +{ + return min_ii(max_ii(1, wpd->taa_sample_len) * scene->display.matcap_ssao_samples, + CAVITY_MAX_SAMPLES); +} + +void workbench_cavity_data_update(WORKBENCH_PrivateData *wpd, WORKBENCH_UBO_World *wd) +{ + View3DShading *shading = &wpd->shading; + const DRWContextState *draw_ctx = DRW_context_state_get(); + Scene *scene = draw_ctx->scene; + + if (CAVITY_ENABLED(wpd)) { + int cavity_sample_count_single_iteration = scene->display.matcap_ssao_samples; + int cavity_sample_count_total = workbench_cavity_total_sample_count(wpd, scene); + int max_iter_count = cavity_sample_count_total / cavity_sample_count_single_iteration; + + int sample = wpd->taa_sample % max_iter_count; + wd->cavity_sample_start = cavity_sample_count_single_iteration * sample; + wd->cavity_sample_end = cavity_sample_count_single_iteration * (sample + 1); + + wd->cavity_sample_count_inv = 1.0f / (wd->cavity_sample_end - wd->cavity_sample_start); + wd->cavity_jitter_scale = 1.0f / 64.0f; + + wd->cavity_valley_factor = shading->cavity_valley_factor; + wd->cavity_ridge_factor = shading->cavity_ridge_factor; + wd->cavity_attenuation = scene->display.matcap_ssao_attenuation; + wd->cavity_distance = scene->display.matcap_ssao_distance; + + wd->curvature_ridge = 0.5f / max_ff(square_f(shading->curvature_ridge_factor), 1e-4f); + wd->curvature_valley = 0.7f / max_ff(square_f(shading->curvature_valley_factor), 1e-4f); + } +} + +void workbench_cavity_samples_ubo_ensure(WORKBENCH_PrivateData *wpd) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + Scene *scene = draw_ctx->scene; + + int cavity_sample_count_single_iteration = scene->display.matcap_ssao_samples; + int cavity_sample_count = workbench_cavity_total_sample_count(wpd, scene); + + if (wpd->vldata->cavity_sample_count != cavity_sample_count) { + DRW_UBO_FREE_SAFE(wpd->vldata->cavity_sample_ubo); + DRW_TEXTURE_FREE_SAFE(wpd->vldata->cavity_jitter_tx); + } + + if (wpd->vldata->cavity_sample_ubo == NULL) { + float *samples = create_disk_samples(cavity_sample_count_single_iteration, + max_ii(1, wpd->taa_sample_len)); + wpd->vldata->cavity_jitter_tx = create_jitter_texture(cavity_sample_count); + /* NOTE: Uniform buffer needs to always be filled to be valid. */ + wpd->vldata->cavity_sample_ubo = DRW_uniformbuffer_create( + sizeof(float[4]) * CAVITY_MAX_SAMPLES, samples); + wpd->vldata->cavity_sample_count = cavity_sample_count; + MEM_freeN(samples); + } +} + +void workbench_cavity_cache_init(WORKBENCH_Data *data) +{ + WORKBENCH_PassList *psl = data->psl; + WORKBENCH_PrivateData *wpd = data->stl->wpd; + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + struct GPUShader *sh; + DRWShadingGroup *grp; + + if (CAVITY_ENABLED(wpd)) { + workbench_cavity_samples_ubo_ensure(wpd); + + int state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_MUL; + DRW_PASS_CREATE(psl->cavity_ps, state); + + sh = workbench_shader_cavity_get(SSAO_ENABLED(wpd), CURVATURE_ENABLED(wpd)); + + grp = DRW_shgroup_create(sh, psl->cavity_ps); + DRW_shgroup_uniform_texture(grp, "normalBuffer", wpd->normal_buffer_tx); + DRW_shgroup_uniform_block(grp, "samples_block", wpd->vldata->cavity_sample_ubo); + DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo); + + if (SSAO_ENABLED(wpd)) { + DRW_shgroup_uniform_texture(grp, "depthBuffer", dtxl->depth); + DRW_shgroup_uniform_texture(grp, "cavityJitter", wpd->vldata->cavity_jitter_tx); + } + if (CURVATURE_ENABLED(wpd)) { + DRW_shgroup_uniform_texture(grp, "objectIdBuffer", wpd->object_id_tx); + } + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } + else { + psl->cavity_ps = NULL; + } +} diff --git a/source/blender/draw/engines/workbench/workbench_effect_dof.c b/source/blender/draw/engines/workbench/workbench_effect_dof.c index ae3e2218463..8aa37c6b9fc 100644 --- a/source/blender/draw/engines/workbench/workbench_effect_dof.c +++ b/source/blender/draw/engines/workbench/workbench_effect_dof.c @@ -18,6 +18,17 @@ /** \file * \ingroup draw_engine + * + * Depth of Field Effect: + * + * We use a gather approach by sampling a lowres version of the color buffer. + * The process can be summarized like this: + * - downsample the color buffer using a COC (Circle of Confusion) aware downsample algo. + * - do a gather pass using the COC computed in the previous pass. + * - do a median filter to reduce noise amount. + * - composite on top of main color buffer. + * + * This is done after all passes and affects every surfaces. */ #include "workbench_private.h" @@ -27,24 +38,6 @@ #include "DNA_camera_types.h" -/* *********** STATIC *********** */ -static struct { - struct GPUShader *effect_dof_prepare_sh; - struct GPUShader *effect_dof_downsample_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 *********** */ - /** * Transform [-1..1] square to unit circle. */ @@ -130,52 +123,40 @@ static void workbench_dof_setup_samples(struct GPUUniformBuffer **ubo, DRW_uniformbuffer_update(*ubo, *data); } -void workbench_dof_engine_init(WORKBENCH_Data *vedata, Object *camera) +void workbench_dof_engine_init(WORKBENCH_Data *vedata) { WORKBENCH_TextureList *txl = vedata->txl; WORKBENCH_StorageList *stl = vedata->stl; - WORKBENCH_PrivateData *wpd = stl->g_data; + WORKBENCH_PrivateData *wpd = stl->wpd; WORKBENCH_FramebufferList *fbl = vedata->fbl; + const DRWContextState *draw_ctx = DRW_context_state_get(); + RegionView3D *rv3d = draw_ctx->rv3d; + View3D *v3d = draw_ctx->v3d; + Scene *scene = draw_ctx->scene; + Object *camera; + + if (v3d && rv3d) { + camera = (rv3d->persp == RV3D_CAMOB) ? v3d->camera : NULL; + } + else { + camera = scene->camera; + } + Camera *cam = camera != NULL ? camera->data : NULL; if ((wpd->shading.flag & V3D_SHADING_DEPTH_OF_FIELD) == 0 || (cam == NULL) || ((cam->dof.flag & CAM_DOF_ENABLED) == 0)) { 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_downsample_sh = DRW_shader_create_fullscreen( - datatoc_workbench_effect_dof_frag_glsl, "#define DOWNSAMPLE\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"); + /* Cleanup. */ + DRW_TEXTURE_FREE_SAFE(txl->dof_source_tx); + DRW_TEXTURE_FREE_SAFE(txl->coc_halfres_tx); + return; } const float *full_size = DRW_viewport_size_get(); int size[2] = {max_ii(1, (int)full_size[0] / 2), max_ii(1, (int)full_size[1] / 2)}; -#if 0 +#if 0 /* TODO(fclem) finish COC min_max optimisation */ /* 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)}; @@ -186,14 +167,14 @@ void workbench_dof_engine_init(WORKBENCH_Data *vedata, Object *camera) DRW_texture_ensure_2d( &txl->coc_halfres_tx, size[0], size[1], GPU_RG8, DRW_TEX_FILTER | DRW_TEX_MIPMAP); wpd->dof_blur_tx = DRW_texture_pool_query_2d( - size[0], size[1], GPU_RGBA16F, &draw_engine_workbench_solid); -#if 0 + size[0], size[1], GPU_RGBA16F, &draw_engine_workbench); +#if 0 /* TODO(fclem) finish COC min_max optimisation */ wpd->coc_temp_tx = DRW_texture_pool_query_2d( - shrink_h_size[0], shrink_h_size[1], GPU_RG8, &draw_engine_workbench_solid); + shrink_h_size[0], shrink_h_size[1], GPU_RG8, &draw_engine_workbench); wpd->coc_tiles_tx[0] = DRW_texture_pool_query_2d( - shrink_w_size[0], shrink_w_size[1], GPU_RG8, &draw_engine_workbench_solid); + shrink_w_size[0], shrink_w_size[1], GPU_RG8, &draw_engine_workbench); wpd->coc_tiles_tx[1] = DRW_texture_pool_query_2d( - shrink_w_size[0], shrink_w_size[1], GPU_RG8, &draw_engine_workbench_solid); + shrink_w_size[0], shrink_w_size[1], GPU_RG8, &draw_engine_workbench); #endif GPU_framebuffer_ensure_config(&fbl->dof_downsample_fb, @@ -202,7 +183,7 @@ void workbench_dof_engine_init(WORKBENCH_Data *vedata, Object *camera) GPU_ATTACHMENT_TEXTURE(txl->dof_source_tx), GPU_ATTACHMENT_TEXTURE(txl->coc_halfres_tx), }); -#if 0 +#if 0 /* TODO(fclem) finish COC min_max optimisation */ GPU_framebuffer_ensure_config(&fbl->dof_coc_tile_h_fb, { GPU_ATTACHMENT_NONE, @@ -231,11 +212,7 @@ void workbench_dof_engine_init(WORKBENCH_Data *vedata, Object *camera) }); { - const DRWContextState *draw_ctx = DRW_context_state_get(); - RegionView3D *rv3d = draw_ctx->rv3d; - /* Parameters */ - /* TODO UI Options */ float fstop = cam->dof.aperture_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); @@ -263,128 +240,125 @@ void workbench_dof_engine_init(WORKBENCH_Data *vedata, Object *camera) float rotation = cam->dof.aperture_rotation; float ratio = 1.0f / cam->dof.aperture_ratio; - if (wpd->dof_ubo == NULL || blades != wpd->dof_blades || rotation != wpd->dof_rotation || - ratio != wpd->dof_ratio) { + if (wpd->vldata->dof_sample_ubo == NULL || blades != wpd->dof_blades || + rotation != wpd->dof_rotation || ratio != wpd->dof_ratio) { wpd->dof_blades = blades; wpd->dof_rotation = rotation; wpd->dof_ratio = ratio; - workbench_dof_setup_samples(&wpd->dof_ubo, &stl->dof_ubo_data, blades, rotation, ratio); + workbench_dof_setup_samples( + &wpd->vldata->dof_sample_ubo, &stl->dof_ubo_data, blades, rotation, ratio); } } wpd->dof_enabled = true; } -void workbench_dof_create_pass(WORKBENCH_Data *vedata, - GPUTexture **dof_input, - GPUTexture *noise_tex) +void workbench_dof_cache_init(WORKBENCH_Data *vedata) { WORKBENCH_PassList *psl = vedata->psl; WORKBENCH_TextureList *txl = vedata->txl; WORKBENCH_StorageList *stl = vedata->stl; - WORKBENCH_PrivateData *wpd = stl->g_data; - struct GPUBatch *quad = DRW_cache_fullscreen_quad_get(); + WORKBENCH_PrivateData *wpd = stl->wpd; if (!wpd->dof_enabled) { return; } - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + GPUShader *prepare_sh, *downsample_sh, *blur1_sh, *blur2_sh, *resolve_sh; + workbench_shader_depth_of_field_get( + &prepare_sh, &downsample_sh, &blur1_sh, &blur2_sh, &resolve_sh); - psl->dof_down_ps = DRW_pass_create("DoF DownSample", DRW_STATE_WRITE_COLOR); - psl->dof_down2_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_CUSTOM); + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); { - DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_prepare_sh, psl->dof_down_ps); - DRW_shgroup_uniform_texture_ref(grp, "sceneColorTex", dof_input); + psl->dof_down_ps = DRW_pass_create("DoF DownSample", DRW_STATE_WRITE_COLOR); + + DRWShadingGroup *grp = DRW_shgroup_create(prepare_sh, psl->dof_down_ps); + DRW_shgroup_uniform_texture(grp, "sceneColorTex", dtxl->color); 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(grp, quad, NULL); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } { - DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_downsample_sh, psl->dof_down2_ps); + psl->dof_down2_ps = DRW_pass_create("DoF DownSample", DRW_STATE_WRITE_COLOR); + + DRWShadingGroup *grp = DRW_shgroup_create(downsample_sh, psl->dof_down2_ps); DRW_shgroup_uniform_texture(grp, "sceneColorTex", txl->dof_source_tx); DRW_shgroup_uniform_texture(grp, "inputCocTex", txl->coc_halfres_tx); - DRW_shgroup_call(grp, quad, NULL); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } -#if 0 +#if 0 /* TODO(fclem) finish COC min_max optimization */ { - DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_flatten_h_sh, - psl->dof_flatten_h_ps); + psl->dof_flatten_h_ps = DRW_pass_create("DoF Flatten Coc H", DRW_STATE_WRITE_COLOR); + + DRWShadingGroup *grp = DRW_shgroup_create(flatten_h_sh, psl->dof_flatten_h_ps); DRW_shgroup_uniform_texture(grp, "inputCocTex", txl->coc_halfres_tx); - DRW_shgroup_call(grp, quad, NULL); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } { - DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_flatten_v_sh, - psl->dof_flatten_v_ps); + psl->dof_flatten_v_ps = DRW_pass_create("DoF Flatten Coc V", DRW_STATE_WRITE_COLOR); + + DRWShadingGroup *grp = DRW_shgroup_create(flatten_v_sh, psl->dof_flatten_v_ps); DRW_shgroup_uniform_texture(grp, "inputCocTex", wpd->coc_temp_tx); - DRW_shgroup_call(grp, quad, NULL); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } { - DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_dilate_v_sh, psl->dof_dilate_v_ps); + psl->dof_dilate_h_ps = DRW_pass_create("DoF Dilate Coc H", DRW_STATE_WRITE_COLOR); + + DRWShadingGroup *grp = DRW_shgroup_create(dilate_v_sh, psl->dof_dilate_v_ps); DRW_shgroup_uniform_texture(grp, "inputCocTex", wpd->coc_tiles_tx[0]); - DRW_shgroup_call(grp, quad, NULL); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } { - DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_dilate_h_sh, psl->dof_dilate_h_ps); + psl->dof_dilate_v_ps = DRW_pass_create("DoF Dilate Coc V", DRW_STATE_WRITE_COLOR); + + DRWShadingGroup *grp = DRW_shgroup_create(dilate_h_sh, psl->dof_dilate_h_ps); DRW_shgroup_uniform_texture(grp, "inputCocTex", wpd->coc_tiles_tx[1]); - DRW_shgroup_call(grp, quad, NULL); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } #endif { - float offset = stl->effects->jitter_index / - (float)workbench_taa_calculate_num_iterations(vedata); - DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_blur1_sh, psl->dof_blur1_ps); - DRW_shgroup_uniform_block(grp, "dofSamplesBlock", wpd->dof_ubo); - DRW_shgroup_uniform_texture(grp, "noiseTex", noise_tex); + psl->dof_blur1_ps = DRW_pass_create("DoF Blur 1", DRW_STATE_WRITE_COLOR); + + /* We reuse the same noise texture. Ensure it is up to date. */ + workbench_cavity_samples_ubo_ensure(wpd); + + float offset = wpd->taa_sample / wpd->taa_sample_len; + DRWShadingGroup *grp = DRW_shgroup_create(blur1_sh, psl->dof_blur1_ps); + DRW_shgroup_uniform_block(grp, "dofSamplesBlock", wpd->vldata->dof_sample_ubo); + DRW_shgroup_uniform_texture(grp, "noiseTex", wpd->vldata->cavity_jitter_tx); DRW_shgroup_uniform_texture(grp, "inputCocTex", txl->coc_halfres_tx); DRW_shgroup_uniform_texture(grp, "halfResColorTex", txl->dof_source_tx); DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1); DRW_shgroup_uniform_float_copy(grp, "noiseOffset", offset); - DRW_shgroup_call(grp, quad, NULL); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } { - DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_blur2_sh, psl->dof_blur2_ps); + psl->dof_blur2_ps = DRW_pass_create("DoF Blur 2", DRW_STATE_WRITE_COLOR); + + DRWShadingGroup *grp = DRW_shgroup_create(blur2_sh, psl->dof_blur2_ps); DRW_shgroup_uniform_texture(grp, "inputCocTex", txl->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(grp, quad, NULL); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } { - DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_dof_resolve_sh, psl->dof_resolve_ps); + psl->dof_resolve_ps = DRW_pass_create("DoF Resolve", + DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM); + + DRWShadingGroup *grp = DRW_shgroup_create(resolve_sh, psl->dof_resolve_ps); DRW_shgroup_uniform_texture(grp, "halfResColorTex", txl->dof_source_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(grp, quad, NULL); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } } -void workbench_dof_engine_free(void) -{ - DRW_SHADER_FREE_SAFE(e_data.effect_dof_prepare_sh); - DRW_SHADER_FREE_SAFE(e_data.effect_dof_downsample_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); -} - static void workbench_dof_downsample_level(void *userData, int UNUSED(level)) { WORKBENCH_PassList *psl = (WORKBENCH_PassList *)userData; @@ -396,7 +370,8 @@ 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; + WORKBENCH_PrivateData *wpd = stl->wpd; + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); if (!wpd->dof_enabled) { return; @@ -410,7 +385,7 @@ void workbench_dof_draw_pass(WORKBENCH_Data *vedata) GPU_framebuffer_recursive_downsample( fbl->dof_downsample_fb, 2, workbench_dof_downsample_level, psl); -#if 0 +#if 0 /* TODO(fclem) finish COC min_max optimization */ GPU_framebuffer_bind(fbl->dof_coc_tile_h_fb); DRW_draw_pass(psl->dof_flatten_h_ps); @@ -430,7 +405,7 @@ void workbench_dof_draw_pass(WORKBENCH_Data *vedata) GPU_framebuffer_bind(fbl->dof_blur2_fb); DRW_draw_pass(psl->dof_blur2_ps); - GPU_framebuffer_bind(fbl->color_only_fb); + GPU_framebuffer_bind(dfbl->color_only_fb); DRW_draw_pass(psl->dof_resolve_ps); DRW_stats_group_end(); diff --git a/source/blender/draw/engines/workbench/workbench_effect_fxaa.c b/source/blender/draw/engines/workbench/workbench_effect_fxaa.c deleted file mode 100644 index 6e3bf1658ab..00000000000 --- a/source/blender/draw/engines/workbench/workbench_effect_fxaa.c +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - */ -#include "workbench_private.h" - -/* *********** STATIC *********** */ -static struct { - struct GPUShader *effect_fxaa_sh; -} e_data = {NULL}; - -/* Shaders */ -extern char datatoc_common_fxaa_lib_glsl[]; -extern char datatoc_common_fullscreen_vert_glsl[]; -extern char datatoc_workbench_effect_fxaa_frag_glsl[]; - -/* *********** Functions *********** */ -void workbench_fxaa_engine_init(void) -{ - if (e_data.effect_fxaa_sh == NULL) { - e_data.effect_fxaa_sh = DRW_shader_create_with_lib(datatoc_common_fullscreen_vert_glsl, - NULL, - datatoc_workbench_effect_fxaa_frag_glsl, - datatoc_common_fxaa_lib_glsl, - NULL); - } -} - -DRWPass *workbench_fxaa_create_pass(GPUTexture **color_buffer_tx) -{ - DRWPass *pass = DRW_pass_create("Effect FXAA", DRW_STATE_WRITE_COLOR); - DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_fxaa_sh, pass); - DRW_shgroup_uniform_texture_ref(grp, "colorBuffer", color_buffer_tx); - DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1); - DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); - return pass; -} - -void workbench_fxaa_engine_free(void) -{ - DRW_SHADER_FREE_SAFE(e_data.effect_fxaa_sh); -} diff --git a/source/blender/draw/engines/workbench/workbench_effect_outline.c b/source/blender/draw/engines/workbench/workbench_effect_outline.c new file mode 100644 index 00000000000..d1bc6b6c435 --- /dev/null +++ b/source/blender/draw/engines/workbench/workbench_effect_outline.c @@ -0,0 +1,55 @@ +/* + * 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. + * + * Copyright 2020, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + * + * Outline Effect: + * + * Simple effect that just samples an object id buffer to detect objects outlines. + */ + +#include "DRW_render.h" + +#include "workbench_engine.h" +#include "workbench_private.h" + +void workbench_outline_cache_init(WORKBENCH_Data *data) +{ + WORKBENCH_PassList *psl = data->psl; + WORKBENCH_PrivateData *wpd = data->stl->wpd; + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + struct GPUShader *sh; + DRWShadingGroup *grp; + + if (OBJECT_OUTLINE_ENABLED(wpd)) { + int state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA_PREMUL; + DRW_PASS_CREATE(psl->outline_ps, state); + + sh = workbench_shader_outline_get(); + + grp = DRW_shgroup_create(sh, psl->outline_ps); + DRW_shgroup_uniform_texture(grp, "objectIdBuffer", wpd->object_id_tx); + DRW_shgroup_uniform_texture(grp, "depthBuffer", dtxl->depth); + DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } + else { + psl->outline_ps = NULL; + } +} diff --git a/source/blender/draw/engines/workbench/workbench_effect_taa.c b/source/blender/draw/engines/workbench/workbench_effect_taa.c deleted file mode 100644 index e2864f8c832..00000000000 --- a/source/blender/draw/engines/workbench/workbench_effect_taa.c +++ /dev/null @@ -1,305 +0,0 @@ -/* - * 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. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - */ - -#include "workbench_private.h" -#include "BLI_jitter_2d.h" - -static struct { - struct GPUShader *effect_taa_sh; - float jitter_5[5][2]; - float jitter_8[8][2]; - float jitter_11[11][2]; - float jitter_16[16][2]; - float jitter_32[32][2]; -} e_data = {NULL}; - -extern char datatoc_workbench_effect_taa_frag_glsl[]; - -static void workbench_taa_jitter_init_order(float (*table)[2], int num) -{ - BLI_jitter_init(table, num); - - /* find closest element to center */ - int closest_index = 0; - float closest_squared_distance = 1.0f; - - for (int index = 0; index < num; index++) { - const float squared_dist = square_f(table[index][0]) + square_f(table[index][1]); - if (squared_dist < closest_squared_distance) { - closest_squared_distance = squared_dist; - closest_index = index; - } - } - - /* move jitter table so that closest sample is in center */ - for (int index = 0; index < num; index++) { - sub_v2_v2(table[index], table[closest_index]); - mul_v2_fl(table[index], 2.0f); - } - - /* swap center sample to the start of the table */ - if (closest_index != 0) { - swap_v2_v2(table[0], table[closest_index]); - } - - /* sort list based on furtest distance with previous */ - for (int i = 0; i < num - 2; i++) { - float f_squared_dist = 0.0; - int f_index = i; - for (int j = i + 1; j < num; j++) { - const float squared_dist = square_f(table[i][0] - table[j][0]) + - square_f(table[i][1] - table[j][1]); - if (squared_dist > f_squared_dist) { - f_squared_dist = squared_dist; - f_index = j; - } - } - swap_v2_v2(table[i + 1], table[f_index]); - } -} - -static void workbench_taa_jitter_init(void) -{ - workbench_taa_jitter_init_order(e_data.jitter_5, 5); - workbench_taa_jitter_init_order(e_data.jitter_8, 8); - workbench_taa_jitter_init_order(e_data.jitter_11, 11); - workbench_taa_jitter_init_order(e_data.jitter_16, 16); - workbench_taa_jitter_init_order(e_data.jitter_32, 32); -} - -int workbench_taa_calculate_num_iterations(WORKBENCH_Data *vedata) -{ - WORKBENCH_StorageList *stl = vedata->stl; - WORKBENCH_PrivateData *wpd = stl->g_data; - const Scene *scene = DRW_context_state_get()->scene; - int result; - if (workbench_is_taa_enabled(wpd)) { - if (DRW_state_is_image_render()) { - const DRWContextState *draw_ctx = DRW_context_state_get(); - if (draw_ctx->v3d) { - result = scene->display.viewport_aa; - } - else { - result = scene->display.render_aa; - } - } - else { - result = wpd->preferences->viewport_aa; - } - } - else { - /* when no TAA is disabled return 1 to render a single sample - * see `workbench_render.c` */ - result = 1; - } - return result; -} - -int workbench_num_viewport_rendering_iterations(WORKBENCH_Data *UNUSED(vedata)) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - const Scene *scene = draw_ctx->scene; - int result = DRW_state_is_image_render() ? scene->display.viewport_aa : 1; - result = MAX2(result, 1); - return result; -} - -void workbench_taa_engine_init(WORKBENCH_Data *vedata) -{ - WORKBENCH_EffectInfo *effect_info = vedata->stl->effects; - const DRWContextState *draw_ctx = DRW_context_state_get(); - RegionView3D *rv3d = draw_ctx->rv3d; - - if (e_data.effect_taa_sh == NULL) { - e_data.effect_taa_sh = DRW_shader_create_fullscreen(datatoc_workbench_effect_taa_frag_glsl, - NULL); - workbench_taa_jitter_init(); - } - - effect_info->view = NULL; - - /* reset complete drawing when navigating. */ - if (effect_info->jitter_index != 0) { - if (rv3d && rv3d->rflag & (RV3D_NAVIGATING | RV3D_PAINTING)) { - effect_info->jitter_index = 0; - } - } - - if (effect_info->view_updated) { - effect_info->jitter_index = 0; - effect_info->view_updated = false; - } - - { - float persmat[4][4]; - DRW_view_persmat_get(NULL, persmat, false); - if (!equals_m4m4(persmat, effect_info->last_mat)) { - copy_m4_m4(effect_info->last_mat, persmat); - effect_info->jitter_index = 0; - } - } -} - -void workbench_taa_engine_free(void) -{ - DRW_SHADER_FREE_SAFE(e_data.effect_taa_sh); -} - -DRWPass *workbench_taa_create_pass(WORKBENCH_Data *vedata, GPUTexture **color_buffer_tx) -{ - WORKBENCH_StorageList *stl = vedata->stl; - WORKBENCH_TextureList *txl = vedata->txl; - WORKBENCH_EffectInfo *effect_info = stl->effects; - WORKBENCH_FramebufferList *fbl = vedata->fbl; - const WORKBENCH_PrivateData *wpd = stl->g_data; - - { - const eGPUTextureFormat hist_buffer_format = workbench_color_texture_format(wpd); - DRW_texture_ensure_fullscreen_2d(&txl->history_buffer_tx, hist_buffer_format, 0); - DRW_texture_ensure_fullscreen_2d(&txl->depth_buffer_tx, GPU_DEPTH24_STENCIL8, 0); - } - - { - GPU_framebuffer_ensure_config(&fbl->effect_taa_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(txl->history_buffer_tx), - }); - GPU_framebuffer_ensure_config(&fbl->depth_buffer_fb, - { - GPU_ATTACHMENT_TEXTURE(txl->depth_buffer_tx), - }); - } - - DRWPass *pass = DRW_pass_create("Effect TAA", DRW_STATE_WRITE_COLOR); - DRWShadingGroup *grp = DRW_shgroup_create(e_data.effect_taa_sh, pass); - DRW_shgroup_uniform_texture_ref(grp, "colorBuffer", color_buffer_tx); - DRW_shgroup_uniform_texture_ref(grp, "historyBuffer", &txl->history_buffer_tx); - DRW_shgroup_uniform_float(grp, "mixFactor", &effect_info->taa_mix_factor, 1); - DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); - - return pass; -} - -void workbench_taa_draw_scene_start(WORKBENCH_Data *vedata) -{ - WORKBENCH_StorageList *stl = vedata->stl; - WORKBENCH_EffectInfo *effect_info = stl->effects; - WORKBENCH_PrivateData *wpd = stl->g_data; - - const float *viewport_size = DRW_viewport_size_get(); - const DRWView *default_view = DRW_view_default_get(); - int num_samples = 8; - float(*samples)[2]; - - num_samples = workbench_taa_calculate_num_iterations(vedata); - switch (num_samples) { - default: - case 5: - samples = e_data.jitter_5; - break; - case 8: - samples = e_data.jitter_8; - break; - case 11: - samples = e_data.jitter_11; - break; - case 16: - samples = e_data.jitter_16; - break; - case 32: - samples = e_data.jitter_32; - break; - } - - const int jitter_index = effect_info->jitter_index; - const float *transform_offset = samples[jitter_index]; - effect_info->taa_mix_factor = 1.0f / (jitter_index + 1); - effect_info->jitter_index = (jitter_index + 1) % num_samples; - /* Copy jitter index to Cavity iteration */ - wpd->ssao_params[3] = effect_info->jitter_index; - - /* construct new matrices from transform delta */ - float winmat[4][4], viewmat[4][4], persmat[4][4]; - DRW_view_winmat_get(default_view, winmat, false); - DRW_view_viewmat_get(default_view, viewmat, false); - DRW_view_persmat_get(default_view, persmat, false); - - window_translate_m4(winmat, - persmat, - transform_offset[0] / viewport_size[0], - transform_offset[1] / viewport_size[1]); - - if (effect_info->view) { - /* When rendering just update the view. This avoids recomputing the culling. */ - DRW_view_update_sub(effect_info->view, viewmat, winmat); - } - else { - /* TAA is not making a big change to the matrices. - * Reuse the main view culling by creating a subview. */ - effect_info->view = DRW_view_create_sub(default_view, viewmat, winmat); - } - DRW_view_set_active(effect_info->view); -} - -void workbench_taa_draw_scene_end(WORKBENCH_Data *vedata) -{ - /* - * If first frame then the offset is 0.0 and its depth is the depth buffer to use - * for the rest of the draw engines. We store it in a persistent buffer. - * - * If it is not the first frame we copy the persistent buffer back to the - * default depth buffer - */ - const WORKBENCH_StorageList *stl = vedata->stl; - const WORKBENCH_FramebufferList *fbl = vedata->fbl; - const DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); - WORKBENCH_EffectInfo *effect_info = stl->effects; - - if (effect_info->jitter_index == 1) { - GPU_framebuffer_blit(dfbl->depth_only_fb, 0, fbl->depth_buffer_fb, 0, GPU_DEPTH_BIT); - } - else { - GPU_framebuffer_blit(fbl->depth_buffer_fb, 0, dfbl->depth_only_fb, 0, GPU_DEPTH_BIT); - } - - GPU_framebuffer_blit(dfbl->color_only_fb, 0, fbl->effect_taa_fb, 0, GPU_COLOR_BIT); - - if (!DRW_state_is_image_render()) { - DRW_view_set_active(NULL); - } - - if (effect_info->jitter_index != 0 && !DRW_state_is_image_render()) { - DRW_viewport_request_redraw(); - } -} - -void workbench_taa_view_updated(WORKBENCH_Data *vedata) -{ - WORKBENCH_StorageList *stl = vedata->stl; - if (stl) { - WORKBENCH_EffectInfo *effect_info = stl->effects; - if (effect_info) { - effect_info->view_updated = true; - } - } -} diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c index 62a192bbb25..b91ca7cf070 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.c +++ b/source/blender/draw/engines/workbench/workbench_engine.c @@ -19,18 +19,557 @@ /** \file * \ingroup draw_engine * - * Simple engine for drawing color and/or depth. - * When we only need simple flat shaders. + * Workbench Engine: + * + * Optimized engine to draw the working viewport with solid and transparent geometry. */ #include "DRW_render.h" +#include "BLI_alloca.h" + +#include "BKE_modifier.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_particle.h" + +#include "DNA_image_types.h" +#include "DNA_fluid_types.h" +#include "DNA_mesh_types.h" +#include "DNA_modifier_types.h" +#include "DNA_node_types.h" + #include "workbench_engine.h" #include "workbench_private.h" #define WORKBENCH_ENGINE "BLENDER_WORKBENCH" -/* Note: currently unused, we may want to register so we can see this when debugging the view. */ +void workbench_engine_init(void *ved) +{ + WORKBENCH_Data *vedata = ved; + WORKBENCH_StorageList *stl = vedata->stl; + WORKBENCH_TextureList *txl = vedata->txl; + + workbench_shader_library_ensure(); + + if (!stl->wpd) { + stl->wpd = MEM_callocN(sizeof(*stl->wpd), __func__); + stl->wpd->view_updated = true; + } + + WORKBENCH_PrivateData *wpd = stl->wpd; + workbench_private_data_init(wpd); + workbench_update_world_ubo(wpd); + + if (txl->dummy_image_tx == NULL) { + float fpixel[4] = {1.0f, 0.0f, 1.0f, 1.0f}; + txl->dummy_image_tx = DRW_texture_create_2d(1, 1, GPU_RGBA8, 0, fpixel); + } + wpd->dummy_image_tx = txl->dummy_image_tx; + + if (OBJECT_ID_PASS_ENABLED(wpd)) { + wpd->object_id_tx = DRW_texture_pool_query_fullscreen(GPU_R16UI, &draw_engine_workbench); + } + else { + /* Dont free because it's a pool texture. */ + wpd->object_id_tx = NULL; + } + + workbench_opaque_engine_init(vedata); + workbench_transparent_engine_init(vedata); + workbench_dof_engine_init(vedata); + workbench_antialiasing_engine_init(vedata); + workbench_volume_engine_init(vedata); +} + +void workbench_cache_init(void *ved) +{ + WORKBENCH_Data *vedata = ved; + + workbench_opaque_cache_init(vedata); + workbench_transparent_cache_init(vedata); + workbench_shadow_cache_init(vedata); + workbench_cavity_cache_init(vedata); + workbench_outline_cache_init(vedata); + workbench_dof_cache_init(vedata); + workbench_antialiasing_cache_init(vedata); + workbench_volume_cache_init(vedata); +} + +/* TODO(fclem) DRW_cache_object_surface_material_get needs a refactor to allow passing NULL + * instead of gpumat_array. Avoiding all this boilerplate code. */ +static struct GPUBatch **workbench_object_surface_material_get(Object *ob) +{ + const int materials_len = DRW_cache_object_material_count_get(ob); + struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len); + memset(gpumat_array, 0, sizeof(*gpumat_array) * materials_len); + + return DRW_cache_object_surface_material_get(ob, gpumat_array, materials_len); +} + +static void workbench_cache_sculpt_populate(WORKBENCH_PrivateData *wpd, + Object *ob, + eV3DShadingColorType color_type) +{ + const bool use_vcol = ELEM(color_type, V3D_SHADING_VERTEX_COLOR); + const bool use_single_drawcall = !ELEM(color_type, V3D_SHADING_MATERIAL_COLOR); + BLI_assert(wpd->shading.color_type != V3D_SHADING_TEXTURE_COLOR); + + if (use_single_drawcall) { + DRWShadingGroup *grp = workbench_material_setup(wpd, ob, 0, color_type, NULL); + DRW_shgroup_call_sculpt(grp, ob, false, false, use_vcol); + } + else { + const int materials_len = DRW_cache_object_material_count_get(ob); + struct DRWShadingGroup **shgrps = BLI_array_alloca(shgrps, materials_len); + for (int i = 0; i < materials_len; i++) { + shgrps[i] = workbench_material_setup(wpd, ob, i + 1, color_type, NULL); + } + DRW_shgroup_call_sculpt_with_materials(shgrps, ob, false); + } +} + +static void workbench_cache_texpaint_populate(WORKBENCH_PrivateData *wpd, Object *ob) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + const Scene *scene = draw_ctx->scene; + const ImagePaintSettings *imapaint = &scene->toolsettings->imapaint; + const bool use_single_drawcall = imapaint->mode == IMAGEPAINT_MODE_IMAGE; + + if (use_single_drawcall) { + struct GPUBatch *geom = DRW_cache_mesh_surface_texpaint_single_get(ob); + if (geom) { + Image *ima = imapaint->canvas; + int interp = (imapaint->interp == IMAGEPAINT_INTERP_LINEAR) ? SHD_INTERP_LINEAR : + SHD_INTERP_CLOSEST; + + DRWShadingGroup *grp = workbench_image_setup(wpd, ob, 0, ima, NULL, interp); + DRW_shgroup_call(grp, geom, ob); + } + } + else { + struct GPUBatch **geoms = DRW_cache_mesh_surface_texpaint_get(ob); + if (geoms) { + const int materials_len = DRW_cache_object_material_count_get(ob); + for (int i = 0; i < materials_len; i++) { + DRWShadingGroup *grp = workbench_image_setup(wpd, ob, i + 1, NULL, NULL, 0); + DRW_shgroup_call(grp, geoms[i], ob); + } + } + } +} + +static void workbench_cache_common_populate(WORKBENCH_PrivateData *wpd, + Object *ob, + eV3DShadingColorType color_type, + bool *r_transp) +{ + const bool use_tex = ELEM(color_type, V3D_SHADING_TEXTURE_COLOR); + const bool use_vcol = ELEM(color_type, V3D_SHADING_VERTEX_COLOR); + const bool use_single_drawcall = !ELEM( + color_type, V3D_SHADING_MATERIAL_COLOR, V3D_SHADING_TEXTURE_COLOR); + + if (use_single_drawcall) { + struct GPUBatch *geom = (use_vcol) ? DRW_cache_mesh_surface_vertpaint_get(ob) : + DRW_cache_object_surface_get(ob); + if (geom) { + DRWShadingGroup *grp = workbench_material_setup(wpd, ob, 0, color_type, r_transp); + DRW_shgroup_call(grp, geom, ob); + } + } + else { + struct GPUBatch **geoms = (use_tex) ? DRW_cache_mesh_surface_texpaint_get(ob) : + workbench_object_surface_material_get(ob); + if (geoms) { + const int materials_len = DRW_cache_object_material_count_get(ob); + for (int i = 0; i < materials_len; i++) { + DRWShadingGroup *grp = workbench_material_setup(wpd, ob, i + 1, color_type, r_transp); + DRW_shgroup_call(grp, geoms[i], ob); + } + } + } +} + +static void workbench_cache_hair_populate(WORKBENCH_PrivateData *wpd, + Object *ob, + ModifierData *md, + eV3DShadingColorType color_type, + bool use_texpaint_mode) +{ + ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys; + ParticleSettings *part = psys->part; + const DRWContextState *draw_ctx = DRW_context_state_get(); + const Scene *scene = draw_ctx->scene; + + const ImagePaintSettings *imapaint = use_texpaint_mode ? &scene->toolsettings->imapaint : NULL; + Image *ima = (imapaint && imapaint->mode == IMAGEPAINT_MODE_IMAGE) ? imapaint->canvas : NULL; + int interp = (imapaint && imapaint->interp == IMAGEPAINT_INTERP_LINEAR) ? SHD_INTERP_LINEAR : + SHD_INTERP_CLOSEST; + DRWShadingGroup *grp = (use_texpaint_mode) ? + workbench_image_hair_setup(wpd, ob, part->omat, ima, NULL, interp) : + workbench_material_hair_setup(wpd, ob, part->omat, color_type); + + DRW_shgroup_hair_create_sub(ob, psys, md, grp); +} + +/* Decide what colortype to draw the object with. + * In some cases it can be overwritten by workbench_material_setup(). */ +static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd, + Object *ob, + bool *r_sculpt_pbvh, + bool *r_texpaint_mode, + bool *r_draw_shadow) +{ + eV3DShadingColorType color_type = wpd->shading.color_type; + const Mesh *me = (ob->type == OB_MESH) ? ob->data : NULL; + + const DRWContextState *draw_ctx = DRW_context_state_get(); + const bool is_active = (ob == draw_ctx->obact); + const bool is_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) && + !DRW_state_is_image_render(); + const bool is_render = DRW_state_is_image_render() && (draw_ctx->v3d == NULL); + const bool is_texpaint_mode = is_active && (wpd->ctx_mode == CTX_MODE_PAINT_TEXTURE); + const bool is_vertpaint_mode = is_active && (wpd->ctx_mode == CTX_MODE_PAINT_VERTEX); + + if ((color_type == V3D_SHADING_TEXTURE_COLOR) && (ob->dt < OB_TEXTURE)) { + color_type = V3D_SHADING_MATERIAL_COLOR; + } + /* Disable color mode if data layer is unavailable. */ + if ((color_type == V3D_SHADING_TEXTURE_COLOR) && (me == NULL || me->mloopuv == NULL)) { + color_type = V3D_SHADING_MATERIAL_COLOR; + } + if ((color_type == V3D_SHADING_VERTEX_COLOR) && (me == NULL || me->mloopcol == NULL)) { + color_type = V3D_SHADING_OBJECT_COLOR; + } + + *r_sculpt_pbvh = is_sculpt_pbvh; + *r_texpaint_mode = false; + + if (!is_sculpt_pbvh && !is_render) { + /* Force texture or vertex mode if object is in paint mode. */ + if (is_texpaint_mode && me && me->mloopuv) { + color_type = V3D_SHADING_TEXTURE_COLOR; + *r_texpaint_mode = true; + } + else if (is_vertpaint_mode && me && me->mloopcol) { + color_type = V3D_SHADING_VERTEX_COLOR; + } + } + + if (r_draw_shadow) { + *r_draw_shadow = (ob->dtx & OB_DRAW_NO_SHADOW_CAST) == 0 && SHADOW_ENABLED(wpd); + /* Currently unsupported in sculpt mode. We could revert to the slow + * method in this case but I'm not sure if it's a good idea given that + * sculpted meshes are heavy to begin with. */ + if (is_sculpt_pbvh) { + *r_draw_shadow = false; + } + + if (is_active && DRW_object_use_hide_faces(ob)) { + *r_draw_shadow = false; + } + } + + return color_type; +} + +void workbench_cache_populate(void *ved, Object *ob) +{ + WORKBENCH_Data *vedata = ved; + WORKBENCH_StorageList *stl = vedata->stl; + WORKBENCH_PrivateData *wpd = stl->wpd; + + if (!DRW_object_is_renderable(ob)) { + return; + } + + if (ob->type == OB_MESH && ob->modifiers.first != NULL) { + bool use_sculpt_pbvh, use_texpaint_mode; + int color_type = workbench_color_type_get(wpd, ob, &use_sculpt_pbvh, &use_texpaint_mode, NULL); + + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type != eModifierType_ParticleSystem) { + continue; + } + ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys; + if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) { + continue; + } + ParticleSettings *part = psys->part; + const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as; + + if (draw_as == PART_DRAW_PATH) { + workbench_cache_hair_populate(wpd, ob, md, color_type, use_texpaint_mode); + } + } + } + + if (!(ob->base_flag & BASE_FROM_DUPLI)) { + ModifierData *md = modifiers_findByType(ob, eModifierType_Fluid); + if (md && modifier_isEnabled(wpd->scene, md, eModifierMode_Realtime)) { + FluidModifierData *fmd = (FluidModifierData *)md; + if (fmd->domain && fmd->domain->type == FLUID_DOMAIN_TYPE_GAS) { + workbench_volume_cache_populate(vedata, wpd->scene, ob, md); + return; /* Do not draw solid in this case. */ + } + } + } + + if (!(DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF)) { + return; + } + + if ((ob->dt < OB_SOLID) && !DRW_state_is_scene_render()) { + return; + } + + if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) { + bool use_sculpt_pbvh, use_texpaint_mode, draw_shadow, has_transp_mat = false; + eV3DShadingColorType color_type = workbench_color_type_get( + wpd, ob, &use_sculpt_pbvh, &use_texpaint_mode, &draw_shadow); + + if (use_sculpt_pbvh) { + workbench_cache_sculpt_populate(wpd, ob, color_type); + } + else if (use_texpaint_mode) { + workbench_cache_texpaint_populate(wpd, ob); + } + else { + workbench_cache_common_populate(wpd, ob, color_type, &has_transp_mat); + } + + if (draw_shadow) { + workbench_shadow_cache_populate(vedata, ob, has_transp_mat); + } + } +} + +void workbench_cache_finish(void *ved) +{ + WORKBENCH_Data *vedata = ved; + WORKBENCH_StorageList *stl = vedata->stl; + WORKBENCH_FramebufferList *fbl = vedata->fbl; + WORKBENCH_PrivateData *wpd = stl->wpd; + + /* TODO(fclem) Only do this when really needed. */ + { + /* HACK we allocate the infront depth here to avoid the overhead when if is not needed. */ + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + + DRW_texture_ensure_fullscreen_2d(&dtxl->depth_in_front, GPU_DEPTH24_STENCIL8, 0); + + GPU_framebuffer_ensure_config(&dfbl->in_front_fb, + { + GPU_ATTACHMENT_TEXTURE(dtxl->depth_in_front), + GPU_ATTACHMENT_TEXTURE(dtxl->color), + }); + + GPU_framebuffer_ensure_config(&fbl->opaque_infront_fb, + { + GPU_ATTACHMENT_TEXTURE(dtxl->depth_in_front), + GPU_ATTACHMENT_TEXTURE(wpd->material_buffer_tx), + GPU_ATTACHMENT_TEXTURE(wpd->normal_buffer_tx), + GPU_ATTACHMENT_TEXTURE(wpd->object_id_tx), + }); + + GPU_framebuffer_ensure_config(&fbl->transp_accum_infront_fb, + { + GPU_ATTACHMENT_TEXTURE(dtxl->depth_in_front), + GPU_ATTACHMENT_TEXTURE(wpd->accum_buffer_tx), + GPU_ATTACHMENT_TEXTURE(wpd->reveal_buffer_tx), + }); + } + + if (wpd->object_id_tx) { + GPU_framebuffer_ensure_config(&fbl->id_clear_fb, + { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(wpd->object_id_tx), + }); + } + else { + GPU_FRAMEBUFFER_FREE_SAFE(fbl->id_clear_fb); + } + + workbench_update_material_ubos(wpd); + + /* TODO don't free reuse next redraw. */ + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 2; k++) { + if (wpd->prepass[i][j][k].material_hash) { + BLI_ghash_free(wpd->prepass[i][j][k].material_hash, NULL, NULL); + wpd->prepass[i][j][k].material_hash = NULL; + } + } + } + } +} + +/* Used by viewport rendering & final rendering. + * Do one render loop iteration (i.e: One TAA sample). */ +void workbench_draw_sample(void *ved) +{ + WORKBENCH_Data *vedata = ved; + WORKBENCH_FramebufferList *fbl = vedata->fbl; + WORKBENCH_PrivateData *wpd = vedata->stl->wpd; + WORKBENCH_PassList *psl = vedata->psl; + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + float clear_col_with_alpha[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + + const bool do_render = workbench_antialiasing_setup(vedata); + const bool xray_is_visible = wpd->shading.xray_alpha > 0.0f; + const bool do_transparent_infront_pass = !DRW_pass_is_empty(psl->transp_accum_infront_ps); + const bool do_transparent_pass = !DRW_pass_is_empty(psl->transp_accum_ps); + const bool do_opaque_infront_pass = !DRW_pass_is_empty(psl->opaque_infront_ps); + const bool do_opaque_pass = !DRW_pass_is_empty(psl->opaque_ps) || do_opaque_infront_pass; + + if (dfbl->in_front_fb) { + GPU_framebuffer_bind(dfbl->in_front_fb); + GPU_framebuffer_clear_depth(dfbl->in_front_fb, 1.0f); + } + + if (do_render) { + GPU_framebuffer_bind(dfbl->default_fb); + GPU_framebuffer_clear_color_depth_stencil(dfbl->default_fb, wpd->background_color, 1.0f, 0x00); + + if (fbl->id_clear_fb) { + GPU_framebuffer_bind(fbl->id_clear_fb); + GPU_framebuffer_clear_color(fbl->id_clear_fb, clear_col); + } + + if (do_opaque_pass) { + GPU_framebuffer_bind(fbl->opaque_fb); + DRW_draw_pass(psl->opaque_ps); + + if (psl->shadow_ps[0]) { + DRW_draw_pass(psl->shadow_ps[0]); + DRW_draw_pass(psl->shadow_ps[1]); + } + + if (do_opaque_infront_pass) { + GPU_framebuffer_bind(fbl->opaque_infront_fb); + DRW_draw_pass(psl->opaque_infront_ps); + + GPU_framebuffer_bind(fbl->opaque_fb); + DRW_draw_pass(psl->merge_infront_ps); + } + + GPU_framebuffer_bind(dfbl->default_fb); + DRW_draw_pass(psl->composite_ps); + + if (psl->cavity_ps) { + GPU_framebuffer_bind(dfbl->color_only_fb); + DRW_draw_pass(psl->cavity_ps); + } + } + + workbench_volume_draw_pass(vedata); + + if (xray_is_visible) { + if (do_transparent_pass) { + GPU_framebuffer_bind(fbl->transp_accum_fb); + GPU_framebuffer_clear_color(fbl->transp_accum_fb, clear_col_with_alpha); + + DRW_draw_pass(psl->transp_accum_ps); + + GPU_framebuffer_bind(dfbl->color_only_fb); + DRW_draw_pass(psl->transp_resolve_ps); + } + + if (do_transparent_infront_pass) { + GPU_framebuffer_bind(fbl->transp_accum_infront_fb); + GPU_framebuffer_clear_color(fbl->transp_accum_infront_fb, clear_col_with_alpha); + + DRW_draw_pass(psl->transp_accum_infront_ps); + + GPU_framebuffer_bind(dfbl->color_only_fb); + DRW_draw_pass(psl->transp_resolve_ps); + } + } + + workbench_transparent_draw_depth_pass(vedata); + + if (psl->outline_ps) { + GPU_framebuffer_bind(dfbl->color_only_fb); + DRW_draw_pass(psl->outline_ps); + } + + workbench_dof_draw_pass(vedata); + } + + workbench_antialiasing_draw_pass(vedata); +} + +/* Viewport rendering. */ +static void workbench_draw_scene(void *ved) +{ + WORKBENCH_Data *vedata = ved; + WORKBENCH_PrivateData *wpd = vedata->stl->wpd; + + if (DRW_state_is_opengl_render()) { + while (wpd->taa_sample < max_ii(1, wpd->taa_sample_len)) { + workbench_update_world_ubo(wpd); + + workbench_draw_sample(vedata); + } + } + else { + workbench_draw_sample(vedata); + } + + workbench_draw_finish(vedata); +} + +void workbench_draw_finish(void *ved) +{ + WORKBENCH_Data *vedata = ved; + workbench_volume_draw_finish(vedata); +} + +static void workbench_engine_free(void) +{ + workbench_shader_free(); +} + +static void workbench_view_update(void *vedata) +{ + WORKBENCH_Data *data = vedata; + workbench_antialiasing_view_updated(data); +} + +static void workbench_id_update(void *UNUSED(vedata), struct ID *id) +{ + if (GS(id->name) == ID_OB) { + WORKBENCH_ObjectData *oed = (WORKBENCH_ObjectData *)DRW_drawdata_get(id, + &draw_engine_workbench); + if (oed != NULL && oed->dd.recalc != 0) { + oed->shadow_bbox_dirty = (oed->dd.recalc & ID_RECALC_ALL) != 0; + oed->dd.recalc = 0; + } + } +} + +static const DrawEngineDataSize workbench_data_size = DRW_VIEWPORT_DATA_SIZE(WORKBENCH_Data); + +DrawEngineType draw_engine_workbench = { + NULL, + NULL, + N_("Workbench"), + &workbench_data_size, + &workbench_engine_init, + &workbench_engine_free, + &workbench_cache_init, + &workbench_cache_populate, + &workbench_cache_finish, + &workbench_draw_scene, + &workbench_view_update, + &workbench_id_update, + &workbench_render, +}; + RenderEngineType DRW_engine_viewport_workbench_type = { NULL, NULL, @@ -44,7 +583,7 @@ RenderEngineType DRW_engine_viewport_workbench_type = { NULL, NULL, &workbench_render_update_passes, - &draw_engine_workbench_solid, + &draw_engine_workbench, {NULL, NULL, NULL}, }; diff --git a/source/blender/draw/engines/workbench/workbench_engine.h b/source/blender/draw/engines/workbench/workbench_engine.h index 78f70abdaab..eee53fcde07 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.h +++ b/source/blender/draw/engines/workbench/workbench_engine.h @@ -23,8 +23,6 @@ #ifndef __WORKBENCH_ENGINE_H__ #define __WORKBENCH_ENGINE_H__ -extern DrawEngineType draw_engine_workbench_solid; -extern DrawEngineType draw_engine_workbench_transparent; extern RenderEngineType DRW_engine_viewport_workbench_type; #endif /* __WORKBENCH_ENGINE_H__ */ diff --git a/source/blender/draw/engines/workbench/workbench_forward.c b/source/blender/draw/engines/workbench/workbench_forward.c deleted file mode 100644 index 7e09016cf61..00000000000 --- a/source/blender/draw/engines/workbench/workbench_forward.c +++ /dev/null @@ -1,817 +0,0 @@ -/* - * 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. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - */ - -#include "workbench_private.h" - -#include "BLI_alloca.h" -#include "BLI_dynstr.h" -#include "BLI_string_utils.h" -#include "BLI_utildefines.h" - -#include "BKE_modifier.h" -#include "BKE_object.h" -#include "BKE_paint.h" -#include "BKE_particle.h" - -#include "DNA_image_types.h" -#include "DNA_fluid_types.h" -#include "DNA_mesh_types.h" -#include "DNA_modifier_types.h" -#include "DNA_node_types.h" - -#include "ED_view3d.h" - -#include "GPU_shader.h" -#include "GPU_texture.h" - -/* *********** STATIC *********** */ - -typedef struct WORKBENCH_FORWARD_Shaders { - struct GPUShader *transparent_accum_sh_cache[MAX_ACCUM_SHADERS]; - struct GPUShader *object_outline_sh; - struct GPUShader *object_outline_texture_sh; - struct GPUShader *object_outline_hair_sh; -} WORKBENCH_FORWARD_Shaders; - -static struct { - WORKBENCH_FORWARD_Shaders sh_data[GPU_SHADER_CFG_LEN]; - - struct GPUShader *composite_sh_cache[2]; - - struct GPUTexture *object_id_tx; /* ref only, not alloced */ - struct GPUTexture *transparent_accum_tx; /* ref only, not alloced */ - struct GPUTexture *transparent_revealage_tx; /* ref only, not alloced */ - struct GPUTexture *composite_buffer_tx; /* ref only, not alloced */ -} e_data = {{{{NULL}}}}; - -/* Shaders */ -extern char datatoc_common_hair_lib_glsl[]; -extern char datatoc_common_view_lib_glsl[]; - -extern char datatoc_workbench_forward_composite_frag_glsl[]; -extern char datatoc_workbench_forward_depth_frag_glsl[]; -extern char datatoc_workbench_forward_transparent_accum_frag_glsl[]; -extern char datatoc_workbench_data_lib_glsl[]; -extern char datatoc_workbench_object_outline_lib_glsl[]; -extern char datatoc_workbench_curvature_lib_glsl[]; -extern char datatoc_workbench_prepass_vert_glsl[]; -extern char datatoc_workbench_common_lib_glsl[]; -extern char datatoc_workbench_world_light_lib_glsl[]; - -/* static functions */ -static char *workbench_build_forward_vert(bool is_hair) -{ - DynStr *ds = BLI_dynstr_new(); - if (is_hair) { - BLI_dynstr_append(ds, datatoc_common_hair_lib_glsl); - } - BLI_dynstr_append(ds, datatoc_common_view_lib_glsl); - BLI_dynstr_append(ds, datatoc_workbench_prepass_vert_glsl); - - char *str = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - return str; -} - -static char *workbench_build_forward_outline_frag(void) -{ - DynStr *ds = BLI_dynstr_new(); - - BLI_dynstr_append(ds, datatoc_common_view_lib_glsl); - BLI_dynstr_append(ds, datatoc_workbench_forward_depth_frag_glsl); - - char *str = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - return str; -} - -static char *workbench_build_forward_transparent_accum_frag(void) -{ - DynStr *ds = BLI_dynstr_new(); - - BLI_dynstr_append(ds, datatoc_common_view_lib_glsl); - BLI_dynstr_append(ds, datatoc_workbench_data_lib_glsl); - BLI_dynstr_append(ds, datatoc_workbench_common_lib_glsl); - BLI_dynstr_append(ds, datatoc_workbench_world_light_lib_glsl); - BLI_dynstr_append(ds, datatoc_workbench_forward_transparent_accum_frag_glsl); - - char *str = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - return str; -} - -static char *workbench_build_forward_composite_frag(void) -{ - DynStr *ds = BLI_dynstr_new(); - - BLI_dynstr_append(ds, datatoc_workbench_data_lib_glsl); - BLI_dynstr_append(ds, datatoc_workbench_common_lib_glsl); - BLI_dynstr_append(ds, datatoc_workbench_object_outline_lib_glsl); - BLI_dynstr_append(ds, datatoc_workbench_curvature_lib_glsl); - BLI_dynstr_append(ds, datatoc_workbench_forward_composite_frag_glsl); - - char *str = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - return str; -} - -WORKBENCH_MaterialData *workbench_forward_get_or_create_material_data( - WORKBENCH_Data *vedata, - Object *ob, - Material *mat, - Image *ima, - ImageUser *iuser, - eV3DShadingColorType color_type, - int interp) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - WORKBENCH_FORWARD_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg]; - WORKBENCH_StorageList *stl = vedata->stl; - WORKBENCH_PassList *psl = vedata->psl; - WORKBENCH_PrivateData *wpd = stl->g_data; - WORKBENCH_MaterialData *material; - WORKBENCH_MaterialData material_template; - DRWShadingGroup *grp; - - /* Solid */ - workbench_material_update_data(wpd, ob, mat, &material_template, color_type); - material_template.color_type = color_type; - material_template.ima = ima; - material_template.iuser = iuser; - material_template.interp = interp; - uint hash = workbench_material_get_hash(&material_template, false); - - material = BLI_ghash_lookup(wpd->material_transp_hash, POINTER_FROM_UINT(hash)); - if (material == NULL) { - material = MEM_mallocN(sizeof(WORKBENCH_MaterialData), __func__); - - /* transparent accum */ - /* select the correct transparent accum shader */ - GPUShader *shader = (wpd->shading.color_type == color_type) ? - wpd->transparent_accum_sh : - wpd->transparent_accum_uniform_sh; - const bool is_tiled = (ima && ima->source == IMA_SRC_TILED); - if (color_type == V3D_SHADING_TEXTURE_COLOR) { - shader = is_tiled ? wpd->transparent_accum_textured_array_sh : - wpd->transparent_accum_textured_sh; - } - - grp = DRW_shgroup_create(shader, psl->transparent_accum_pass); - DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo); - DRW_shgroup_uniform_float_copy(grp, "alpha", material_template.alpha); - DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)wpd->viewvecs, 3); - workbench_material_copy(material, &material_template); - if (STUDIOLIGHT_TYPE_MATCAP_ENABLED(wpd)) { - BKE_studiolight_ensure_flag(wpd->studio_light, - STUDIOLIGHT_MATCAP_DIFFUSE_GPUTEXTURE | - STUDIOLIGHT_MATCAP_SPECULAR_GPUTEXTURE); - DRW_shgroup_uniform_texture( - grp, "matcapDiffuseImage", wpd->studio_light->matcap_diffuse.gputexture); - if (workbench_is_specular_highlight_enabled(wpd)) { - DRW_shgroup_uniform_texture( - grp, "matcapSpecularImage", wpd->studio_light->matcap_specular.gputexture); - } - } - if (workbench_is_specular_highlight_enabled(wpd) || MATCAP_ENABLED(wpd)) { - DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1); - } - if (SHADOW_ENABLED(wpd)) { - DRW_shgroup_uniform_float_copy(grp, "shadowMultiplier", wpd->shadow_multiplier); - DRW_shgroup_uniform_float_copy(grp, "shadowShift", wpd->shadow_shift); - DRW_shgroup_uniform_float_copy(grp, "shadowFocus", wpd->shadow_focus); - } - - workbench_material_shgroup_uniform(wpd, grp, material, ob, false, is_tiled, interp); - material->shgrp = grp; - - /* Depth */ - if (color_type == V3D_SHADING_TEXTURE_COLOR) { - material->shgrp_object_outline = DRW_shgroup_create(sh_data->object_outline_texture_sh, - psl->object_outline_pass); - GPUTexture *tex = GPU_texture_from_blender( - material->ima, material->iuser, NULL, GL_TEXTURE_2D); - DRW_shgroup_uniform_texture(material->shgrp_object_outline, "image", tex); - } - else { - material->shgrp_object_outline = DRW_shgroup_create(sh_data->object_outline_sh, - psl->object_outline_pass); - } - if (draw_ctx->sh_cfg == GPU_SHADER_CFG_CLIPPED) { - DRW_shgroup_state_enable(material->shgrp_object_outline, DRW_STATE_CLIP_PLANES); - } - BLI_ghash_insert(wpd->material_transp_hash, POINTER_FROM_UINT(hash), material); - } - return material; -} - -static GPUShader *ensure_forward_accum_shaders(WORKBENCH_PrivateData *wpd, - bool is_uniform_color, - bool is_hair, - bool is_tiled, - const WORKBENCH_ColorOverride color_override, - eGPUShaderConfig sh_cfg) -{ - WORKBENCH_FORWARD_Shaders *sh_data = &e_data.sh_data[sh_cfg]; - int index = workbench_material_get_accum_shader_index( - wpd, is_uniform_color, is_hair, is_tiled, color_override); - if (sh_data->transparent_accum_sh_cache[index] == NULL) { - const GPUShaderConfigData *sh_cfg_data = &GPU_shader_cfg_data[sh_cfg]; - char *defines = workbench_material_build_defines( - wpd, is_uniform_color, is_hair, is_tiled, color_override); - char *transparent_accum_vert = workbench_build_forward_vert(is_hair); - char *transparent_accum_frag = workbench_build_forward_transparent_accum_frag(); - sh_data->transparent_accum_sh_cache[index] = GPU_shader_create_from_arrays({ - .vert = (const char *[]){sh_cfg_data->lib, transparent_accum_vert, NULL}, - .frag = (const char *[]){transparent_accum_frag, NULL}, - .defs = (const char *[]){sh_cfg_data->def, defines, NULL}, - }); - MEM_freeN(transparent_accum_vert); - MEM_freeN(transparent_accum_frag); - MEM_freeN(defines); - } - return sh_data->transparent_accum_sh_cache[index]; -} - -static GPUShader *ensure_forward_composite_shaders(WORKBENCH_PrivateData *wpd) -{ - int index = OBJECT_OUTLINE_ENABLED(wpd) ? 1 : 0; - if (e_data.composite_sh_cache[index] == NULL) { - char *defines = workbench_material_build_defines( - wpd, false, false, false, WORKBENCH_COLOR_OVERRIDE_OFF); - char *composite_frag = workbench_build_forward_composite_frag(); - e_data.composite_sh_cache[index] = DRW_shader_create_fullscreen(composite_frag, defines); - MEM_freeN(composite_frag); - MEM_freeN(defines); - } - return e_data.composite_sh_cache[index]; -} - -void workbench_forward_choose_shaders(WORKBENCH_PrivateData *wpd, eGPUShaderConfig sh_cfg) -{ - wpd->composite_sh = ensure_forward_composite_shaders(wpd); - wpd->transparent_accum_sh = ensure_forward_accum_shaders( - wpd, false, false, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); - wpd->transparent_accum_hair_sh = ensure_forward_accum_shaders( - wpd, false, true, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); - wpd->transparent_accum_uniform_sh = ensure_forward_accum_shaders( - wpd, true, false, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); - wpd->transparent_accum_uniform_hair_sh = ensure_forward_accum_shaders( - wpd, true, true, false, WORKBENCH_COLOR_OVERRIDE_OFF, sh_cfg); - wpd->transparent_accum_textured_sh = ensure_forward_accum_shaders( - wpd, false, false, false, WORKBENCH_COLOR_OVERRIDE_TEXTURE, sh_cfg); - wpd->transparent_accum_textured_array_sh = ensure_forward_accum_shaders( - wpd, false, false, true, WORKBENCH_COLOR_OVERRIDE_TEXTURE, sh_cfg); - wpd->transparent_accum_vertex_sh = ensure_forward_accum_shaders( - wpd, false, false, false, WORKBENCH_COLOR_OVERRIDE_VERTEX, sh_cfg); -} - -void workbench_forward_outline_shaders_ensure(WORKBENCH_PrivateData *wpd, eGPUShaderConfig sh_cfg) -{ - WORKBENCH_FORWARD_Shaders *sh_data = &e_data.sh_data[sh_cfg]; - - if (sh_data->object_outline_sh == NULL) { - const GPUShaderConfigData *sh_cfg_data = &GPU_shader_cfg_data[sh_cfg]; - char *defines = workbench_material_build_defines( - wpd, false, false, false, WORKBENCH_COLOR_OVERRIDE_OFF); - char *defines_texture = workbench_material_build_defines( - wpd, true, false, false, WORKBENCH_COLOR_OVERRIDE_OFF); - char *defines_hair = workbench_material_build_defines( - wpd, false, true, false, WORKBENCH_COLOR_OVERRIDE_OFF); - char *forward_vert = workbench_build_forward_vert(false); - char *forward_frag = workbench_build_forward_outline_frag(); - char *forward_hair_vert = workbench_build_forward_vert(true); - - const char *define_id_pass = "#define OBJECT_ID_PASS_ENABLED\n"; - - sh_data->object_outline_sh = GPU_shader_create_from_arrays({ - .vert = (const char *[]){sh_cfg_data->lib, forward_vert, NULL}, - .frag = (const char *[]){forward_frag, NULL}, - .defs = (const char *[]){sh_cfg_data->def, defines, define_id_pass, NULL}, - }); - sh_data->object_outline_texture_sh = GPU_shader_create_from_arrays({ - .vert = (const char *[]){sh_cfg_data->lib, forward_vert, NULL}, - .frag = (const char *[]){forward_frag, NULL}, - .defs = (const char *[]){sh_cfg_data->def, defines_texture, define_id_pass, NULL}, - }); - sh_data->object_outline_hair_sh = GPU_shader_create_from_arrays({ - .vert = (const char *[]){sh_cfg_data->lib, forward_hair_vert, NULL}, - .frag = (const char *[]){forward_frag, NULL}, - .defs = (const char *[]){sh_cfg_data->def, defines_hair, define_id_pass, NULL}, - }); - - MEM_freeN(forward_hair_vert); - MEM_freeN(forward_vert); - MEM_freeN(forward_frag); - MEM_freeN(defines); - MEM_freeN(defines_texture); - MEM_freeN(defines_hair); - } -} - -/* public functions */ -void workbench_forward_engine_init(WORKBENCH_Data *vedata) -{ - WORKBENCH_FramebufferList *fbl = vedata->fbl; - WORKBENCH_PassList *psl = vedata->psl; - WORKBENCH_StorageList *stl = vedata->stl; - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); - const DRWContextState *draw_ctx = DRW_context_state_get(); - DRWShadingGroup *grp; - - if (!stl->g_data) { - /* Alloc transient pointers */ - stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__); - } - if (!stl->effects) { - stl->effects = MEM_callocN(sizeof(*stl->effects), __func__); - workbench_effect_info_init(stl->effects); - } - WORKBENCH_PrivateData *wpd = stl->g_data; - workbench_private_data_init(wpd); - - workbench_forward_outline_shaders_ensure(wpd, draw_ctx->sh_cfg); - - workbench_volume_engine_init(); - workbench_fxaa_engine_init(); - workbench_taa_engine_init(vedata); - - workbench_forward_outline_shaders_ensure(wpd, draw_ctx->sh_cfg); - workbench_forward_choose_shaders(wpd, draw_ctx->sh_cfg); - - const float *viewport_size = DRW_viewport_size_get(); - const int size[2] = {(int)viewport_size[0], (int)viewport_size[1]}; - const eGPUTextureFormat comp_tex_format = GPU_RGBA16F; - - e_data.object_id_tx = DRW_texture_pool_query_2d( - size[0], size[1], GPU_R32UI, &draw_engine_workbench_transparent); - e_data.transparent_accum_tx = DRW_texture_pool_query_2d( - size[0], size[1], GPU_RGBA16F, &draw_engine_workbench_transparent); - e_data.transparent_revealage_tx = DRW_texture_pool_query_2d( - size[0], size[1], GPU_R16F, &draw_engine_workbench_transparent); - e_data.composite_buffer_tx = DRW_texture_pool_query_2d( - size[0], size[1], comp_tex_format, &draw_engine_workbench_transparent); - - GPU_framebuffer_ensure_config(&fbl->object_outline_fb, - { - GPU_ATTACHMENT_TEXTURE(dtxl->depth), - GPU_ATTACHMENT_TEXTURE(e_data.object_id_tx), - }); - GPU_framebuffer_ensure_config(&fbl->transparent_accum_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(e_data.transparent_accum_tx), - GPU_ATTACHMENT_TEXTURE(e_data.transparent_revealage_tx), - }); - GPU_framebuffer_ensure_config(&fbl->composite_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(e_data.composite_buffer_tx), - }); - GPU_framebuffer_ensure_config(&fbl->effect_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(e_data.transparent_accum_tx), - }); - - workbench_volume_cache_init(vedata); - - DRWState clip_state = WORLD_CLIPPING_ENABLED(wpd) ? DRW_STATE_CLIP_PLANES : 0; - DRWState cull_state = CULL_BACKFACE_ENABLED(wpd) ? DRW_STATE_CULL_BACK : 0; - - /* Transparency Accum */ - { - int state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_OIT | cull_state | clip_state; - psl->transparent_accum_pass = DRW_pass_create("Transparent Accum", state); - } - /* Depth */ - { - int state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | cull_state | - clip_state; - psl->object_outline_pass = DRW_pass_create("Object Outline Pass", state); - } - /* Composite */ - { - int state = DRW_STATE_WRITE_COLOR; - if (DRW_state_is_scene_render()) { - /* Composite the scene over cleared background. */ - state |= DRW_STATE_BLEND_ALPHA_PREMUL; - } - psl->composite_pass = DRW_pass_create("Composite", state); - - grp = DRW_shgroup_create(wpd->composite_sh, psl->composite_pass); - if (OBJECT_ID_PASS_ENABLED(wpd)) { - DRW_shgroup_uniform_texture_ref(grp, "objectId", &e_data.object_id_tx); - } - DRW_shgroup_uniform_texture_ref(grp, "transparentAccum", &e_data.transparent_accum_tx); - DRW_shgroup_uniform_texture_ref(grp, "transparentRevealage", &e_data.transparent_revealage_tx); - DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo); - DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1); - DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); - } - - { - workbench_aa_create_pass(vedata, &e_data.transparent_accum_tx); - } - - if (wpd->shading.type == OB_WIRE) { - wpd->shading.xray_alpha = 0.0f; - wpd->shading.xray_alpha_wire = 0.0f; - } -} - -void workbench_forward_engine_free() -{ - for (int sh_data_index = 0; sh_data_index < ARRAY_SIZE(e_data.sh_data); sh_data_index++) { - WORKBENCH_FORWARD_Shaders *sh_data = &e_data.sh_data[sh_data_index]; - for (int index = 0; index < MAX_ACCUM_SHADERS; index++) { - DRW_SHADER_FREE_SAFE(sh_data->transparent_accum_sh_cache[index]); - } - DRW_SHADER_FREE_SAFE(sh_data->object_outline_sh); - DRW_SHADER_FREE_SAFE(sh_data->object_outline_texture_sh); - DRW_SHADER_FREE_SAFE(sh_data->object_outline_hair_sh); - } - - for (int index = 0; index < 2; index++) { - DRW_SHADER_FREE_SAFE(e_data.composite_sh_cache[index]); - } - - 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)) -{ -} - -static void workbench_forward_cache_populate_particles(WORKBENCH_Data *vedata, Object *ob) -{ - WORKBENCH_StorageList *stl = vedata->stl; - WORKBENCH_PassList *psl = vedata->psl; - WORKBENCH_PrivateData *wpd = stl->g_data; - - for (ModifierData *md = ob->modifiers.first; md; md = md->next) { - if (md->type != eModifierType_ParticleSystem) { - continue; - } - ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys; - if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) { - continue; - } - ParticleSettings *part = psys->part; - const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as; - - if (draw_as == PART_DRAW_PATH) { - const DRWContextState *draw_ctx = DRW_context_state_get(); - Material *mat; - Image *image; - ImageUser *iuser; - int interp; - workbench_material_get_image_and_mat(ob, part->omat, &image, &iuser, &interp, &mat); - eV3DShadingColorType color_type = workbench_material_determine_color_type( - wpd, image, ob, false); - WORKBENCH_MaterialData *material = workbench_forward_get_or_create_material_data( - vedata, ob, mat, image, iuser, color_type, interp); - - struct GPUShader *shader = (wpd->shading.color_type == color_type) ? - wpd->transparent_accum_hair_sh : - wpd->transparent_accum_uniform_hair_sh; - DRWShadingGroup *shgrp = DRW_shgroup_hair_create( - ob, psys, md, psl->transparent_accum_pass, shader); - DRW_shgroup_uniform_block(shgrp, "world_block", wpd->world_ubo); - workbench_material_shgroup_uniform(wpd, shgrp, material, ob, false, false, interp); - DRW_shgroup_uniform_vec4(shgrp, "viewvecs[0]", (float *)wpd->viewvecs, 3); - /* Hairs have lots of layer and can rapidly become the most prominent surface. - * So lower their alpha artificially. */ - float hair_alpha = XRAY_ALPHA(wpd) * 0.33f; - DRW_shgroup_uniform_float_copy(shgrp, "alpha", hair_alpha); - if (STUDIOLIGHT_TYPE_MATCAP_ENABLED(wpd)) { - BKE_studiolight_ensure_flag(wpd->studio_light, - STUDIOLIGHT_MATCAP_DIFFUSE_GPUTEXTURE | - STUDIOLIGHT_MATCAP_SPECULAR_GPUTEXTURE); - DRW_shgroup_uniform_texture( - shgrp, "matcapDiffuseImage", wpd->studio_light->matcap_diffuse.gputexture); - if (workbench_is_specular_highlight_enabled(wpd)) { - DRW_shgroup_uniform_texture( - shgrp, "matcapSpecularImage", wpd->studio_light->matcap_specular.gputexture); - } - } - if (workbench_is_specular_highlight_enabled(wpd) || MATCAP_ENABLED(wpd)) { - DRW_shgroup_uniform_vec2(shgrp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1); - } - - WORKBENCH_FORWARD_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg]; - shgrp = DRW_shgroup_hair_create( - ob, psys, md, vedata->psl->object_outline_pass, sh_data->object_outline_hair_sh); - } - } -} -static void workbench_forward_cache_populate_texture_paint_mode(WORKBENCH_Data *vedata, Object *ob) -{ - WORKBENCH_StorageList *stl = vedata->stl; - WORKBENCH_PrivateData *wpd = stl->g_data; - const DRWContextState *draw_ctx = DRW_context_state_get(); - - Scene *scene = draw_ctx->scene; - const bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) && - !DRW_state_is_image_render(); - WORKBENCH_MaterialData *material; - - /* Force workbench to render active object textured when in texture paint mode */ - const ImagePaintSettings *imapaint = &scene->toolsettings->imapaint; - - /* Single Image mode */ - if (imapaint->mode == IMAGEPAINT_MODE_IMAGE) { - Image *image = imapaint->canvas; - int interp = (imapaint->interp == IMAGEPAINT_INTERP_LINEAR) ? SHD_INTERP_LINEAR : - SHD_INTERP_CLOSEST; - eV3DShadingColorType color_type = workbench_material_determine_color_type( - wpd, image, ob, use_sculpt_pbvh); - struct GPUBatch *geom = DRW_cache_mesh_surface_texpaint_single_get(ob); - material = workbench_forward_get_or_create_material_data( - vedata, ob, NULL, image, NULL, color_type, interp); - - DRW_shgroup_call(material->shgrp, geom, ob); - DRW_shgroup_call(material->shgrp_object_outline, geom, ob); - } - else { - /* IMAGEPAINT_MODE_MATERIAL */ - const int materials_len = DRW_cache_object_material_count_get(ob); - struct GPUBatch **geom_array = DRW_cache_mesh_surface_texpaint_get(ob); - for (int i = 0; i < materials_len; i++) { - if (geom_array != NULL && geom_array[i] != NULL) { - Material *mat; - Image *image; - ImageUser *iuser; - int interp; - workbench_material_get_image_and_mat(ob, i + 1, &image, &iuser, &interp, &mat); - eV3DShadingColorType color_type = workbench_material_determine_color_type( - wpd, image, ob, use_sculpt_pbvh); - material = workbench_forward_get_or_create_material_data( - vedata, ob, mat, image, iuser, color_type, interp); - - DRW_shgroup_call(material->shgrp, geom_array[i], ob); - DRW_shgroup_call(material->shgrp_object_outline, geom_array[i], ob); - } - } - } -} -static void workbench_forward_cache_populate_vertex_paint_mode(WORKBENCH_Data *vedata, Object *ob) -{ - WORKBENCH_StorageList *stl = vedata->stl; - WORKBENCH_PrivateData *wpd = stl->g_data; - const DRWContextState *draw_ctx = DRW_context_state_get(); - - const bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) && - !DRW_state_is_image_render(); - WORKBENCH_MaterialData *material; - - eV3DShadingColorType color_type = workbench_material_determine_color_type( - wpd, NULL, ob, use_sculpt_pbvh); - struct GPUBatch *geom = DRW_cache_mesh_surface_vertpaint_get(ob); - material = workbench_forward_get_or_create_material_data( - vedata, ob, NULL, NULL, NULL, color_type, false); - DRW_shgroup_call(material->shgrp, geom, ob); - DRW_shgroup_call(material->shgrp_object_outline, geom, ob); -} - -void workbench_forward_cache_populate(WORKBENCH_Data *vedata, Object *ob) -{ - WORKBENCH_StorageList *stl = vedata->stl; - WORKBENCH_PrivateData *wpd = stl->g_data; - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - const bool is_wire = (ob->dt == OB_WIRE); - - if (!DRW_object_is_renderable(ob)) { - return; - } - - if (ob->type == OB_MESH) { - workbench_forward_cache_populate_particles(vedata, ob); - } - - ModifierData *md; - if (((ob->base_flag & BASE_FROM_DUPLI) == 0) && - (md = modifiers_findByType(ob, eModifierType_Fluid)) && - (modifier_isEnabled(scene, md, eModifierMode_Realtime)) && - (((FluidModifierData *)md)->domain != NULL) && - (((FluidModifierData *)md)->domain->type == FLUID_DOMAIN_TYPE_GAS)) { - workbench_volume_cache_populate(vedata, scene, ob, md); - return; /* Do not draw solid in this case. */ - } - - if (!(DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF)) { - return; - } - if (ob->dt < OB_WIRE) { - return; - } - - WORKBENCH_MaterialData *material = NULL; - if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) { - const bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) && - !DRW_state_is_image_render(); - const int materials_len = DRW_cache_object_material_count_get(ob); - const Mesh *me = (ob->type == OB_MESH) ? ob->data : NULL; - const WORKBENCH_ColorOverride color_override = workbench_object_color_override_get(ob); - const bool use_texture_paint_drawing = !(DRW_state_is_image_render() && - draw_ctx->v3d == NULL) && - (color_override == WORKBENCH_COLOR_OVERRIDE_TEXTURE) && - me && me->mloopuv; - const bool use_vertex_paint_drawing = !(DRW_state_is_image_render() && - draw_ctx->v3d == NULL) && - (color_override == WORKBENCH_COLOR_OVERRIDE_VERTEX) && - me && me->mloopcol; - - if (use_texture_paint_drawing) { - workbench_forward_cache_populate_texture_paint_mode(vedata, ob); - } - else if (use_vertex_paint_drawing) { - workbench_forward_cache_populate_vertex_paint_mode(vedata, ob); - } - else if (!use_sculpt_pbvh && TEXTURE_DRAWING_ENABLED(wpd) && me && me->mloopuv) { - struct GPUBatch **geom_array = DRW_cache_mesh_surface_texpaint_get(ob); - for (int i = 0; i < materials_len; i++) { - Material *mat; - Image *image; - ImageUser *iuser; - int interp; - workbench_material_get_image_and_mat(ob, i + 1, &image, &iuser, &interp, &mat); - eV3DShadingColorType color_type = workbench_material_determine_color_type( - wpd, image, ob, use_sculpt_pbvh); - material = workbench_forward_get_or_create_material_data( - vedata, ob, mat, image, iuser, color_type, interp); - DRW_shgroup_call(material->shgrp_object_outline, geom_array[i], ob); - DRW_shgroup_call(material->shgrp, geom_array[i], ob); - } - } - else if (ELEM(wpd->shading.color_type, - V3D_SHADING_SINGLE_COLOR, - V3D_SHADING_OBJECT_COLOR, - V3D_SHADING_RANDOM_COLOR, - V3D_SHADING_VERTEX_COLOR)) { - /* No material split needed */ - eV3DShadingColorType color_type = workbench_material_determine_color_type( - wpd, NULL, ob, use_sculpt_pbvh); - - if (use_sculpt_pbvh) { - material = workbench_forward_get_or_create_material_data( - vedata, ob, NULL, NULL, NULL, color_type, 0); - bool use_vcol = (color_type == V3D_SHADING_VERTEX_COLOR); - /* TODO(fclem) make this call optional */ - DRW_shgroup_call_sculpt(material->shgrp_object_outline, ob, false, false, false); - if (!is_wire) { - DRW_shgroup_call_sculpt(material->shgrp, ob, false, false, use_vcol); - } - } - else { - struct GPUBatch *geom = (color_type == V3D_SHADING_VERTEX_COLOR) ? - DRW_cache_mesh_surface_vertpaint_get(ob) : - DRW_cache_object_surface_get(ob); - if (geom) { - material = workbench_forward_get_or_create_material_data( - vedata, ob, NULL, NULL, NULL, color_type, 0); - /* TODO(fclem) make this call optional */ - DRW_shgroup_call(material->shgrp_object_outline, geom, ob); - if (!is_wire) { - DRW_shgroup_call(material->shgrp, geom, ob); - } - } - } - } - else { - /* Draw material color */ - if (use_sculpt_pbvh) { - struct DRWShadingGroup **shgrps = BLI_array_alloca(shgrps, materials_len); - - for (int i = 0; i < materials_len; i++) { - struct Material *mat = BKE_object_material_get(ob, i + 1); - material = workbench_forward_get_or_create_material_data( - vedata, ob, mat, NULL, NULL, V3D_SHADING_MATERIAL_COLOR, 0); - shgrps[i] = material->shgrp; - } - /* TODO(fclem) make this call optional */ - DRW_shgroup_call_sculpt(material->shgrp_object_outline, ob, false, false, false); - if (!is_wire) { - DRW_shgroup_call_sculpt_with_materials(shgrps, ob, false); - } - } - else { - struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len); - memset(gpumat_array, 0, sizeof(*gpumat_array) * materials_len); - - struct GPUBatch **mat_geom = DRW_cache_object_surface_material_get( - ob, gpumat_array, materials_len); - if (mat_geom) { - for (int i = 0; i < materials_len; i++) { - if (mat_geom[i] == NULL) { - continue; - } - - Material *mat = BKE_object_material_get(ob, i + 1); - material = workbench_forward_get_or_create_material_data( - vedata, ob, mat, NULL, NULL, V3D_SHADING_MATERIAL_COLOR, 0); - /* TODO(fclem) make this call optional */ - DRW_shgroup_call(material->shgrp_object_outline, mat_geom[i], ob); - if (!is_wire) { - DRW_shgroup_call(material->shgrp, mat_geom[i], ob); - } - } - } - } - } - } -} - -void workbench_forward_cache_finish(WORKBENCH_Data *UNUSED(vedata)) -{ -} - -void workbench_forward_draw_scene(WORKBENCH_Data *vedata) -{ - WORKBENCH_PassList *psl = vedata->psl; - WORKBENCH_StorageList *stl = vedata->stl; - WORKBENCH_FramebufferList *fbl = vedata->fbl; - WORKBENCH_PrivateData *wpd = stl->g_data; - DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); - - if (dfbl->in_front_fb) { - /* TODO(fclem) This clear should be done in a global place. */ - GPU_framebuffer_bind(dfbl->in_front_fb); - GPU_framebuffer_clear_depth(dfbl->in_front_fb, 1.0f); - } - - if (workbench_is_taa_enabled(wpd)) { - workbench_taa_draw_scene_start(vedata); - } - - /* Write Depth + Object ID */ - const float clear_outline[4] = {0.0f}; - GPU_framebuffer_bind(fbl->object_outline_fb); - GPU_framebuffer_clear_color(fbl->object_outline_fb, clear_outline); - DRW_draw_pass(psl->object_outline_pass); - - if (XRAY_ALPHA(wpd) > 0.0) { - const float clear_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; - GPU_framebuffer_bind(fbl->transparent_accum_fb); - GPU_framebuffer_clear_color(fbl->transparent_accum_fb, clear_color); - DRW_draw_pass(psl->transparent_accum_pass); - } - else { - /* TODO(fclem): this is unnecessary and takes up perf. - * Better change the composite frag shader to not use the tx. */ - const float clear_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; - GPU_framebuffer_bind(fbl->transparent_accum_fb); - GPU_framebuffer_clear_color(fbl->transparent_accum_fb, clear_color); - } - - /* Composite */ - GPU_framebuffer_bind(fbl->composite_fb); - - if (DRW_state_is_scene_render()) { - float clear_color[4]; - workbench_clear_color_get(clear_color); - GPU_framebuffer_clear_color(fbl->composite_fb, clear_color); - } - - DRW_draw_pass(psl->composite_pass); - DRW_draw_pass(psl->volume_pass); - - /* Only when clipping is enabled. */ - if (psl->background_pass) { - DRW_draw_pass(psl->background_pass); - } - - /* Color correct and Anti aliasing */ - workbench_aa_draw_pass(vedata, e_data.composite_buffer_tx); -} - -void workbench_forward_draw_finish(WORKBENCH_Data *vedata) -{ - WORKBENCH_StorageList *stl = vedata->stl; - WORKBENCH_PrivateData *wpd = stl->g_data; - - workbench_private_data_free(wpd); - workbench_volume_smoke_textures_free(wpd); -} diff --git a/source/blender/draw/engines/workbench/workbench_materials.c b/source/blender/draw/engines/workbench/workbench_materials.c index 5f1e3461d9f..246f5f88045 100644 --- a/source/blender/draw/engines/workbench/workbench_materials.c +++ b/source/blender/draw/engines/workbench/workbench_materials.c @@ -22,6 +22,8 @@ #include "workbench_private.h" +#include "BLI_memblock.h" + #include "BKE_image.h" #include "BKE_node.h" @@ -31,310 +33,78 @@ #include "DNA_node_types.h" #include "DNA_mesh_types.h" +#include "GPU_uniformbuffer.h" + #include "ED_uvedit.h" #define HSV_SATURATION 0.5 #define HSV_VALUE 0.8 -void workbench_material_update_data(WORKBENCH_PrivateData *wpd, - Object *ob, - Material *mat, - WORKBENCH_MaterialData *data, - int color_type) +void workbench_material_ubo_data(WORKBENCH_PrivateData *wpd, + Object *ob, + Material *mat, + WORKBENCH_UBO_Material *data, + eV3DShadingColorType color_type) { - data->metallic = 0.0f; - data->roughness = 0.632455532f; /* sqrtf(0.4f); */ - data->alpha = wpd->shading.xray_alpha; + float metallic = 0.0f; + float roughness = 0.632455532f; /* sqrtf(0.4f); */ + float alpha = wpd->shading.xray_alpha; - if (color_type == V3D_SHADING_SINGLE_COLOR) { - copy_v3_v3(data->base_color, wpd->shading.single_color); - } - else if (color_type == V3D_SHADING_ERROR_COLOR) { - copy_v3_fl3(data->base_color, 0.8, 0.0, 0.8); - } - else if (color_type == V3D_SHADING_RANDOM_COLOR) { - uint hash = BLI_ghashutil_strhash_p_murmur(ob->id.name); - if (ob->id.lib) { - hash = (hash * 13) ^ BLI_ghashutil_strhash_p_murmur(ob->id.lib->name); - } - - float hue = BLI_hash_int_01(hash); - float hsv[3] = {hue, HSV_SATURATION, HSV_VALUE}; - hsv_to_rgb_v(hsv, data->base_color); - } - else if (ELEM(color_type, V3D_SHADING_OBJECT_COLOR, V3D_SHADING_VERTEX_COLOR)) { - data->alpha *= ob->color[3]; - copy_v3_v3(data->base_color, ob->color); - } - else { - /* V3D_SHADING_MATERIAL_COLOR or V3D_SHADING_TEXTURE_COLOR */ - if (mat) { - data->alpha *= mat->a; - copy_v3_v3(data->base_color, &mat->r); - if (workbench_is_specular_highlight_enabled(wpd)) { - data->metallic = mat->metallic; - data->roughness = sqrtf(mat->roughness); /* Remap to disney roughness. */ - } - } - else { - copy_v3_fl(data->base_color, 0.8f); - } - } -} - -char *workbench_material_build_defines(WORKBENCH_PrivateData *wpd, - bool is_uniform_color, - bool is_hair, - bool is_tiled, - const WORKBENCH_ColorOverride color_override) -{ - char *str = NULL; - bool use_textures = (wpd->shading.color_type == V3D_SHADING_TEXTURE_COLOR) && !is_uniform_color; - bool use_vertex_colors = (wpd->shading.color_type == V3D_SHADING_VERTEX_COLOR) && - !is_uniform_color; - - switch (color_override) { - case WORKBENCH_COLOR_OVERRIDE_TEXTURE: - use_textures = true; - use_vertex_colors = false; - is_hair = false; - break; - case WORKBENCH_COLOR_OVERRIDE_VERTEX: - use_textures = false; - use_vertex_colors = true; - is_hair = false; - is_tiled = false; + switch (color_type) { + case V3D_SHADING_SINGLE_COLOR: + copy_v3_v3(data->base_color, wpd->shading.single_color); break; - case WORKBENCH_COLOR_OVERRIDE_OFF: - break; - } - - DynStr *ds = BLI_dynstr_new(); - - if (wpd->shading.flag & V3D_SHADING_OBJECT_OUTLINE) { - BLI_dynstr_append(ds, "#define V3D_SHADING_OBJECT_OUTLINE\n"); - } - if (wpd->shading.flag & V3D_SHADING_SHADOW) { - BLI_dynstr_append(ds, "#define V3D_SHADING_SHADOW\n"); - } - if (SSAO_ENABLED(wpd) || CURVATURE_ENABLED(wpd)) { - BLI_dynstr_append(ds, "#define WB_CAVITY\n"); - } - if (workbench_is_specular_highlight_enabled(wpd)) { - BLI_dynstr_append(ds, "#define V3D_SHADING_SPECULAR_HIGHLIGHT\n"); - } - if (STUDIOLIGHT_ENABLED(wpd)) { - BLI_dynstr_append(ds, "#define V3D_LIGHTING_STUDIO\n"); - } - if (FLAT_ENABLED(wpd)) { - BLI_dynstr_append(ds, "#define V3D_LIGHTING_FLAT\n"); - } - if (MATCAP_ENABLED(wpd)) { - BLI_dynstr_append(ds, "#define V3D_LIGHTING_MATCAP\n"); - } - if (OBJECT_ID_PASS_ENABLED(wpd)) { - BLI_dynstr_append(ds, "#define OBJECT_ID_PASS_ENABLED\n"); - } - if (workbench_is_matdata_pass_enabled(wpd)) { - BLI_dynstr_append(ds, "#define MATDATA_PASS_ENABLED\n"); - } - if (NORMAL_VIEWPORT_PASS_ENABLED(wpd)) { - BLI_dynstr_append(ds, "#define NORMAL_VIEWPORT_PASS_ENABLED\n"); - } - if (use_vertex_colors) { - BLI_dynstr_append(ds, "#define V3D_SHADING_VERTEX_COLOR\n"); - } - if (use_textures) { - BLI_dynstr_append(ds, "#define V3D_SHADING_TEXTURE_COLOR\n"); - } - if (NORMAL_ENCODING_ENABLED()) { - BLI_dynstr_append(ds, "#define WORKBENCH_ENCODE_NORMALS\n"); - } - if (is_hair) { - BLI_dynstr_append(ds, "#define HAIR_SHADER\n"); - } - if (use_textures && is_tiled) { - BLI_dynstr_append(ds, "#define TEXTURE_IMAGE_ARRAY\n"); - } - - str = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - return str; -} - -uint workbench_material_get_hash(WORKBENCH_MaterialData *mat, bool is_ghost) -{ - union { - struct { - /* WHATCH: Keep in sync with View3DShading.color_type max value. */ - uchar color_type; - uchar diff_r; - uchar diff_g; - uchar diff_b; - - uchar alpha; - uchar ghost; - uchar metal; - uchar roughness; - - void *ima; - }; - /* HACK to ensure input is 4 uint long. */ - uint a[4]; - } input = {.color_type = (uchar)(mat->color_type), - .diff_r = (uchar)(mat->base_color[0] * 0xFF), - .diff_g = (uchar)(mat->base_color[1] * 0xFF), - .diff_b = (uchar)(mat->base_color[2] * 0xFF), - - .alpha = (uint)(mat->alpha * 0xFF), - .ghost = (uchar)is_ghost, - .metal = (uchar)(mat->metallic * 0xFF), - .roughness = (uchar)(mat->roughness * 0xFF), - - .ima = mat->ima}; - - BLI_assert(sizeof(input) == sizeof(uint) * 4); - - return BLI_ghashutil_uinthash_v4((uint *)&input); -} - -int workbench_material_get_composite_shader_index(WORKBENCH_PrivateData *wpd) -{ - /* NOTE: change MAX_COMPOSITE_SHADERS accordingly when modifying this function. */ - int index = 0; - /* 2 bits FLAT/STUDIO/MATCAP + Specular highlight */ - index = wpd->shading.light; - SET_FLAG_FROM_TEST(index, wpd->shading.flag & V3D_SHADING_SHADOW, 1 << 2); - SET_FLAG_FROM_TEST(index, wpd->shading.flag & V3D_SHADING_CAVITY, 1 << 3); - SET_FLAG_FROM_TEST(index, wpd->shading.flag & V3D_SHADING_OBJECT_OUTLINE, 1 << 4); - SET_FLAG_FROM_TEST(index, workbench_is_matdata_pass_enabled(wpd), 1 << 5); - SET_FLAG_FROM_TEST(index, workbench_is_specular_highlight_enabled(wpd), 1 << 6); - BLI_assert(index < MAX_COMPOSITE_SHADERS); - return index; -} - -int workbench_material_get_prepass_shader_index(WORKBENCH_PrivateData *wpd, - bool is_uniform_color, - bool is_hair, - bool is_tiled, - const WORKBENCH_ColorOverride color_override) -{ - bool use_textures = (wpd->shading.color_type == V3D_SHADING_TEXTURE_COLOR) && !is_uniform_color; - bool use_vertex_colors = (wpd->shading.color_type == V3D_SHADING_VERTEX_COLOR) && - !is_uniform_color; - - switch (color_override) { - case WORKBENCH_COLOR_OVERRIDE_TEXTURE: - use_textures = true; - use_vertex_colors = false; - break; - case WORKBENCH_COLOR_OVERRIDE_VERTEX: - use_textures = false; - use_vertex_colors = true; - is_tiled = false; - break; - case WORKBENCH_COLOR_OVERRIDE_OFF: - break; - } - - /* NOTE: change MAX_PREPASS_SHADERS accordingly when modifying this function. */ - int index = 0; - SET_FLAG_FROM_TEST(index, is_hair, 1 << 0); - SET_FLAG_FROM_TEST(index, workbench_is_matdata_pass_enabled(wpd), 1 << 1); - SET_FLAG_FROM_TEST(index, OBJECT_ID_PASS_ENABLED(wpd), 1 << 2); - SET_FLAG_FROM_TEST(index, NORMAL_VIEWPORT_PASS_ENABLED(wpd), 1 << 3); - SET_FLAG_FROM_TEST(index, MATCAP_ENABLED(wpd), 1 << 4); - SET_FLAG_FROM_TEST(index, use_textures, 1 << 5); - SET_FLAG_FROM_TEST(index, use_vertex_colors, 1 << 6); - SET_FLAG_FROM_TEST(index, is_tiled && use_textures, 1 << 7); - BLI_assert(index < MAX_PREPASS_SHADERS); - return index; -} - -int workbench_material_get_accum_shader_index(WORKBENCH_PrivateData *wpd, - bool is_uniform_color, - bool is_hair, - bool is_tiled, - const WORKBENCH_ColorOverride color_override) -{ - bool use_textures = (wpd->shading.color_type == V3D_SHADING_TEXTURE_COLOR) && !is_uniform_color; - bool use_vertex_colors = (wpd->shading.color_type == V3D_SHADING_VERTEX_COLOR) && - !is_uniform_color; - - switch (color_override) { - case WORKBENCH_COLOR_OVERRIDE_TEXTURE: - use_textures = true; - use_vertex_colors = false; - is_hair = false; + case V3D_SHADING_RANDOM_COLOR: { + uint hash = BLI_ghashutil_strhash_p_murmur(ob->id.name); + if (ob->id.lib) { + hash = (hash * 13) ^ BLI_ghashutil_strhash_p_murmur(ob->id.lib->name); + } + float hue = BLI_hash_int_01(hash); + float hsv[3] = {hue, HSV_SATURATION, HSV_VALUE}; + hsv_to_rgb_v(hsv, data->base_color); break; - case WORKBENCH_COLOR_OVERRIDE_VERTEX: - use_textures = false; - use_vertex_colors = true; - is_hair = false; - is_tiled = false; + } + case V3D_SHADING_OBJECT_COLOR: + case V3D_SHADING_VERTEX_COLOR: + alpha *= ob->color[3]; + copy_v3_v3(data->base_color, ob->color); break; - case WORKBENCH_COLOR_OVERRIDE_OFF: + case V3D_SHADING_MATERIAL_COLOR: + case V3D_SHADING_TEXTURE_COLOR: + default: + if (mat) { + alpha *= mat->a; + copy_v3_v3(data->base_color, &mat->r); + metallic = mat->metallic; + roughness = sqrtf(mat->roughness); /* Remap to disney roughness. */ + } + else { + copy_v3_fl(data->base_color, 0.8f); + } break; } - /* NOTE: change MAX_ACCUM_SHADERS accordingly when modifying this function. */ - int index = 0; - /* 2 bits FLAT/STUDIO/MATCAP + Specular highlight */ - index = wpd->shading.light; - SET_FLAG_FROM_TEST(index, use_textures, 1 << 2); - SET_FLAG_FROM_TEST(index, use_vertex_colors, 1 << 3); - SET_FLAG_FROM_TEST(index, is_hair, 1 << 4); - /* 1 bits SHADOWS (only facing factor) */ - SET_FLAG_FROM_TEST(index, SHADOW_ENABLED(wpd), 1 << 5); - SET_FLAG_FROM_TEST(index, workbench_is_specular_highlight_enabled(wpd), 1 << 6); - SET_FLAG_FROM_TEST(index, is_tiled && use_textures, 1 << 7); - BLI_assert(index < MAX_ACCUM_SHADERS); - return index; + uint32_t packed_metallic = unit_float_to_uchar_clamp(metallic); + uint32_t packed_roughness = unit_float_to_uchar_clamp(roughness); + uint32_t packed_alpha = unit_float_to_uchar_clamp(alpha); + data->packed_data = (packed_alpha << 16u) | (packed_roughness << 8u) | packed_metallic; } -eV3DShadingColorType workbench_material_determine_color_type(WORKBENCH_PrivateData *wpd, - Image *ima, - Object *ob, - bool use_sculpt_pbvh) +/* Return correct material or empty default material if slot is empty. */ +BLI_INLINE Material *workbench_object_material_get(Object *ob, int mat_nr) { - eV3DShadingColorType color_type = wpd->shading.color_type; - const Mesh *me = (ob->type == OB_MESH) ? ob->data : NULL; - - if ((color_type == V3D_SHADING_TEXTURE_COLOR) && - (ima == NULL || use_sculpt_pbvh || (ob->dt < OB_TEXTURE))) { - color_type = V3D_SHADING_MATERIAL_COLOR; + Material *ma = BKE_object_material_get(ob, mat_nr); + if (ma == NULL) { + ma = BKE_material_default_empty(); } - if (color_type == V3D_SHADING_VERTEX_COLOR && (me == NULL || me->mloopcol == NULL)) { - color_type = V3D_SHADING_OBJECT_COLOR; - } - - switch (workbench_object_color_override_get(ob)) { - /* Force V3D_SHADING_TEXTURE_COLOR for active object when in texture painting - * no matter the shading color that the user has chosen, when there is no - * texture we will render the object with the error color */ - case WORKBENCH_COLOR_OVERRIDE_TEXTURE: - color_type = ima ? V3D_SHADING_TEXTURE_COLOR : V3D_SHADING_ERROR_COLOR; - break; - - /* Force V3D_SHADING_VERTEX_COLOR for active object when in vertex painting - * no matter the shading color that the user has chosen, when there is no - * vertex color we will render the object with the error color */ - case WORKBENCH_COLOR_OVERRIDE_VERTEX: - color_type = V3D_SHADING_VERTEX_COLOR; - break; - - case WORKBENCH_COLOR_OVERRIDE_OFF: - break; - } - - return color_type; + return ma; } -void workbench_material_get_image_and_mat( - Object *ob, int mat_nr, Image **r_image, ImageUser **r_iuser, int *r_interp, Material **r_mat) +BLI_INLINE void workbench_material_get_image( + Object *ob, int mat_nr, Image **r_image, ImageUser **r_iuser, int *r_interp) { bNode *node; - *r_mat = BKE_object_material_get(ob, mat_nr); + ED_object_get_active_image(ob, mat_nr, r_image, r_iuser, &node, NULL); if (node && *r_image) { switch (node->type) { @@ -358,53 +128,170 @@ void workbench_material_get_image_and_mat( } } -void workbench_material_shgroup_uniform(WORKBENCH_PrivateData *wpd, - DRWShadingGroup *grp, - WORKBENCH_MaterialData *material, - Object *ob, - const bool deferred, - const bool is_tiled, - const int interp) +/* Return true if the current material ubo has changed and needs to be rebind. */ +BLI_INLINE bool workbench_material_chunk_select(WORKBENCH_PrivateData *wpd, + uint32_t id, + uint32_t *r_mat_id) { - if (deferred && !workbench_is_matdata_pass_enabled(wpd)) { - return; + bool resource_changed = false; + /* Divide in chunks of MAX_MATERIAL. */ + uint32_t chunk = id >> 12u; + *r_mat_id = id & 0xFFFu; + /* We need to add a new chunk. */ + while (chunk >= wpd->material_chunk_count) { + wpd->material_chunk_count++; + wpd->material_ubo_data_curr = BLI_memblock_alloc(wpd->material_ubo_data); + wpd->material_ubo_curr = workbench_material_ubo_alloc(wpd); + wpd->material_chunk_curr = chunk; + resource_changed = true; + } + /* We need to go back to a previous chunk. */ + if (wpd->material_chunk_curr != chunk) { + wpd->material_ubo_data_curr = BLI_memblock_elem_get(wpd->material_ubo_data, 0, chunk); + wpd->material_ubo_curr = BLI_memblock_elem_get(wpd->material_ubo, 0, chunk); + wpd->material_chunk_curr = chunk; + resource_changed = true; } + return resource_changed; +} - const bool use_highlight = workbench_is_specular_highlight_enabled(wpd); - const bool use_texture = (V3D_SHADING_TEXTURE_COLOR == workbench_material_determine_color_type( - wpd, material->ima, ob, false)); - if (use_texture) { - if (is_tiled) { - GPUTexture *array_tex = GPU_texture_from_blender( - material->ima, material->iuser, NULL, GL_TEXTURE_2D_ARRAY); - GPUTexture *data_tex = GPU_texture_from_blender( - material->ima, material->iuser, NULL, GL_TEXTURE_1D_ARRAY); - DRW_shgroup_uniform_texture(grp, "image_tile_array", array_tex); - DRW_shgroup_uniform_texture(grp, "image_tile_data", data_tex); - } - else { - GPUTexture *tex = GPU_texture_from_blender( - material->ima, material->iuser, NULL, GL_TEXTURE_2D); - DRW_shgroup_uniform_texture(grp, "image", tex); +DRWShadingGroup *workbench_material_setup_ex(WORKBENCH_PrivateData *wpd, + Object *ob, + int mat_nr, + eV3DShadingColorType color_type, + bool hair, + bool *r_transp) +{ + Image *ima = NULL; + ImageUser *iuser = NULL; + int interp; + const bool infront = (ob->dtx & OB_DRAWXRAY) != 0; + + if (color_type == V3D_SHADING_TEXTURE_COLOR) { + workbench_material_get_image(ob, mat_nr, &ima, &iuser, &interp); + if (ima == NULL) { + /* Fallback to material color. */ + color_type = V3D_SHADING_MATERIAL_COLOR; } - DRW_shgroup_uniform_bool_copy( - grp, "imagePremultiplied", (material->ima->alpha_mode == IMA_ALPHA_PREMUL)); - DRW_shgroup_uniform_bool_copy(grp, "imageNearest", (interp == SHD_INTERP_CLOSEST)); } - DRW_shgroup_uniform_vec4(grp, "materialColorAndMetal", material->base_color, 1); + switch (color_type) { + case V3D_SHADING_TEXTURE_COLOR: { + return workbench_image_setup_ex(wpd, ob, mat_nr, ima, iuser, interp, hair); + } + case V3D_SHADING_MATERIAL_COLOR: { + /* For now, we use the same ubo for material and object coloring but with different indices. + * This means they are mutually exclusive. */ + BLI_assert( + ELEM(wpd->shading.color_type, V3D_SHADING_MATERIAL_COLOR, V3D_SHADING_TEXTURE_COLOR)); + + Material *ma = workbench_object_material_get(ob, mat_nr); + + const bool transp = wpd->shading.xray_alpha < 1.0f || ma->a < 1.0f; + WORKBENCH_Prepass *prepass = &wpd->prepass[transp][infront][hair]; + + if (r_transp && transp) { + *r_transp = true; + } - if (use_highlight) { - DRW_shgroup_uniform_float(grp, "materialRoughness", &material->roughness, 1); + DRWShadingGroup **grp_mat = NULL; + /* A hashmap stores material shgroups to pack all similar drawcalls together. */ + if (BLI_ghash_ensure_p(prepass->material_hash, ma, (void ***)&grp_mat)) { + return *grp_mat; + } + + uint32_t mat_id, id = wpd->material_index++; + + workbench_material_chunk_select(wpd, id, &mat_id); + workbench_material_ubo_data(wpd, ob, ma, &wpd->material_ubo_data_curr[mat_id], color_type); + + DRWShadingGroup *grp = prepass->common_shgrp; + *grp_mat = grp = DRW_shgroup_create_sub(grp); + DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr); + DRW_shgroup_uniform_int_copy(grp, "materialIndex", mat_id); + return grp; + } + case V3D_SHADING_VERTEX_COLOR: { + const bool transp = wpd->shading.xray_alpha < 1.0f; + DRWShadingGroup *grp = wpd->prepass[transp][infront][hair].vcol_shgrp; + return grp; + } + default: { + /* For now, we use the same ubo for material and object coloring but with different indices. + * This means they are mutually exclusive. */ + BLI_assert( + !ELEM(wpd->shading.color_type, V3D_SHADING_MATERIAL_COLOR, V3D_SHADING_TEXTURE_COLOR)); + + uint32_t mat_id, id = DRW_object_resource_id_get(ob); + + bool resource_changed = workbench_material_chunk_select(wpd, id, &mat_id); + workbench_material_ubo_data(wpd, ob, NULL, &wpd->material_ubo_data_curr[mat_id], color_type); + + const bool transp = wpd->shading.xray_alpha < 1.0f || ob->color[3] < 1.0f; + DRWShadingGroup *grp = wpd->prepass[transp][infront][hair].common_shgrp; + if (resource_changed) { + grp = DRW_shgroup_create_sub(grp); + DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr); + } + if (r_transp && transp) { + *r_transp = true; + } + return grp; + } } } -void workbench_material_copy(WORKBENCH_MaterialData *dest_material, - const WORKBENCH_MaterialData *source_material) +/* If ima is null, search appropriate image node but will fallback to purple texture otherwise. */ +DRWShadingGroup *workbench_image_setup_ex(WORKBENCH_PrivateData *wpd, + Object *ob, + int mat_nr, + Image *ima, + ImageUser *iuser, + int interp, + bool hair) { - copy_v3_v3(dest_material->base_color, source_material->base_color); - dest_material->metallic = source_material->metallic; - dest_material->roughness = source_material->roughness; - dest_material->ima = source_material->ima; - dest_material->iuser = source_material->iuser; + GPUTexture *tex = NULL, *tex_tile_data = NULL; + + if (ima == NULL) { + workbench_material_get_image(ob, mat_nr, &ima, &iuser, &interp); + } + + if (ima) { + if (ima->source == IMA_SRC_TILED) { + tex = GPU_texture_from_blender(ima, iuser, NULL, GL_TEXTURE_2D_ARRAY); + tex_tile_data = GPU_texture_from_blender(ima, iuser, NULL, GL_TEXTURE_1D_ARRAY); + } + else { + tex = GPU_texture_from_blender(ima, iuser, NULL, GL_TEXTURE_2D); + } + } + + if (tex == NULL) { + printf("Image not foudn\n"); + tex = wpd->dummy_image_tx; + } + + const bool infront = (ob->dtx & OB_DRAWXRAY) != 0; + const bool transp = wpd->shading.xray_alpha < 1.0f; + WORKBENCH_Prepass *prepass = &wpd->prepass[transp][infront][hair]; + + DRWShadingGroup **grp_tex = NULL; + /* A hashmap stores image shgroups to pack all similar drawcalls together. */ + if (BLI_ghash_ensure_p(prepass->material_hash, tex, (void ***)&grp_tex)) { + return *grp_tex; + } + + DRWShadingGroup *grp = (tex_tile_data) ? prepass->image_tiled_shgrp : prepass->image_shgrp; + + *grp_tex = grp = DRW_shgroup_create_sub(grp); + if (tex_tile_data) { + DRW_shgroup_uniform_texture_persistent(grp, "imageTileArray", tex); + DRW_shgroup_uniform_texture_persistent(grp, "imageTileData", tex_tile_data); + } + else { + DRW_shgroup_uniform_texture_persistent(grp, "imageTexture", tex); + } + DRW_shgroup_uniform_bool_copy(grp, "imagePremult", (ima && ima->alpha_mode == IMA_ALPHA_PREMUL)); + DRW_shgroup_uniform_bool_copy(grp, "imageNearest", (interp == SHD_INTERP_CLOSEST)); + return grp; } diff --git a/source/blender/draw/engines/workbench/workbench_opaque.c b/source/blender/draw/engines/workbench/workbench_opaque.c new file mode 100644 index 00000000000..abb76550b96 --- /dev/null +++ b/source/blender/draw/engines/workbench/workbench_opaque.c @@ -0,0 +1,165 @@ +/* + * 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. + * + * Copyright 2020, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + * + * Opaque Pieline: + * + * Use deferred shading to render opaque surfaces. + * This decouple the shading cost from scene complexity. + * + * The rendering is broken down in two passes: + * - the pre-pass where we render all the surfaces and output material data. + * - the composite pass where we compute the final aspect of the pixels. + */ + +#include "DRW_render.h" + +#include "GPU_extensions.h" + +#include "workbench_engine.h" +#include "workbench_private.h" + +void workbench_opaque_engine_init(WORKBENCH_Data *data) +{ + WORKBENCH_FramebufferList *fbl = data->fbl; + WORKBENCH_PrivateData *wpd = data->stl->wpd; + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + DrawEngineType *owner = (DrawEngineType *)&workbench_opaque_engine_init; + + /* Reused the same textures format for transparent pipeline to share the textures. */ + const eGPUTextureFormat col_tex_format = GPU_RGBA16F; + const eGPUTextureFormat nor_tex_format = NORMAL_ENCODING_ENABLED() ? GPU_RG16F : GPU_RGBA16F; + + wpd->material_buffer_tx = DRW_texture_pool_query_fullscreen(col_tex_format, owner); + wpd->normal_buffer_tx = DRW_texture_pool_query_fullscreen(nor_tex_format, owner); + + GPU_framebuffer_ensure_config(&fbl->opaque_fb, + { + GPU_ATTACHMENT_TEXTURE(dtxl->depth), + GPU_ATTACHMENT_TEXTURE(wpd->material_buffer_tx), + GPU_ATTACHMENT_TEXTURE(wpd->normal_buffer_tx), + GPU_ATTACHMENT_TEXTURE(wpd->object_id_tx), + }); +} + +void workbench_opaque_cache_init(WORKBENCH_Data *data) +{ + WORKBENCH_PassList *psl = data->psl; + WORKBENCH_PrivateData *wpd = data->stl->wpd; + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + struct GPUShader *sh; + DRWShadingGroup *grp; + + const bool use_matcap = (wpd->shading.light == V3D_LIGHTING_MATCAP); + + { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; + + int opaque = 0; + for (int infront = 0; infront < 2; infront++) { + DRWPass *pass; + if (infront) { + DRW_PASS_CREATE(psl->opaque_infront_ps, state | wpd->cull_state | wpd->clip_state); + pass = psl->opaque_infront_ps; + } + else { + DRW_PASS_CREATE(psl->opaque_ps, state | wpd->cull_state | wpd->clip_state); + pass = psl->opaque_ps; + } + + for (int hair = 0; hair < 2; hair++) { + wpd->prepass[opaque][infront][hair].material_hash = BLI_ghash_ptr_new(__func__); + + sh = workbench_shader_opaque_get(wpd, hair); + + wpd->prepass[opaque][infront][hair].common_shgrp = grp = DRW_shgroup_create(sh, pass); + DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr); + DRW_shgroup_uniform_int_copy(grp, "materialIndex", -1); + + wpd->prepass[opaque][infront][hair].vcol_shgrp = grp = DRW_shgroup_create(sh, pass); + DRW_shgroup_uniform_block_persistent(grp, "material_block", wpd->material_ubo_curr); + DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. (uses vcol) */ + + sh = workbench_shader_opaque_image_get(wpd, hair, false); + + wpd->prepass[opaque][infront][hair].image_shgrp = grp = DRW_shgroup_create(sh, pass); + DRW_shgroup_uniform_block_persistent(grp, "material_block", wpd->material_ubo_curr); + DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */ + DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap); + + sh = workbench_shader_opaque_image_get(wpd, hair, true); + + wpd->prepass[opaque][infront][hair].image_tiled_shgrp = grp = DRW_shgroup_create(sh, pass); + DRW_shgroup_uniform_block_persistent(grp, "material_block", wpd->material_ubo_curr); + DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */ + DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap); + } + } + } + { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_GREATER | DRW_STATE_STENCIL_EQUAL; + + DRW_PASS_CREATE(psl->composite_ps, state); + + sh = workbench_shader_composite_get(wpd); + + grp = DRW_shgroup_create(sh, psl->composite_ps); + DRW_shgroup_uniform_block_persistent(grp, "world_block", wpd->world_ubo); + DRW_shgroup_uniform_texture_persistent(grp, "materialBuffer", wpd->material_buffer_tx); + DRW_shgroup_uniform_texture_persistent(grp, "normalBuffer", wpd->normal_buffer_tx); + DRW_shgroup_uniform_bool_copy(grp, "forceShadowing", false); + DRW_shgroup_stencil_mask(grp, 0x00); + + if (STUDIOLIGHT_TYPE_MATCAP_ENABLED(wpd)) { + BKE_studiolight_ensure_flag(wpd->studio_light, + STUDIOLIGHT_MATCAP_DIFFUSE_GPUTEXTURE | + STUDIOLIGHT_MATCAP_SPECULAR_GPUTEXTURE); + struct GPUTexture *diff_tx = wpd->studio_light->matcap_diffuse.gputexture; + struct GPUTexture *spec_tx = wpd->studio_light->matcap_specular.gputexture; + const bool use_spec = workbench_is_specular_highlight_enabled(wpd); + spec_tx = (use_spec && spec_tx) ? spec_tx : diff_tx; + DRW_shgroup_uniform_texture_persistent(grp, "matcapDiffuseImage", diff_tx); + DRW_shgroup_uniform_texture_persistent(grp, "matcapSpecularImage", spec_tx); + } + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + + if (SHADOW_ENABLED(wpd)) { + grp = DRW_shgroup_create_sub(grp); + DRW_shgroup_uniform_bool_copy(grp, "forceShadowing", true); + DRW_shgroup_state_disable(grp, DRW_STATE_STENCIL_EQUAL); + DRW_shgroup_state_enable(grp, DRW_STATE_STENCIL_NEQUAL); + DRW_shgroup_stencil_mask(grp, 0x00); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } + } + { + DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS | DRW_STATE_WRITE_STENCIL | + DRW_STATE_STENCIL_ALWAYS; + + DRW_PASS_CREATE(psl->merge_infront_ps, state); + + sh = workbench_shader_merge_infront_get(wpd); + + grp = DRW_shgroup_create(sh, psl->merge_infront_ps); + DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth_in_front); + DRW_shgroup_stencil_mask(grp, 0x00); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } +} diff --git a/source/blender/draw/engines/workbench/workbench_private.h b/source/blender/draw/engines/workbench/workbench_private.h index bb93864966e..a68b66e0f85 100644 --- a/source/blender/draw/engines/workbench/workbench_private.h +++ b/source/blender/draw/engines/workbench/workbench_private.h @@ -34,16 +34,14 @@ #include "workbench_engine.h" +extern struct DrawEngineType draw_engine_workbench; + #define WORKBENCH_ENGINE "BLENDER_WORKBENCH" -#define MAX_COMPOSITE_SHADERS (1 << 7) -#define MAX_PREPASS_SHADERS (1 << 8) -#define MAX_ACCUM_SHADERS (1 << 8) -#define MAX_CAVITY_SHADERS (1 << 3) - -#define TEXTURE_DRAWING_ENABLED(wpd) (wpd->shading.color_type == V3D_SHADING_TEXTURE_COLOR) -#define VERTEX_COLORS_ENABLED(wpd) (wpd->shading.color_type == V3D_SHADING_VERTEX_COLOR) -#define MATERIAL_COLORS_ENABLED(wpd) (wpd->shading.color_type == V3D_SHADING_MATERIAL_COLOR) -#define FLAT_ENABLED(wpd) (wpd->shading.light == V3D_LIGHTING_FLAT) + +#define MAX_MATERIAL (1 << 12) + +#define DEBUG_SHADOW_VOLUME 0 + #define STUDIOLIGHT_ENABLED(wpd) (wpd->shading.light == V3D_LIGHTING_STUDIO) #define MATCAP_ENABLED(wpd) (wpd->shading.light == V3D_LIGHTING_MATCAP) #define USE_WORLD_ORIENTATION(wpd) ((wpd->shading.flag & V3D_SHADING_WORLD_ORIENTATION) != 0) @@ -63,45 +61,24 @@ (wpd->shading.cavity_type == V3D_SHADING_CAVITY_BOTH))) #define CAVITY_ENABLED(wpd) (CURVATURE_ENABLED(wpd) || SSAO_ENABLED(wpd)) #define SHADOW_ENABLED(wpd) (wpd->shading.flag & V3D_SHADING_SHADOW) -#define GHOST_ENABLED(psl) \ - (!DRW_pass_is_empty(psl->ghost_prepass_pass) || !DRW_pass_is_empty(psl->ghost_prepass_hair_pass)) #define CULL_BACKFACE_ENABLED(wpd) ((wpd->shading.flag & V3D_SHADING_BACKFACE_CULLING) != 0) -#define OIT_ENABLED(wpd) \ - (ELEM(wpd->shading.color_type, \ - V3D_SHADING_MATERIAL_COLOR, \ - V3D_SHADING_OBJECT_COLOR, \ - V3D_SHADING_TEXTURE_COLOR, \ - V3D_SHADING_VERTEX_COLOR)) - -#define IS_NAVIGATING(wpd) \ - ((DRW_context_state_get()->rv3d) && \ - (DRW_context_state_get()->rv3d->rflag & (RV3D_NAVIGATING | RV3D_PAINTING))) #define OBJECT_OUTLINE_ENABLED(wpd) (wpd->shading.flag & V3D_SHADING_OBJECT_OUTLINE) #define OBJECT_ID_PASS_ENABLED(wpd) (OBJECT_OUTLINE_ENABLED(wpd) || CURVATURE_ENABLED(wpd)) -#define NORMAL_VIEWPORT_COMP_PASS_ENABLED(wpd) \ - (MATCAP_ENABLED(wpd) || STUDIOLIGHT_ENABLED(wpd) || SHADOW_ENABLED(wpd)) -#define NORMAL_VIEWPORT_PASS_ENABLED(wpd) \ - (NORMAL_VIEWPORT_COMP_PASS_ENABLED(wpd) || SSAO_ENABLED(wpd) || CURVATURE_ENABLED(wpd)) #define NORMAL_ENCODING_ENABLED() (true) -#define WORLD_CLIPPING_ENABLED(wpd) (wpd->world_clip_planes != NULL) struct RenderEngine; struct RenderLayer; struct rcti; typedef struct WORKBENCH_FramebufferList { - /* Deferred render buffers */ - struct GPUFrameBuffer *prepass_fb; - struct GPUFrameBuffer *ghost_prepass_fb; - struct GPUFrameBuffer *cavity_fb; - struct GPUFrameBuffer *composite_fb; - struct GPUFrameBuffer *id_clear_fb; + struct GPUFrameBuffer *opaque_fb; + struct GPUFrameBuffer *opaque_infront_fb; - struct GPUFrameBuffer *effect_fb; - struct GPUFrameBuffer *effect_taa_fb; - struct GPUFrameBuffer *depth_buffer_fb; - struct GPUFrameBuffer *color_only_fb; + struct GPUFrameBuffer *transp_accum_fb; + struct GPUFrameBuffer *transp_accum_infront_fb; + + struct GPUFrameBuffer *id_clear_fb; struct GPUFrameBuffer *dof_downsample_fb; struct GPUFrameBuffer *dof_coc_tile_h_fb; @@ -110,10 +87,9 @@ typedef struct WORKBENCH_FramebufferList { struct GPUFrameBuffer *dof_blur1_fb; struct GPUFrameBuffer *dof_blur2_fb; - /* Forward render buffers */ - struct GPUFrameBuffer *object_outline_fb; - struct GPUFrameBuffer *transparent_accum_fb; - struct GPUFrameBuffer *transparent_revealage_fb; + struct GPUFrameBuffer *antialiasing_fb; + struct GPUFrameBuffer *smaa_edge_fb; + struct GPUFrameBuffer *smaa_weight_fb; } WORKBENCH_FramebufferList; typedef struct WORKBENCH_TextureList { @@ -121,34 +97,35 @@ typedef struct WORKBENCH_TextureList { struct GPUTexture *coc_halfres_tx; struct GPUTexture *history_buffer_tx; struct GPUTexture *depth_buffer_tx; + struct GPUTexture *smaa_search_tx; + struct GPUTexture *smaa_area_tx; + struct GPUTexture *dummy_image_tx; + struct GPUTexture *dummy_volume_tx; + struct GPUTexture *dummy_coba_tx; } WORKBENCH_TextureList; typedef struct WORKBENCH_StorageList { - struct WORKBENCH_PrivateData *g_data; - struct WORKBENCH_EffectInfo *effects; + struct WORKBENCH_PrivateData *wpd; float *dof_ubo_data; } WORKBENCH_StorageList; typedef struct WORKBENCH_PassList { - /* deferred rendering */ - struct DRWPass *prepass_pass; - struct DRWPass *prepass_hair_pass; - struct DRWPass *ghost_prepass_pass; - struct DRWPass *ghost_prepass_hair_pass; - struct DRWPass *cavity_pass; - struct DRWPass *shadow_depth_pass_pass; - struct DRWPass *shadow_depth_pass_mani_pass; - struct DRWPass *shadow_depth_fail_pass; - struct DRWPass *shadow_depth_fail_mani_pass; - struct DRWPass *shadow_depth_fail_caps_pass; - struct DRWPass *shadow_depth_fail_caps_mani_pass; - struct DRWPass *composite_pass; - struct DRWPass *composite_shadow_pass; - struct DRWPass *oit_composite_pass; - struct DRWPass *background_pass; - struct DRWPass *background_pass_clip; - struct DRWPass *ghost_resolve_pass; - struct DRWPass *effect_aa_pass; + struct DRWPass *opaque_ps; + struct DRWPass *opaque_infront_ps; + + struct DRWPass *transp_resolve_ps; + struct DRWPass *transp_accum_ps; + struct DRWPass *transp_accum_infront_ps; + + struct DRWPass *shadow_ps[2]; + + struct DRWPass *merge_infront_ps; + + struct DRWPass *cavity_ps; + struct DRWPass *outline_ps; + + struct DRWPass *composite_ps; + struct DRWPass *dof_down_ps; struct DRWPass *dof_down2_ps; struct DRWPass *dof_flatten_v_ps; @@ -158,12 +135,13 @@ typedef struct WORKBENCH_PassList { struct DRWPass *dof_blur1_ps; struct DRWPass *dof_blur2_ps; struct DRWPass *dof_resolve_ps; - struct DRWPass *volume_pass; - /* forward rendering */ - struct DRWPass *transparent_accum_pass; - struct DRWPass *object_outline_pass; - struct DRWPass *depth_pass; + struct DRWPass *volume_ps; + + struct DRWPass *aa_accum_ps; + struct DRWPass *aa_edge_ps; + struct DRWPass *aa_weight_ps; + struct DRWPass *aa_resolve_ps; } WORKBENCH_PassList; typedef struct WORKBENCH_Data { @@ -180,84 +158,164 @@ typedef struct WORKBENCH_UBO_Light { float diffuse_color[3], wrapped; } WORKBENCH_UBO_Light; +typedef struct WORKBENCH_UBO_Material { + float base_color[3]; + /* Packed data into a int. Decoded in the shader. */ + uint32_t packed_data; +} WORKBENCH_UBO_Material; + typedef struct WORKBENCH_UBO_World { + float viewvecs[3][4]; + float viewport_size[2], viewport_size_inv[2]; float object_outline_color[4]; float shadow_direction_vs[4]; + float shadow_focus, shadow_shift, shadow_mul, shadow_add; WORKBENCH_UBO_Light lights[4]; float ambient_color[4]; - int num_lights; - int matcap_orientation; + + int cavity_sample_start; + int cavity_sample_end; + float cavity_sample_count_inv; + float cavity_jitter_scale; + + float cavity_valley_factor; + float cavity_ridge_factor; + float cavity_attenuation; + float cavity_distance; + float curvature_ridge; float curvature_valley; + float ui_scale; + float _pad0; + + int matcap_orientation; + int use_specular; /* Bools are 32bit ints in GLSL. */ + int _pad1; + int _pad2; } WORKBENCH_UBO_World; + BLI_STATIC_ASSERT_ALIGN(WORKBENCH_UBO_World, 16) +BLI_STATIC_ASSERT_ALIGN(WORKBENCH_UBO_Light, 16) +BLI_STATIC_ASSERT_ALIGN(WORKBENCH_UBO_Material, 16) -typedef struct WORKBENCH_PrivateData { +typedef struct WORKBENCH_Prepass { + /** Hash storing shading group for each Material or GPUTexture to reduce state changes. */ struct GHash *material_hash; - struct GHash *material_transp_hash; - struct GPUShader *prepass_sh; - struct GPUShader *prepass_hair_sh; - struct GPUShader *prepass_uniform_sh; - struct GPUShader *prepass_uniform_hair_sh; - struct GPUShader *prepass_textured_sh; - struct GPUShader *prepass_textured_array_sh; - struct GPUShader *prepass_vertex_sh; - struct GPUShader *composite_sh; - struct GPUShader *background_sh; - struct GPUShader *transparent_accum_sh; - struct GPUShader *transparent_accum_hair_sh; - struct GPUShader *transparent_accum_uniform_sh; - struct GPUShader *transparent_accum_uniform_hair_sh; - struct GPUShader *transparent_accum_textured_sh; - struct GPUShader *transparent_accum_textured_array_sh; - struct GPUShader *transparent_accum_vertex_sh; + /** First common (non-vcol and non-image colored) shading group to created subgroups. */ + struct DRWShadingGroup *common_shgrp; + /** First Vertex Color shading group to created subgroups. */ + struct DRWShadingGroup *vcol_shgrp; + /** First Image shading group to created subgroups. */ + struct DRWShadingGroup *image_shgrp; + /** First UDIM (tiled image) shading group to created subgroups. */ + struct DRWShadingGroup *image_tiled_shgrp; +} WORKBENCH_Prepass; + +typedef struct WORKBENCH_PrivateData { + /** ViewLayerData for faster access. */ + struct WORKBENCH_ViewLayerData *vldata; + /** Copy of draw_ctx->sh_cfg for faster access. */ + eGPUShaderConfig sh_cfg; + /** Global clip and cull states. */ + DRWState clip_state, cull_state; + /** Copy of scene->display.shading or v3d->shading for viewport. */ View3DShading shading; + /** Chosen studiolight or matcap. */ StudioLight *studio_light; + /** Copy of ctx_draw->scene for faster access. */ + struct Scene *scene; + /** Shorthand version of U global for user preferences. */ const UserDef *preferences; - /* Does this instance owns the `world_ubo` field. - * Normally the field is borrowed from `WORKBENCH_WorldData`. In case that - * there is no World attached to the scene the UBO cannot be cached and should - * be freed after using. */ - bool is_world_ubo_owner; + /** Copy of context mode for faster access. */ + eContextObjectMode ctx_mode; + /** Shorthand for wpd->vldata->world_ubo. */ struct GPUUniformBuffer *world_ubo; - struct DRWShadingGroup *shadow_shgrp; - struct DRWShadingGroup *depth_shgrp; - WORKBENCH_UBO_World world_data; - float shadow_multiplier; - float shadow_shift; - float shadow_focus; - float cached_shadow_direction[3]; + /** Background color to clear the color buffer with. */ + float background_color[4]; + + /* Shadow */ + /** Previous shadow direction to test if shadow has changed. */ + float shadow_cached_direction[3]; + /** Current shadow direction in world space. */ + float shadow_direction_ws[3]; + /** Shadow precomputed matrices. */ float shadow_mat[4][4]; float shadow_inv[4][4]; - /* Far plane of the view frustum. */ + /** Far plane of the view frustum. Used for shadow volume extrusion. */ float shadow_far_plane[4]; - /* Near plane corners in shadow space. */ - float shadow_near_corners[4][3]; - /* min and max of shadow_near_corners. allow fast test */ + /** Min and max of shadow_near_corners. Speed up culling test. */ float shadow_near_min[3]; float shadow_near_max[3]; - /* This is a parallelogram, so only 2 normal and distance to the edges. */ + /** This is a parallelogram, so only 2 normal and distance to the edges. */ float shadow_near_sides[2][4]; + /* Shadow shading groups. First array elem is for non-manifold geom and second for manifold. */ + struct DRWShadingGroup *shadow_pass_grp[2]; + struct DRWShadingGroup *shadow_fail_grp[2]; + struct DRWShadingGroup *shadow_fail_caps_grp[2]; + /** If the shadow has changed direction and ob bboxes needs to be updated. */ bool shadow_changed; - bool is_playback; - float (*world_clip_planes)[4]; + /* Temporal Antialiasing */ + /** Total number of samples to after which TAA stops accumulating samples. */ + int taa_sample_len; + /** Current TAA sample index in [0..taa_sample_len[ range. */ + int taa_sample; + /** Inverse of taa_sample to divide the accumulation buffer. */ + float taa_sample_inv; + /** If the view has been updated and TAA needs to be reset. */ + bool view_updated; + /** View */ + struct DRWView *view; + /** Last projection matrix to see if view is still valid. */ + float last_mat[4][4]; + + /* Smart Morphological Anti-Aliasing */ + /** Temp buffers to store edges and weights. */ + struct GPUTexture *smaa_edge_tx; + struct GPUTexture *smaa_weight_tx; + /** Weight of the smaa pass. */ + float smaa_mix_factor; + + /** Opaque pipeline buffers. */ + struct GPUTexture *material_buffer_tx; + struct GPUTexture *composite_buffer_tx; + struct GPUTexture *normal_buffer_tx; + /** Transparent pipeline buffers. */ + struct GPUTexture *accum_buffer_tx; + struct GPUTexture *reveal_buffer_tx; + /** Object IDs buffer for curvature & outline. */ + struct GPUTexture *object_id_tx; + + /** Prepass infos for each draw types [transparent][infront][hair]. */ + WORKBENCH_Prepass prepass[2][2][2]; + + /* Materials */ + /** Copy of vldata->material_ubo for faster access. */ + struct BLI_memblock *material_ubo; + /** Copy of vldata->material_ubo_data for faster access. */ + struct BLI_memblock *material_ubo_data; + /** Current material chunk being filled by workbench_material_setup_ex(). */ + WORKBENCH_UBO_Material *material_ubo_data_curr; + struct GPUUniformBuffer *material_ubo_curr; + /** Copy of txl->dummy_image_tx for faster access. */ + struct GPUTexture *dummy_image_tx; + /** Total number of used material chunk. */ + int material_chunk_count; + /** Index of current material chunk. */ + int material_chunk_curr; + /** Index of current material inside the material chunk. Only for material coloring mode. */ + int material_index; /* Volumes */ - bool volumes_do; + /** List of smoke domain textures to free after drawing. */ ListBase smoke_domains; - /* Ssao */ - float winmat[4][4]; - float viewvecs[3][4]; - float ssao_params[4]; - float ssao_settings[4]; - - /* Dof */ + /* Depth of Field */ + /** Depth of field temp buffers. */ struct GPUTexture *dof_blur_tx; struct GPUTexture *coc_temp_tx; struct GPUTexture *coc_tiles_tx[2]; - struct GPUUniformBuffer *dof_ubo; + /** Depth of field parameters. */ float dof_aperturesize; float dof_distance; float dof_invsensorsize; @@ -265,37 +323,15 @@ typedef struct WORKBENCH_PrivateData { float dof_blades; float dof_rotation; float dof_ratio; - bool dof_enabled; - /* Color Management */ - bool use_color_management; - bool use_color_render_settings; + /** True if any volume needs to be rendered. */ + bool volumes_do; + /** Convenience boolean. */ + bool dof_enabled; + bool is_playback; + bool is_navigating; } WORKBENCH_PrivateData; /* Transient data */ -typedef struct WORKBENCH_EffectInfo { - /** View */ - struct DRWView *view; - /** Last projection matrix to see if view is still valid. */ - float last_mat[4][4]; - int jitter_index; - float taa_mix_factor; - bool view_updated; -} WORKBENCH_EffectInfo; - -typedef struct WORKBENCH_MaterialData { - float base_color[3], metallic; - float roughness, alpha; - eV3DShadingColorType color_type; - int interp; - Image *ima; - ImageUser *iuser; - - /* Linked shgroup for drawing */ - DRWShadingGroup *shgrp; - /* forward rendering */ - DRWShadingGroup *shgrp_object_outline; -} WORKBENCH_MaterialData; - typedef struct WORKBENCH_ObjectData { DrawData dd; @@ -307,20 +343,21 @@ typedef struct WORKBENCH_ObjectData { bool shadow_bbox_dirty; } WORKBENCH_ObjectData; -typedef struct WORKBENCH_WorldData { - DrawData dd; - /* The cached `GPUUniformBuffer`, that is reused between draw calls. */ +typedef struct WORKBENCH_ViewLayerData { + /** Depth of field sample location array.*/ + struct GPUUniformBuffer *dof_sample_ubo; + /** All constant data used for a render loop.*/ struct GPUUniformBuffer *world_ubo; -} WORKBENCH_WorldData; - -/* Enumeration containing override options for base color rendering. - * This is used to during painting to force the base color to show what you are - * painting using the selected lighting model. */ -typedef enum WORKBENCH_ColorOverride { - WORKBENCH_COLOR_OVERRIDE_OFF = 0, - WORKBENCH_COLOR_OVERRIDE_TEXTURE = CTX_MODE_PAINT_TEXTURE, - WORKBENCH_COLOR_OVERRIDE_VERTEX = CTX_MODE_PAINT_VERTEX, -} WORKBENCH_ColorOverride; + /** Cavity sample location array.*/ + struct GPUUniformBuffer *cavity_sample_ubo; + /** Blue noise texture used to randomize the sampling of some effects.*/ + struct GPUTexture *cavity_jitter_tx; + /** Materials ubos allocated in a memblock for easy bookeeping. */ + struct BLI_memblock *material_ubo; + struct BLI_memblock *material_ubo_data; + /** Number of samples for which cavity_sample_ubo is valid. */ + int cavity_sample_count; +} WORKBENCH_ViewLayerData; /* inline helper functions */ BLI_INLINE bool workbench_is_specular_highlight_enabled(WORKBENCH_PrivateData *wpd) @@ -333,234 +370,129 @@ BLI_INLINE bool workbench_is_specular_highlight_enabled(WORKBENCH_PrivateData *w return false; } -BLI_INLINE bool workbench_is_taa_enabled(WORKBENCH_PrivateData *wpd) -{ - if (DRW_state_is_image_render()) { - const DRWContextState *draw_ctx = DRW_context_state_get(); - if (draw_ctx->v3d) { - return draw_ctx->scene->display.viewport_aa > SCE_DISPLAY_AA_FXAA; - } - else { - return draw_ctx->scene->display.render_aa > SCE_DISPLAY_AA_FXAA; - } - } - else { - return !(IS_NAVIGATING(wpd) || wpd->is_playback) && - wpd->preferences->viewport_aa > SCE_DISPLAY_AA_FXAA; - } -} - -BLI_INLINE bool workbench_is_fxaa_enabled(WORKBENCH_PrivateData *wpd) -{ - if (DRW_state_is_image_render()) { - const DRWContextState *draw_ctx = DRW_context_state_get(); - if (draw_ctx->v3d) { - return draw_ctx->scene->display.viewport_aa == SCE_DISPLAY_AA_FXAA; - } - else { - return draw_ctx->scene->display.render_aa == SCE_DISPLAY_AA_FXAA; - } - } - else { - if (wpd->preferences->viewport_aa == SCE_DISPLAY_AA_FXAA) { - return true; - } - - /* when navigating or animation playback use FXAA if scene uses TAA. */ - return (IS_NAVIGATING(wpd) || wpd->is_playback) && - wpd->preferences->viewport_aa > SCE_DISPLAY_AA_FXAA; - } -} - -/** Is texture paint mode enabled (globally) */ -BLI_INLINE bool workbench_is_in_texture_paint_mode(void) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - return draw_ctx->object_mode == OB_MODE_TEXTURE_PAINT; -} - -/** Is vertex paint mode enabled (globally) */ -BLI_INLINE bool workbench_is_in_vertex_paint_mode(void) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - return draw_ctx->object_mode == OB_MODE_VERTEX_PAINT; -} - -/* Must the `View3DShading.color_type` be overriden for the given object. */ -BLI_INLINE WORKBENCH_ColorOverride workbench_object_color_override_get(Object *ob) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - if (ob->type == OB_MESH && (draw_ctx->obact == ob)) { - const enum eContextObjectMode mode = CTX_data_mode_enum_ex( - draw_ctx->object_edit, draw_ctx->obact, draw_ctx->object_mode); - if (mode == CTX_MODE_PAINT_TEXTURE) { - return WORKBENCH_COLOR_OVERRIDE_TEXTURE; - } - else if (mode == CTX_MODE_PAINT_VERTEX) { - return WORKBENCH_COLOR_OVERRIDE_VERTEX; - } - } - - return WORKBENCH_COLOR_OVERRIDE_OFF; -} - -BLI_INLINE bool workbench_is_matdata_pass_enabled(WORKBENCH_PrivateData *wpd) -{ - return (wpd->shading.color_type != V3D_SHADING_SINGLE_COLOR || MATCAP_ENABLED(wpd)) || - workbench_is_in_texture_paint_mode() || workbench_is_in_vertex_paint_mode(); -} - -/** - * Get the default texture format to be used by the color and history buffers. - * - * Use GPU_RGBA16F for final renderings and for drawing textures. This - * allows displaying HDRI textures. Vertex Colors uses GPU_RGBA16 to resolve - * color banding issues (T66100). All other modes use GPU_RGBA8 to reduce - * bandwidth and gpu memory. - */ -BLI_INLINE eGPUTextureFormat workbench_color_texture_format(const WORKBENCH_PrivateData *wpd) -{ - eGPUTextureFormat result; - if (DRW_state_is_image_render() || workbench_is_in_texture_paint_mode() || - TEXTURE_DRAWING_ENABLED(wpd)) { - result = GPU_RGBA16F; - } - else { - result = GPU_RGBA16; - } - return result; -} - -/* workbench_deferred.c */ -void workbench_deferred_engine_init(WORKBENCH_Data *vedata); -void workbench_deferred_engine_free(void); -void workbench_deferred_draw_scene(WORKBENCH_Data *vedata); -void workbench_deferred_draw_finish(WORKBENCH_Data *vedata); -void workbench_deferred_cache_init(WORKBENCH_Data *vedata); -void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob); -void workbench_deferred_cache_finish(WORKBENCH_Data *vedata); - -/* workbench_forward.c */ -void workbench_forward_engine_init(WORKBENCH_Data *vedata); -void workbench_forward_engine_free(void); -void workbench_forward_draw_scene(WORKBENCH_Data *vedata); -void workbench_forward_draw_finish(WORKBENCH_Data *vedata); -void workbench_forward_cache_init(WORKBENCH_Data *vedata); -void workbench_forward_cache_populate(WORKBENCH_Data *vedata, Object *ob); -void workbench_forward_cache_finish(WORKBENCH_Data *vedata); - -/* For OIT in deferred */ -void workbench_forward_outline_shaders_ensure(WORKBENCH_PrivateData *wpd, eGPUShaderConfig sh_cfg); -void workbench_forward_choose_shaders(WORKBENCH_PrivateData *wpd, eGPUShaderConfig sh_cfg); -WORKBENCH_MaterialData *workbench_forward_get_or_create_material_data( - WORKBENCH_Data *vedata, - Object *ob, - Material *mat, - Image *ima, - ImageUser *iuser, - eV3DShadingColorType color_type, - int interp); - -/* workbench_effect_aa.c */ -void workbench_aa_create_pass(WORKBENCH_Data *vedata, GPUTexture **tx); -void workbench_aa_draw_pass(WORKBENCH_Data *vedata, GPUTexture *tx); - -/* workbench_effect_fxaa.c */ -void workbench_fxaa_engine_init(void); -void workbench_fxaa_engine_free(void); -DRWPass *workbench_fxaa_create_pass(GPUTexture **color_buffer_tx); - -/* workbench_effect_taa.c */ -void workbench_taa_engine_init(WORKBENCH_Data *vedata); -void workbench_taa_engine_free(void); -DRWPass *workbench_taa_create_pass(WORKBENCH_Data *vedata, GPUTexture **color_buffer_tx); -void workbench_taa_draw_scene_start(WORKBENCH_Data *vedata); -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); -int workbench_num_viewport_rendering_iterations(WORKBENCH_Data *vedata); - +/* workbench_opaque.c */ +void workbench_opaque_engine_init(WORKBENCH_Data *data); +void workbench_opaque_cache_init(WORKBENCH_Data *data); + +/* workbench_transparent.c */ +void workbench_transparent_engine_init(WORKBENCH_Data *data); +void workbench_transparent_cache_init(WORKBENCH_Data *data); +void workbench_transparent_draw_depth_pass(WORKBENCH_Data *data); + +/* workbench_shadow.c */ +void workbench_shadow_data_update(WORKBENCH_PrivateData *wpd, WORKBENCH_UBO_World *wd); +void workbench_shadow_cache_init(WORKBENCH_Data *data); +void workbench_shadow_cache_populate(WORKBENCH_Data *data, Object *ob, const bool has_transp_mat); + +/* workbench_shader.c */ +GPUShader *workbench_shader_opaque_get(WORKBENCH_PrivateData *wpd, bool hair); +GPUShader *workbench_shader_opaque_image_get(WORKBENCH_PrivateData *wpd, bool hair, bool tiled); +GPUShader *workbench_shader_composite_get(WORKBENCH_PrivateData *wpd); +GPUShader *workbench_shader_merge_infront_get(WORKBENCH_PrivateData *wpd); + +GPUShader *workbench_shader_transparent_get(WORKBENCH_PrivateData *wpd, bool hair); +GPUShader *workbench_shader_transparent_image_get(WORKBENCH_PrivateData *wpd, + bool hair, + bool tiled); +GPUShader *workbench_shader_transparent_resolve_get(WORKBENCH_PrivateData *wpd); + +GPUShader *workbench_shader_shadow_pass_get(bool manifold); +GPUShader *workbench_shader_shadow_fail_get(bool manifold, bool cap); + +GPUShader *workbench_shader_cavity_get(bool cavity, bool curvature); +GPUShader *workbench_shader_outline_get(void); + +GPUShader *workbench_shader_antialiasing_accumulation_get(void); +GPUShader *workbench_shader_antialiasing_get(int stage); + +GPUShader *workbench_shader_volume_get(bool slice, bool coba, bool cubic); + +void workbench_shader_depth_of_field_get(GPUShader **prepare_sh, + GPUShader **downsample_sh, + GPUShader **blur1_sh, + GPUShader **blur2_sh, + GPUShader **resolve_sh); + +void workbench_shader_library_ensure(void); +void workbench_shader_free(void); + +/* workbench_effect_antialiasing.c */ +int workbench_antialiasing_sample_count_get(WORKBENCH_PrivateData *wpd); +void workbench_antialiasing_engine_init(WORKBENCH_Data *vedata); +void workbench_antialiasing_cache_init(WORKBENCH_Data *vedata); +void workbench_antialiasing_view_updated(WORKBENCH_Data *vedata); +bool workbench_antialiasing_setup(WORKBENCH_Data *vedata); +void workbench_antialiasing_draw_pass(WORKBENCH_Data *vedata); + +/* workbench_effect_cavity.c */ +void workbench_cavity_data_update(WORKBENCH_PrivateData *wpd, WORKBENCH_UBO_World *wd); +void workbench_cavity_samples_ubo_ensure(WORKBENCH_PrivateData *wpd); +void workbench_cavity_cache_init(WORKBENCH_Data *data); + +/* workbench_effect_outline.c */ +void workbench_outline_cache_init(WORKBENCH_Data *data); /* 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, - GPUTexture *noise_tex); +void workbench_dof_engine_init(WORKBENCH_Data *vedata); +void workbench_dof_cache_init(WORKBENCH_Data *vedata); void workbench_dof_draw_pass(WORKBENCH_Data *vedata); /* workbench_materials.c */ -eV3DShadingColorType workbench_material_determine_color_type(WORKBENCH_PrivateData *wpd, - Image *ima, - Object *ob, - bool use_sculpt_pbvh); -void workbench_material_get_image_and_mat( - Object *ob, int mat_nr, Image **r_image, ImageUser **r_iuser, int *r_interp, Material **r_mat); -char *workbench_material_build_defines(WORKBENCH_PrivateData *wpd, - bool is_uniform_color, - bool is_hair, - bool is_tiled, - const WORKBENCH_ColorOverride color_override); -void workbench_material_update_data(WORKBENCH_PrivateData *wpd, - Object *ob, - Material *mat, - WORKBENCH_MaterialData *data, - int color_type); -uint workbench_material_get_hash(WORKBENCH_MaterialData *material_template, bool is_ghost); -int workbench_material_get_composite_shader_index(WORKBENCH_PrivateData *wpd); -int workbench_material_get_prepass_shader_index(WORKBENCH_PrivateData *wpd, - bool is_uniform_color, - bool is_hair, - bool is_tiled, - const WORKBENCH_ColorOverride color_override); -int workbench_material_get_accum_shader_index(WORKBENCH_PrivateData *wpd, - bool is_uniform_color, - bool is_hair, - bool is_tiled, - const WORKBENCH_ColorOverride color_override); -void workbench_material_shgroup_uniform(WORKBENCH_PrivateData *wpd, - DRWShadingGroup *grp, - WORKBENCH_MaterialData *material, - Object *ob, - const bool deferred, - const bool is_tiled, - const int interp); -void workbench_material_copy(WORKBENCH_MaterialData *dest_material, - const WORKBENCH_MaterialData *source_material); - -/* workbench_studiolight.c */ -void studiolight_update_world(WORKBENCH_PrivateData *wpd, - StudioLight *sl, - WORKBENCH_UBO_World *wd); -void studiolight_update_light(WORKBENCH_PrivateData *wpd, const float light_direction[3]); -bool studiolight_object_cast_visible_shadow(WORKBENCH_PrivateData *wpd, - Object *ob, - WORKBENCH_ObjectData *oed); -float studiolight_object_shadow_distance(WORKBENCH_PrivateData *wpd, - Object *ob, - WORKBENCH_ObjectData *oed); -bool studiolight_camera_in_object_shadow(WORKBENCH_PrivateData *wpd, - Object *ob, - WORKBENCH_ObjectData *oed); +void workbench_material_ubo_data(WORKBENCH_PrivateData *wpd, + Object *ob, + Material *mat, + WORKBENCH_UBO_Material *data, + eV3DShadingColorType color_type); + +DRWShadingGroup *workbench_material_setup_ex(WORKBENCH_PrivateData *wpd, + Object *ob, + int mat_nr, + eV3DShadingColorType color_type, + bool hair, + bool *r_transp); +DRWShadingGroup *workbench_image_setup_ex(WORKBENCH_PrivateData *wpd, + Object *ob, + int mat_nr, + Image *ima, + ImageUser *iuser, + int interp, + bool hair); + +#define workbench_material_setup(wpd, ob, mat_nr, color_type, r_transp) \ + workbench_material_setup_ex(wpd, ob, mat_nr, color_type, false, r_transp) +#define workbench_image_setup(wpd, ob, mat_nr, ima, iuser, interp) \ + workbench_image_setup_ex(wpd, ob, mat_nr, ima, iuser, interp, false) + +#define workbench_material_hair_setup(wpd, ob, mat_nr, color_type) \ + workbench_material_setup_ex(wpd, ob, mat_nr, color_type, true, 0) +#define workbench_image_hair_setup(wpd, ob, mat_nr, ima, iuser, interp) \ + workbench_image_setup_ex(wpd, ob, mat_nr, ima, iuser, interp, true) /* workbench_data.c */ -void workbench_effect_info_init(WORKBENCH_EffectInfo *effect_info); void workbench_private_data_init(WORKBENCH_PrivateData *wpd); -void workbench_private_data_free(WORKBENCH_PrivateData *wpd); -void workbench_private_data_get_light_direction(float r_light_direction[3]); -void workbench_clear_color_get(float color[4]); +void workbench_update_world_ubo(WORKBENCH_PrivateData *wpd); +void workbench_update_material_ubos(WORKBENCH_PrivateData *wpd); +struct GPUUniformBuffer *workbench_material_ubo_alloc(WORKBENCH_PrivateData *wpd); /* workbench_volume.c */ -void workbench_volume_engine_init(void); -void workbench_volume_engine_free(void); +void workbench_volume_engine_init(WORKBENCH_Data *vedata); void workbench_volume_cache_init(WORKBENCH_Data *vedata); void workbench_volume_cache_populate(WORKBENCH_Data *vedata, - Scene *scene, - Object *ob, + struct Scene *UNUSED(scene), + struct Object *ob, struct ModifierData *md); -void workbench_volume_smoke_textures_free(WORKBENCH_PrivateData *wpd); +void workbench_volume_draw_pass(WORKBENCH_Data *vedata); +void workbench_volume_draw_finish(WORKBENCH_Data *vedata); + +/* workbench_engine.c */ +void workbench_engine_init(void *ved); +void workbench_cache_init(void *ved); +void workbench_cache_populate(void *ved, Object *ob); +void workbench_cache_finish(void *ved); +void workbench_draw_sample(void *ved); +void workbench_draw_finish(void *ved); /* workbench_render.c */ -void workbench_render(WORKBENCH_Data *vedata, +void workbench_render(void *ved, struct RenderEngine *engine, struct RenderLayer *render_layer, const struct rcti *rect); diff --git a/source/blender/draw/engines/workbench/workbench_render.c b/source/blender/draw/engines/workbench/workbench_render.c index 6e005e7ccaf..5a315e80a47 100644 --- a/source/blender/draw/engines/workbench/workbench_render.c +++ b/source/blender/draw/engines/workbench/workbench_render.c @@ -41,20 +41,12 @@ #include "workbench_private.h" -static void workbench_render_deferred_cache(void *vedata, - struct Object *ob, - struct RenderEngine *UNUSED(engine), - struct Depsgraph *UNUSED(depsgraph)) +static void workbench_render_cache(void *vedata, + struct Object *ob, + struct RenderEngine *UNUSED(engine), + struct Depsgraph *UNUSED(depsgraph)) { - workbench_deferred_solid_cache_populate(vedata, ob); -} - -static void workbench_render_forward_cache(void *vedata, - struct Object *ob, - struct RenderEngine *UNUSED(engine), - struct Depsgraph *UNUSED(depsgraph)) -{ - workbench_forward_cache_populate(vedata, ob); + workbench_cache_populate(vedata, ob); } static void workbench_render_matrices_init(RenderEngine *engine, Depsgraph *depsgraph) @@ -171,18 +163,11 @@ static void workbench_render_result_z(struct RenderLayer *rl, } } -static void workbench_render_framebuffers_finish(void) -{ -} - -void workbench_render(WORKBENCH_Data *data, - RenderEngine *engine, - RenderLayer *render_layer, - const rcti *rect) +void workbench_render(void *ved, RenderEngine *engine, RenderLayer *render_layer, const rcti *rect) { + WORKBENCH_Data *data = ved; DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); const DRWContextState *draw_ctx = DRW_context_state_get(); - const Scene *scene = draw_ctx->scene; Depsgraph *depsgraph = draw_ctx->depsgraph; workbench_render_matrices_init(engine, depsgraph); @@ -191,60 +176,32 @@ void workbench_render(WORKBENCH_Data *data, return; } - const bool deferred = !XRAY_FLAG_ENABLED(&scene->display); + workbench_engine_init(data); - if (deferred) { - /* Init engine. */ - workbench_deferred_engine_init(data); + workbench_cache_init(data); + DRW_render_object_iter(data, engine, depsgraph, workbench_render_cache); + workbench_cache_finish(data); - /* Init objects. */ - workbench_deferred_cache_init(data); - DRW_render_object_iter(data, engine, depsgraph, workbench_render_deferred_cache); - workbench_deferred_cache_finish(data); - DRW_render_instance_buffer_finish(); + DRW_render_instance_buffer_finish(); - /* Also we weed to have a correct fbo bound for DRW_hair_update */ - GPU_framebuffer_bind(dfbl->color_only_fb); - DRW_hair_update(); - - /* Draw. */ - int num_samples = workbench_taa_calculate_num_iterations(data); - for (int sample = 0; sample < num_samples; sample++) { - if (RE_engine_test_break(engine)) { - break; - } - workbench_deferred_draw_scene(data); - } + /* Also we weed to have a correct fbo bound for DRW_hair_update */ + GPU_framebuffer_bind(dfbl->default_fb); + DRW_hair_update(); - workbench_deferred_draw_finish(data); - } - else { - /* Init engine. */ - workbench_forward_engine_init(data); - - /* Init objects. */ - workbench_forward_cache_init(data); - DRW_render_object_iter(data, engine, depsgraph, workbench_render_forward_cache); - workbench_forward_cache_finish(data); - DRW_render_instance_buffer_finish(); - - /* Also we weed to have a correct fbo bound for DRW_hair_update */ - GPU_framebuffer_bind(dfbl->color_only_fb); - DRW_hair_update(); - - /* Draw. */ - int num_samples = workbench_taa_calculate_num_iterations(data); - for (int sample = 0; sample < num_samples; sample++) { - if (RE_engine_test_break(engine)) { - break; - } + GPU_framebuffer_bind(dfbl->default_fb); + GPU_framebuffer_clear_depth(dfbl->default_fb, 1.0f); - workbench_forward_draw_scene(data); + WORKBENCH_PrivateData *wpd = data->stl->wpd; + while (wpd->taa_sample < max_ii(1, wpd->taa_sample_len)) { + if (RE_engine_test_break(engine)) { + break; } - - workbench_forward_draw_finish(data); + workbench_update_world_ubo(wpd); + workbench_draw_sample(data); } + workbench_draw_finish(data); + /* Write render output. */ const char *viewname = RE_GetActiveRenderView(engine->re); RenderPass *rp = RE_pass_find_by_name(render_layer, RE_PASSNAME_COMBINED, viewname); @@ -260,8 +217,6 @@ void workbench_render(WORKBENCH_Data *data, rp->rect); workbench_render_result_z(render_layer, viewname, rect); - - workbench_render_framebuffers_finish(); } void workbench_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer) diff --git a/source/blender/draw/engines/workbench/workbench_shader.c b/source/blender/draw/engines/workbench/workbench_shader.c new file mode 100644 index 00000000000..14a980fe628 --- /dev/null +++ b/source/blender/draw/engines/workbench/workbench_shader.c @@ -0,0 +1,533 @@ +/* + * 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. + * + * Copyright 2020, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + */ + +#include "DRW_render.h" + +#include "BLI_dynstr.h" + +#include "workbench_engine.h" +#include "workbench_private.h" + +extern char datatoc_common_hair_lib_glsl[]; +extern char datatoc_common_view_lib_glsl[]; +extern char datatoc_common_smaa_lib_glsl[]; + +extern char datatoc_workbench_prepass_vert_glsl[]; +extern char datatoc_workbench_prepass_hair_vert_glsl[]; +extern char datatoc_workbench_prepass_frag_glsl[]; + +extern char datatoc_workbench_effect_cavity_frag_glsl[]; +extern char datatoc_workbench_effect_outline_frag_glsl[]; +extern char datatoc_workbench_effect_dof_frag_glsl[]; +extern char datatoc_workbench_effect_taa_frag_glsl[]; +extern char datatoc_workbench_effect_smaa_frag_glsl[]; +extern char datatoc_workbench_effect_smaa_vert_glsl[]; + +extern char datatoc_workbench_composite_frag_glsl[]; + +extern char datatoc_workbench_transparent_accum_frag_glsl[]; +extern char datatoc_workbench_transparent_resolve_frag_glsl[]; + +extern char datatoc_workbench_merge_infront_frag_glsl[]; + +extern char datatoc_workbench_shadow_vert_glsl[]; +extern char datatoc_workbench_shadow_geom_glsl[]; +extern char datatoc_workbench_shadow_caps_geom_glsl[]; +extern char datatoc_workbench_shadow_debug_frag_glsl[]; + +extern char datatoc_workbench_volume_vert_glsl[]; +extern char datatoc_workbench_volume_frag_glsl[]; + +extern char datatoc_workbench_cavity_lib_glsl[]; +extern char datatoc_workbench_common_lib_glsl[]; +extern char datatoc_workbench_curvature_lib_glsl[]; +extern char datatoc_workbench_data_lib_glsl[]; +extern char datatoc_workbench_image_lib_glsl[]; +extern char datatoc_workbench_matcap_lib_glsl[]; +extern char datatoc_workbench_material_lib_glsl[]; +extern char datatoc_workbench_shader_interface_lib_glsl[]; +extern char datatoc_workbench_world_light_lib_glsl[]; + +extern char datatoc_gpu_shader_depth_only_frag_glsl[]; +extern char datatoc_gpu_shader_common_obinfos_lib_glsl[]; + +/* Maximum number of variations. */ +#define MAX_LIGHTING 3 +#define MAX_COLOR 3 +#define MAX_GEOM 2 + +enum { + VOLUME_SH_SLICE = 0, + VOLUME_SH_COBA, + VOLUME_SH_CUBIC, +}; + +#define VOLUME_SH_MAX (1 << (VOLUME_SH_CUBIC + 1)) + +static struct { + struct GPUShader *opaque_prepass_sh_cache[GPU_SHADER_CFG_LEN][MAX_GEOM][MAX_COLOR]; + struct GPUShader *transp_prepass_sh_cache[GPU_SHADER_CFG_LEN][MAX_GEOM][MAX_LIGHTING][MAX_COLOR]; + + struct GPUShader *opaque_composite_sh[MAX_LIGHTING]; + struct GPUShader *oit_resolve_sh; + struct GPUShader *outline_sh; + struct GPUShader *merge_infront_sh; + + struct GPUShader *shadow_depth_pass_sh[2]; + struct GPUShader *shadow_depth_fail_sh[2][2]; + + struct GPUShader *cavity_sh[2][2]; + + struct GPUShader *dof_prepare_sh; + struct GPUShader *dof_downsample_sh; + struct GPUShader *dof_blur1_sh; + struct GPUShader *dof_blur2_sh; + struct GPUShader *dof_resolve_sh; + + struct GPUShader *aa_accum_sh; + struct GPUShader *smaa_sh[3]; + + struct GPUShader *volume_sh[2][2][2]; + + struct DRWShaderLibrary *lib; +} e_data = {{{{NULL}}}}; + +void workbench_shader_library_ensure(void) +{ + if (e_data.lib == NULL) { + e_data.lib = DRW_shader_library_create(); + /* NOTE: Theses needs to be ordered by dependencies. */ + DRW_SHADER_LIB_ADD(e_data.lib, common_hair_lib); + DRW_SHADER_LIB_ADD(e_data.lib, common_view_lib); + DRW_SHADER_LIB_ADD(e_data.lib, gpu_shader_common_obinfos_lib); + DRW_SHADER_LIB_ADD(e_data.lib, workbench_shader_interface_lib); + DRW_SHADER_LIB_ADD(e_data.lib, workbench_common_lib); + DRW_SHADER_LIB_ADD(e_data.lib, workbench_image_lib); + DRW_SHADER_LIB_ADD(e_data.lib, workbench_material_lib); + DRW_SHADER_LIB_ADD(e_data.lib, workbench_data_lib); + DRW_SHADER_LIB_ADD(e_data.lib, workbench_matcap_lib); + DRW_SHADER_LIB_ADD(e_data.lib, workbench_cavity_lib); + DRW_SHADER_LIB_ADD(e_data.lib, workbench_curvature_lib); + DRW_SHADER_LIB_ADD(e_data.lib, workbench_world_light_lib); + } +} + +static char *workbench_build_defines( + WORKBENCH_PrivateData *wpd, bool textured, bool tiled, bool cavity, bool curvature) +{ + char *str = NULL; + + DynStr *ds = BLI_dynstr_new(); + + if (wpd && wpd->shading.light == V3D_LIGHTING_STUDIO) { + BLI_dynstr_append(ds, "#define V3D_LIGHTING_STUDIO\n"); + } + else if (wpd && wpd->shading.light == V3D_LIGHTING_MATCAP) { + BLI_dynstr_append(ds, "#define V3D_LIGHTING_MATCAP\n"); + } + else { + BLI_dynstr_append(ds, "#define V3D_LIGHTING_FLAT\n"); + } + + if (NORMAL_ENCODING_ENABLED()) { + BLI_dynstr_append(ds, "#define WORKBENCH_ENCODE_NORMALS\n"); + } + + if (textured) { + BLI_dynstr_append(ds, "#define V3D_SHADING_TEXTURE_COLOR\n"); + } + if (tiled) { + BLI_dynstr_append(ds, "#define TEXTURE_IMAGE_ARRAY\n"); + } + if (cavity) { + BLI_dynstr_append(ds, "#define USE_CAVITY\n"); + } + if (curvature) { + BLI_dynstr_append(ds, "#define USE_CURVATURE\n"); + } + + str = BLI_dynstr_get_cstring(ds); + BLI_dynstr_free(ds); + return str; +} + +static int workbench_color_index(WORKBENCH_PrivateData *UNUSED(wpd), bool textured, bool tiled) +{ + BLI_assert(2 < MAX_COLOR); + return (textured) ? (tiled ? 2 : 1) : 0; +} + +static GPUShader *workbench_shader_get_ex( + WORKBENCH_PrivateData *wpd, bool transp, bool hair, bool textured, bool tiled) +{ + int color = workbench_color_index(wpd, textured, tiled); + int light = wpd->shading.light; + BLI_assert(light < MAX_LIGHTING); + struct GPUShader **shader = + (transp) ? &e_data.transp_prepass_sh_cache[wpd->sh_cfg][hair][light][color] : + &e_data.opaque_prepass_sh_cache[wpd->sh_cfg][hair][color]; + + if (*shader == NULL) { + char *defines = workbench_build_defines(wpd, textured, tiled, false, false); + + char *frag_file = transp ? datatoc_workbench_transparent_accum_frag_glsl : + datatoc_workbench_prepass_frag_glsl; + char *frag_src = DRW_shader_library_create_shader_string(e_data.lib, frag_file); + + char *vert_file = hair ? datatoc_workbench_prepass_hair_vert_glsl : + datatoc_workbench_prepass_vert_glsl; + char *vert_src = DRW_shader_library_create_shader_string(e_data.lib, vert_file); + + const GPUShaderConfigData *sh_cfg_data = &GPU_shader_cfg_data[wpd->sh_cfg]; + + *shader = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg_data->lib, vert_src, NULL}, + .frag = (const char *[]){frag_src, NULL}, + .defs = (const char *[]){sh_cfg_data->def, + defines, + transp ? "#define TRANSPARENT_MATERIAL\n" : + "#define OPAQUE_MATERIAL\n", + NULL}, + }); + + MEM_freeN(defines); + MEM_freeN(frag_src); + MEM_freeN(vert_src); + } + return *shader; +} + +GPUShader *workbench_shader_opaque_get(WORKBENCH_PrivateData *wpd, bool hair) +{ + return workbench_shader_get_ex(wpd, false, hair, false, false); +} + +GPUShader *workbench_shader_opaque_image_get(WORKBENCH_PrivateData *wpd, bool hair, bool tiled) +{ + return workbench_shader_get_ex(wpd, false, hair, true, tiled); +} + +GPUShader *workbench_shader_transparent_get(WORKBENCH_PrivateData *wpd, bool hair) +{ + return workbench_shader_get_ex(wpd, true, hair, false, false); +} + +GPUShader *workbench_shader_transparent_image_get(WORKBENCH_PrivateData *wpd, + bool hair, + bool tiled) +{ + return workbench_shader_get_ex(wpd, true, hair, true, tiled); +} + +GPUShader *workbench_shader_composite_get(WORKBENCH_PrivateData *wpd) +{ + int light = wpd->shading.light; + struct GPUShader **shader = &e_data.opaque_composite_sh[light]; + BLI_assert(light < MAX_LIGHTING); + + if (*shader == NULL) { + char *defines = workbench_build_defines(wpd, false, false, false, false); + char *frag = DRW_shader_library_create_shader_string(e_data.lib, + datatoc_workbench_composite_frag_glsl); + + *shader = DRW_shader_create_fullscreen(frag, defines); + + MEM_freeN(defines); + MEM_freeN(frag); + } + return *shader; +} + +GPUShader *workbench_shader_merge_infront_get(WORKBENCH_PrivateData *UNUSED(wpd)) +{ + if (e_data.merge_infront_sh == NULL) { + char *frag = DRW_shader_library_create_shader_string( + e_data.lib, datatoc_workbench_merge_infront_frag_glsl); + + e_data.merge_infront_sh = DRW_shader_create_fullscreen(frag, NULL); + + MEM_freeN(frag); + } + return e_data.merge_infront_sh; +} + +GPUShader *workbench_shader_transparent_resolve_get(WORKBENCH_PrivateData *wpd) +{ + if (e_data.oit_resolve_sh == NULL) { + char *defines = workbench_build_defines(wpd, false, false, false, false); + + e_data.oit_resolve_sh = DRW_shader_create_fullscreen( + datatoc_workbench_transparent_resolve_frag_glsl, defines); + + MEM_freeN(defines); + } + return e_data.oit_resolve_sh; +} + +static GPUShader *workbench_shader_shadow_pass_get_ex(bool depth_pass, bool manifold, bool cap) +{ + struct GPUShader **shader = (depth_pass) ? &e_data.shadow_depth_pass_sh[manifold] : + &e_data.shadow_depth_fail_sh[manifold][cap]; + + if (*shader == NULL) { +#if DEBUG_SHADOW_VOLUME + const char *shadow_frag = datatoc_workbench_shadow_debug_frag_glsl; +#else + const char *shadow_frag = datatoc_gpu_shader_depth_only_frag_glsl; +#endif + + *shader = GPU_shader_create_from_arrays({ + .vert = (const char *[]){datatoc_common_view_lib_glsl, + datatoc_workbench_shadow_vert_glsl, + NULL}, + .geom = (const char *[]){(cap) ? datatoc_workbench_shadow_caps_geom_glsl : + datatoc_workbench_shadow_geom_glsl, + NULL}, + .frag = (const char *[]){shadow_frag, NULL}, + .defs = (const char *[]){(depth_pass) ? "#define SHADOW_PASS\n" : "#define SHADOW_FAIL\n", + (manifold) ? "" : "#define DOUBLE_MANIFOLD\n", + NULL}, + }); + } + return *shader; +} + +GPUShader *workbench_shader_shadow_pass_get(bool manifold) +{ + return workbench_shader_shadow_pass_get_ex(true, manifold, false); +} + +GPUShader *workbench_shader_shadow_fail_get(bool manifold, bool cap) +{ + return workbench_shader_shadow_pass_get_ex(false, manifold, cap); +} + +GPUShader *workbench_shader_cavity_get(bool cavity, bool curvature) +{ + BLI_assert(cavity || curvature); + struct GPUShader **shader = &e_data.cavity_sh[cavity][curvature]; + + if (*shader == NULL) { + char *defines = workbench_build_defines(NULL, false, false, cavity, curvature); + char *frag = DRW_shader_library_create_shader_string( + e_data.lib, datatoc_workbench_effect_cavity_frag_glsl); + + *shader = DRW_shader_create_fullscreen(frag, defines); + + MEM_freeN(defines); + MEM_freeN(frag); + } + return *shader; +} + +GPUShader *workbench_shader_outline_get(void) +{ + if (e_data.outline_sh == NULL) { + char *frag = DRW_shader_library_create_shader_string( + e_data.lib, datatoc_workbench_effect_outline_frag_glsl); + + e_data.outline_sh = DRW_shader_create_fullscreen(frag, NULL); + + MEM_freeN(frag); + } + return e_data.outline_sh; +} + +void workbench_shader_depth_of_field_get(GPUShader **prepare_sh, + GPUShader **downsample_sh, + GPUShader **blur1_sh, + GPUShader **blur2_sh, + GPUShader **resolve_sh) +{ + if (e_data.dof_prepare_sh == NULL) { + e_data.dof_prepare_sh = DRW_shader_create_fullscreen(datatoc_workbench_effect_dof_frag_glsl, + "#define PREPARE\n"); + + e_data.dof_downsample_sh = DRW_shader_create_fullscreen(datatoc_workbench_effect_dof_frag_glsl, + "#define DOWNSAMPLE\n"); +#if 0 /* TODO(fclem) finish COC min_max optimization */ + e_data.dof_flatten_v_sh = DRW_shader_create_fullscreen(datatoc_workbench_effect_dof_frag_glsl, + "#define FLATTEN_VERTICAL\n"); + + e_data.dof_flatten_h_sh = DRW_shader_create_fullscreen(datatoc_workbench_effect_dof_frag_glsl, + "#define FLATTEN_HORIZONTAL\n"); + + e_data.dof_dilate_v_sh = DRW_shader_create_fullscreen(datatoc_workbench_effect_dof_frag_glsl, + "#define DILATE_VERTICAL\n"); + + e_data.dof_dilate_h_sh = DRW_shader_create_fullscreen(datatoc_workbench_effect_dof_frag_glsl, + "#define DILATE_HORIZONTAL\n"); +#endif + e_data.dof_blur1_sh = DRW_shader_create_fullscreen(datatoc_workbench_effect_dof_frag_glsl, + "#define BLUR1\n"); + + e_data.dof_blur2_sh = DRW_shader_create_fullscreen(datatoc_workbench_effect_dof_frag_glsl, + "#define BLUR2\n"); + + e_data.dof_resolve_sh = DRW_shader_create_fullscreen(datatoc_workbench_effect_dof_frag_glsl, + "#define RESOLVE\n"); + } + + *prepare_sh = e_data.dof_prepare_sh; + *downsample_sh = e_data.dof_downsample_sh; + *blur1_sh = e_data.dof_blur1_sh; + *blur2_sh = e_data.dof_blur2_sh; + *resolve_sh = e_data.dof_resolve_sh; +} + +GPUShader *workbench_shader_antialiasing_accumulation_get(void) +{ + if (e_data.aa_accum_sh == NULL) { + char *frag = DRW_shader_library_create_shader_string(e_data.lib, + datatoc_workbench_effect_taa_frag_glsl); + + e_data.aa_accum_sh = DRW_shader_create_fullscreen(frag, NULL); + + MEM_freeN(frag); + } + return e_data.aa_accum_sh; +} + +GPUShader *workbench_shader_antialiasing_get(int stage) +{ + BLI_assert(stage < 3); + if (!e_data.smaa_sh[stage]) { + char stage_define[32]; + BLI_snprintf(stage_define, sizeof(stage_define), "#define SMAA_STAGE %d\n", stage); + + e_data.smaa_sh[stage] = GPU_shader_create_from_arrays({ + .vert = + (const char *[]){ + "#define SMAA_INCLUDE_VS 1\n", + "#define SMAA_INCLUDE_PS 0\n", + "uniform vec4 viewportMetrics;\n", + datatoc_common_smaa_lib_glsl, + datatoc_workbench_effect_smaa_vert_glsl, + NULL, + }, + .frag = + (const char *[]){ + "#define SMAA_INCLUDE_VS 0\n", + "#define SMAA_INCLUDE_PS 1\n", + "uniform vec4 viewportMetrics;\n", + datatoc_common_smaa_lib_glsl, + datatoc_workbench_effect_smaa_frag_glsl, + NULL, + }, + .defs = + (const char *[]){ + "#define SMAA_GLSL_3\n", + "#define SMAA_RT_METRICS viewportMetrics\n", + "#define SMAA_PRESET_HIGH\n", + "#define SMAA_LUMA_WEIGHT float4(1.0, 1.0, 1.0, 1.0)\n", + "#define SMAA_NO_DISCARD\n", + stage_define, + NULL, + }, + }); + } + return e_data.smaa_sh[stage]; +} + +GPUShader *workbench_shader_volume_get(bool slice, bool coba, bool cubic) +{ + GPUShader **shader = &e_data.volume_sh[slice][coba][cubic]; + + if (*shader == NULL) { + DynStr *ds = BLI_dynstr_new(); + + if (slice) { + BLI_dynstr_append(ds, "#define VOLUME_SLICE\n"); + } + if (coba) { + BLI_dynstr_append(ds, "#define USE_COBA\n"); + } + if (cubic) { + BLI_dynstr_append(ds, "#define USE_TRICUBIC\n"); + } + + char *defines = BLI_dynstr_get_cstring(ds); + BLI_dynstr_free(ds); + + char *vert = DRW_shader_library_create_shader_string(e_data.lib, + datatoc_workbench_volume_vert_glsl); + char *frag = DRW_shader_library_create_shader_string(e_data.lib, + datatoc_workbench_volume_frag_glsl); + + *shader = DRW_shader_create(vert, NULL, frag, defines); + + MEM_freeN(vert); + MEM_freeN(frag); + MEM_freeN(defines); + } + return *shader; +} + +void workbench_shader_free(void) +{ + for (int j = 0; j < sizeof(e_data.opaque_prepass_sh_cache) / sizeof(void *); j++) { + struct GPUShader **sh_array = &e_data.opaque_prepass_sh_cache[0][0][0]; + DRW_SHADER_FREE_SAFE(sh_array[j]); + } + for (int j = 0; j < sizeof(e_data.transp_prepass_sh_cache) / sizeof(void *); j++) { + struct GPUShader **sh_array = &e_data.transp_prepass_sh_cache[0][0][0][0]; + DRW_SHADER_FREE_SAFE(sh_array[j]); + } + for (int j = 0; j < sizeof(e_data.opaque_composite_sh) / sizeof(void *); j++) { + struct GPUShader **sh_array = &e_data.opaque_composite_sh[0]; + DRW_SHADER_FREE_SAFE(sh_array[j]); + } + for (int j = 0; j < sizeof(e_data.shadow_depth_pass_sh) / sizeof(void *); j++) { + struct GPUShader **sh_array = &e_data.shadow_depth_pass_sh[0]; + DRW_SHADER_FREE_SAFE(sh_array[j]); + } + for (int j = 0; j < sizeof(e_data.shadow_depth_fail_sh) / sizeof(void *); j++) { + struct GPUShader **sh_array = &e_data.shadow_depth_fail_sh[0][0]; + DRW_SHADER_FREE_SAFE(sh_array[j]); + } + for (int j = 0; j < sizeof(e_data.cavity_sh) / sizeof(void *); j++) { + struct GPUShader **sh_array = &e_data.cavity_sh[0][0]; + DRW_SHADER_FREE_SAFE(sh_array[j]); + } + for (int j = 0; j < sizeof(e_data.smaa_sh) / sizeof(void *); j++) { + struct GPUShader **sh_array = &e_data.smaa_sh[0]; + DRW_SHADER_FREE_SAFE(sh_array[j]); + } + for (int j = 0; j < sizeof(e_data.volume_sh) / sizeof(void *); j++) { + struct GPUShader **sh_array = &e_data.volume_sh[0][0][0]; + DRW_SHADER_FREE_SAFE(sh_array[j]); + } + + DRW_SHADER_FREE_SAFE(e_data.oit_resolve_sh); + DRW_SHADER_FREE_SAFE(e_data.outline_sh); + DRW_SHADER_FREE_SAFE(e_data.merge_infront_sh); + + DRW_SHADER_FREE_SAFE(e_data.dof_prepare_sh); + DRW_SHADER_FREE_SAFE(e_data.dof_downsample_sh); + DRW_SHADER_FREE_SAFE(e_data.dof_blur1_sh); + DRW_SHADER_FREE_SAFE(e_data.dof_blur2_sh); + DRW_SHADER_FREE_SAFE(e_data.dof_resolve_sh); + + DRW_SHADER_FREE_SAFE(e_data.aa_accum_sh); + + DRW_SHADER_LIB_FREE_SAFE(e_data.lib); +} diff --git a/source/blender/draw/engines/workbench/workbench_shadow.c b/source/blender/draw/engines/workbench/workbench_shadow.c new file mode 100644 index 00000000000..8da75942944 --- /dev/null +++ b/source/blender/draw/engines/workbench/workbench_shadow.c @@ -0,0 +1,367 @@ +/* + * 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. + * + * Copyright 2020, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + * + * Shadow: + * + * Use stencil shadow buffer to cast a sharp shadow over opaque surfaces. + * + * After the main prepass we render shadow volumes using custom depth & stencil states to + * set the stencil of shadowed area to anything but 0. + * + * Then the shading pass will shade the areas with stencil not equal 0 differently. + */ + +#include "DRW_render.h" + +#include "BKE_object.h" + +#include "BLI_math.h" + +#include "workbench_engine.h" +#include "workbench_private.h" + +static void compute_parallel_lines_nor_and_dist(const float v1[2], + const float v2[2], + const float v3[2], + float r_line[4]) +{ + sub_v2_v2v2(r_line, v2, v1); + /* Find orthogonal vector. */ + SWAP(float, r_line[0], r_line[1]); + r_line[0] = -r_line[0]; + /* Edge distances. */ + r_line[2] = dot_v2v2(r_line, v1); + r_line[3] = dot_v2v2(r_line, v3); + /* Make sure r_line[2] is the minimum. */ + if (r_line[2] > r_line[3]) { + SWAP(float, r_line[2], r_line[3]); + } +} + +static void workbench_shadow_update(WORKBENCH_PrivateData *wpd) +{ + wpd->shadow_changed = !compare_v3v3( + wpd->shadow_cached_direction, wpd->shadow_direction_ws, 1e-5f); + + if (wpd->shadow_changed) { + float up[3] = {0.0f, 0.0f, 1.0f}; + unit_m4(wpd->shadow_mat); + + /* TODO fix singularity. */ + copy_v3_v3(wpd->shadow_mat[2], wpd->shadow_direction_ws); + cross_v3_v3v3(wpd->shadow_mat[0], wpd->shadow_mat[2], up); + normalize_v3(wpd->shadow_mat[0]); + cross_v3_v3v3(wpd->shadow_mat[1], wpd->shadow_mat[2], wpd->shadow_mat[0]); + + invert_m4_m4(wpd->shadow_inv, wpd->shadow_mat); + + copy_v3_v3(wpd->shadow_cached_direction, wpd->shadow_direction_ws); + } + + float planes[6][4]; + DRW_culling_frustum_planes_get(NULL, planes); + /* we only need the far plane. */ + copy_v4_v4(wpd->shadow_far_plane, planes[2]); + + BoundBox frustum_corners; + DRW_culling_frustum_corners_get(NULL, &frustum_corners); + + float shadow_near_corners[4][3]; + mul_v3_mat3_m4v3(shadow_near_corners[0], wpd->shadow_inv, frustum_corners.vec[0]); + mul_v3_mat3_m4v3(shadow_near_corners[1], wpd->shadow_inv, frustum_corners.vec[3]); + mul_v3_mat3_m4v3(shadow_near_corners[2], wpd->shadow_inv, frustum_corners.vec[7]); + mul_v3_mat3_m4v3(shadow_near_corners[3], wpd->shadow_inv, frustum_corners.vec[4]); + + INIT_MINMAX(wpd->shadow_near_min, wpd->shadow_near_max); + for (int i = 0; i < 4; i++) { + minmax_v3v3_v3(wpd->shadow_near_min, wpd->shadow_near_max, shadow_near_corners[i]); + } + + compute_parallel_lines_nor_and_dist(shadow_near_corners[0], + shadow_near_corners[1], + shadow_near_corners[2], + wpd->shadow_near_sides[0]); + compute_parallel_lines_nor_and_dist(shadow_near_corners[1], + shadow_near_corners[2], + shadow_near_corners[0], + wpd->shadow_near_sides[1]); +} + +void workbench_shadow_data_update(WORKBENCH_PrivateData *wpd, WORKBENCH_UBO_World *wd) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + const Scene *scene = draw_ctx->scene; + + float view_matrix[4][4]; + DRW_view_viewmat_get(NULL, view_matrix, false); + + /* Turn the light in a way where it's more user friendly to control. */ + copy_v3_v3(wpd->shadow_direction_ws, scene->display.light_direction); + SWAP(float, wpd->shadow_direction_ws[2], wpd->shadow_direction_ws[1]); + wpd->shadow_direction_ws[2] = -wpd->shadow_direction_ws[2]; + wpd->shadow_direction_ws[0] = -wpd->shadow_direction_ws[0]; + + /* Shadow direction. */ + mul_v3_mat3_m4v3(wd->shadow_direction_vs, view_matrix, wpd->shadow_direction_ws); + + /* Clamp to avoid overshadowing and shading errors. */ + float focus = clamp_f(scene->display.shadow_focus, 0.0001f, 0.99999f); + wd->shadow_shift = scene->display.shadow_shift; + wd->shadow_focus = 1.0f - focus * (1.0f - wd->shadow_shift); + + if (SHADOW_ENABLED(wpd)) { + wd->shadow_mul = wpd->shading.shadow_intensity; + wd->shadow_add = 1.0f - wd->shadow_mul; + } + else { + wd->shadow_mul = 0.0f; + wd->shadow_add = 1.0f; + } +} + +void workbench_shadow_cache_init(WORKBENCH_Data *data) +{ + WORKBENCH_PassList *psl = data->psl; + WORKBENCH_PrivateData *wpd = data->stl->wpd; + struct GPUShader *sh; + DRWShadingGroup *grp; + + if (SHADOW_ENABLED(wpd)) { + workbench_shadow_update(wpd); + +#if DEBUG_SHADOW_VOLUME + DRWState depth_pass_state = DRW_STATE_DEPTH_LESS; + DRWState depth_fail_state = DRW_STATE_DEPTH_GREATER_EQUAL; + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL; +#else + DRWState depth_pass_state = DRW_STATE_WRITE_STENCIL_SHADOW_PASS; + DRWState depth_fail_state = DRW_STATE_WRITE_STENCIL_SHADOW_FAIL; + DRWState state = DRW_STATE_DEPTH_LESS | DRW_STATE_STENCIL_ALWAYS; +#endif + + /* TODO(fclem) Merge into one pass with subpasses. */ + DRW_PASS_CREATE(psl->shadow_ps[0], state | depth_pass_state); + DRW_PASS_CREATE(psl->shadow_ps[1], state | depth_fail_state); + + /* Stencil Shadow passes. */ + for (int manifold = 0; manifold < 2; manifold++) { + sh = workbench_shader_shadow_pass_get(manifold); + wpd->shadow_pass_grp[manifold] = grp = DRW_shgroup_create(sh, psl->shadow_ps[0]); + DRW_shgroup_stencil_mask(grp, 0xFF); /* Needed once to set the stencil state for the pass. */ + + sh = workbench_shader_shadow_fail_get(manifold, false); + wpd->shadow_fail_grp[manifold] = grp = DRW_shgroup_create(sh, psl->shadow_ps[1]); + DRW_shgroup_stencil_mask(grp, 0xFF); /* Needed once to set the stencil state for the pass. */ + + sh = workbench_shader_shadow_fail_get(manifold, true); + wpd->shadow_fail_caps_grp[manifold] = grp = DRW_shgroup_create(sh, psl->shadow_ps[1]); + } + } + else { + psl->shadow_ps[0] = NULL; + psl->shadow_ps[1] = NULL; + } +} + +static BoundBox *workbench_shadow_object_shadow_bbox_get(WORKBENCH_PrivateData *wpd, + Object *ob, + WORKBENCH_ObjectData *oed) +{ + if (oed->shadow_bbox_dirty || wpd->shadow_changed) { + float tmp_mat[4][4]; + mul_m4_m4m4(tmp_mat, wpd->shadow_inv, ob->obmat); + + /* Get AABB in shadow space. */ + INIT_MINMAX(oed->shadow_min, oed->shadow_max); + + /* From object space to shadow space */ + BoundBox *bbox = BKE_object_boundbox_get(ob); + for (int i = 0; i < 8; i++) { + float corner[3]; + mul_v3_m4v3(corner, tmp_mat, bbox->vec[i]); + minmax_v3v3_v3(oed->shadow_min, oed->shadow_max, corner); + } + oed->shadow_depth = oed->shadow_max[2] - oed->shadow_min[2]; + /* Extend towards infinity. */ + oed->shadow_max[2] += 1e4f; + + /* Get extended AABB in world space. */ + BKE_boundbox_init_from_minmax(&oed->shadow_bbox, oed->shadow_min, oed->shadow_max); + for (int i = 0; i < 8; i++) { + mul_m4_v3(wpd->shadow_mat, oed->shadow_bbox.vec[i]); + } + oed->shadow_bbox_dirty = false; + } + + return &oed->shadow_bbox; +} + +static bool workbench_shadow_object_cast_visible_shadow(WORKBENCH_PrivateData *wpd, + Object *ob, + WORKBENCH_ObjectData *oed) +{ + BoundBox *shadow_bbox = workbench_shadow_object_shadow_bbox_get(wpd, ob, oed); + const DRWView *default_view = DRW_view_default_get(); + return DRW_culling_box_test(default_view, shadow_bbox); +} + +static float workbench_shadow_object_shadow_distance(WORKBENCH_PrivateData *wpd, + Object *ob, + WORKBENCH_ObjectData *oed) +{ + BoundBox *shadow_bbox = workbench_shadow_object_shadow_bbox_get(wpd, ob, oed); + + int corners[4] = {0, 3, 4, 7}; + float dist = 1e4f, dist_isect; + for (int i = 0; i < 4; i++) { + if (isect_ray_plane_v3(shadow_bbox->vec[corners[i]], + wpd->shadow_cached_direction, + wpd->shadow_far_plane, + &dist_isect, + true)) { + if (dist_isect < dist) { + dist = dist_isect; + } + } + else { + /* All rays are parallels. If one fails, the other will too. */ + break; + } + } + return max_ii(dist - oed->shadow_depth, 0); +} + +static bool workbench_shadow_camera_in_object_shadow(WORKBENCH_PrivateData *wpd, + Object *ob, + WORKBENCH_ObjectData *oed) +{ + /* Just to be sure the min, max are updated. */ + workbench_shadow_object_shadow_bbox_get(wpd, ob, oed); + /* Test if near plane is in front of the shadow. */ + if (oed->shadow_min[2] > wpd->shadow_near_max[2]) { + return false; + } + + /* Separation Axis Theorem test */ + + /* Test bbox sides first (faster) */ + if ((oed->shadow_min[0] > wpd->shadow_near_max[0]) || + (oed->shadow_max[0] < wpd->shadow_near_min[0]) || + (oed->shadow_min[1] > wpd->shadow_near_max[1]) || + (oed->shadow_max[1] < wpd->shadow_near_min[1])) { + return false; + } + /* Test projected near rectangle sides */ + const float pts[4][2] = { + {oed->shadow_min[0], oed->shadow_min[1]}, + {oed->shadow_min[0], oed->shadow_max[1]}, + {oed->shadow_max[0], oed->shadow_min[1]}, + {oed->shadow_max[0], oed->shadow_max[1]}, + }; + + for (int i = 0; i < 2; i++) { + float min_dst = FLT_MAX, max_dst = -FLT_MAX; + for (int j = 0; j < 4; j++) { + float dst = dot_v2v2(wpd->shadow_near_sides[i], pts[j]); + /* Do min max */ + if (min_dst > dst) { + min_dst = dst; + } + if (max_dst < dst) { + max_dst = dst; + } + } + + if ((wpd->shadow_near_sides[i][2] > max_dst) || (wpd->shadow_near_sides[i][3] < min_dst)) { + return false; + } + } + /* No separation axis found. Both shape intersect. */ + return true; +} + +static void workbench_init_object_data(DrawData *dd) +{ + WORKBENCH_ObjectData *data = (WORKBENCH_ObjectData *)dd; + data->shadow_bbox_dirty = true; +} + +void workbench_shadow_cache_populate(WORKBENCH_Data *data, Object *ob, const bool has_transp_mat) +{ + WORKBENCH_PrivateData *wpd = data->stl->wpd; + + bool is_manifold; + struct GPUBatch *geom_shadow = DRW_cache_object_edge_detection_get(ob, &is_manifold); + if (geom_shadow == NULL) { + return; + } + + WORKBENCH_ObjectData *engine_object_data = (WORKBENCH_ObjectData *)DRW_drawdata_ensure( + &ob->id, + &draw_engine_workbench, + sizeof(WORKBENCH_ObjectData), + &workbench_init_object_data, + NULL); + + if (workbench_shadow_object_cast_visible_shadow(wpd, ob, engine_object_data)) { + mul_v3_mat3_m4v3(engine_object_data->shadow_dir, ob->imat, wpd->shadow_direction_ws); + + DRWShadingGroup *grp; + bool use_shadow_pass_technique = !workbench_shadow_camera_in_object_shadow( + wpd, ob, engine_object_data); + + /* Shadow pass technique needs object to be have all its surface opaque. */ + if (has_transp_mat) { + use_shadow_pass_technique = false; + } + + if (use_shadow_pass_technique) { + grp = DRW_shgroup_create_sub(wpd->shadow_pass_grp[is_manifold]); + DRW_shgroup_uniform_vec3(grp, "lightDirection", engine_object_data->shadow_dir, 1); + DRW_shgroup_uniform_float_copy(grp, "lightDistance", 1e5f); + DRW_shgroup_call_no_cull(grp, geom_shadow, ob); +#if DEBUG_SHADOW_VOLUME + DRW_debug_bbox(&engine_object_data->shadow_bbox, (float[4]){1.0f, 0.0f, 0.0f, 1.0f}); +#endif + } + else { + float extrude_distance = workbench_shadow_object_shadow_distance( + wpd, ob, engine_object_data); + + /* TODO(fclem): only use caps if they are in the view frustum. */ + const bool need_caps = true; + if (need_caps) { + grp = DRW_shgroup_create_sub(wpd->shadow_fail_caps_grp[is_manifold]); + DRW_shgroup_uniform_vec3(grp, "lightDirection", engine_object_data->shadow_dir, 1); + DRW_shgroup_uniform_float_copy(grp, "lightDistance", extrude_distance); + DRW_shgroup_call_no_cull(grp, DRW_cache_object_surface_get(ob), ob); + } + + grp = DRW_shgroup_create_sub(wpd->shadow_fail_grp[is_manifold]); + DRW_shgroup_uniform_vec3(grp, "lightDirection", engine_object_data->shadow_dir, 1); + DRW_shgroup_uniform_float_copy(grp, "lightDistance", extrude_distance); + DRW_shgroup_call_no_cull(grp, geom_shadow, ob); +#if DEBUG_SHADOW_VOLUME + DRW_debug_bbox(&engine_object_data->shadow_bbox, (float[4]){0.0f, 1.0f, 0.0f, 1.0f}); +#endif + } + } +} \ No newline at end of file diff --git a/source/blender/draw/engines/workbench/workbench_studiolight.c b/source/blender/draw/engines/workbench/workbench_studiolight.c deleted file mode 100644 index 1fb0b394cb1..00000000000 --- a/source/blender/draw/engines/workbench/workbench_studiolight.c +++ /dev/null @@ -1,257 +0,0 @@ -/* - * 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. - * - * Copyright 2016, Blender Foundation. - */ - -/** \file - * \ingroup draw_engine - */ -#include "BKE_studiolight.h" - -#include "workbench_private.h" - -#include "BKE_object.h" - -#include "BLI_math.h" - -void studiolight_update_world(WORKBENCH_PrivateData *wpd, - StudioLight *studiolight, - WORKBENCH_UBO_World *wd) -{ - float view_matrix[4][4], rot_matrix[4][4]; - DRW_view_viewmat_get(NULL, view_matrix, false); - - if (USE_WORLD_ORIENTATION(wpd)) { - axis_angle_to_mat4_single(rot_matrix, 'Z', -wpd->shading.studiolight_rot_z); - mul_m4_m4m4(rot_matrix, view_matrix, rot_matrix); - swap_v3_v3(rot_matrix[2], rot_matrix[1]); - negate_v3(rot_matrix[2]); - } - else { - unit_m4(rot_matrix); - } - - if (U.edit_studio_light) { - studiolight = BKE_studiolight_studio_edit_get(); - } - - /* Studio Lights. */ - for (int i = 0; i < 4; i++) { - WORKBENCH_UBO_Light *light = &wd->lights[i]; - - SolidLight *sl = &studiolight->light[i]; - if (sl->flag) { - copy_v3_v3(light->light_direction, sl->vec); - mul_mat3_m4_v3(rot_matrix, light->light_direction); - /* We should predivide the power by PI but that makes the lights really dim. */ - copy_v3_v3(light->specular_color, sl->spec); - copy_v3_v3(light->diffuse_color, sl->col); - light->wrapped = sl->smooth; - } - else { - copy_v3_fl3(light->light_direction, 1.0f, 0.0f, 0.0f); - copy_v3_fl(light->specular_color, 0.0f); - copy_v3_fl(light->diffuse_color, 0.0f); - } - } - - copy_v3_v3(wd->ambient_color, studiolight->light_ambient); -} - -static void compute_parallel_lines_nor_and_dist(const float v1[2], - const float v2[2], - const float v3[2], - float r_line[4]) -{ - sub_v2_v2v2(r_line, v2, v1); - /* Find orthogonal vector. */ - SWAP(float, r_line[0], r_line[1]); - r_line[0] = -r_line[0]; - /* Edge distances. */ - r_line[2] = dot_v2v2(r_line, v1); - r_line[3] = dot_v2v2(r_line, v3); - /* Make sure r_line[2] is the minimum. */ - if (r_line[2] > r_line[3]) { - SWAP(float, r_line[2], r_line[3]); - } -} - -void studiolight_update_light(WORKBENCH_PrivateData *wpd, const float light_direction[3]) -{ - wpd->shadow_changed = !compare_v3v3(wpd->cached_shadow_direction, light_direction, 1e-5f); - - if (wpd->shadow_changed) { - float up[3] = {0.0f, 0.0f, 1.0f}; - unit_m4(wpd->shadow_mat); - - /* TODO fix singularity. */ - copy_v3_v3(wpd->shadow_mat[2], light_direction); - cross_v3_v3v3(wpd->shadow_mat[0], wpd->shadow_mat[2], up); - normalize_v3(wpd->shadow_mat[0]); - cross_v3_v3v3(wpd->shadow_mat[1], wpd->shadow_mat[2], wpd->shadow_mat[0]); - - invert_m4_m4(wpd->shadow_inv, wpd->shadow_mat); - - copy_v3_v3(wpd->cached_shadow_direction, light_direction); - } - - float planes[6][4]; - DRW_culling_frustum_planes_get(NULL, planes); - /* we only need the far plane. */ - copy_v4_v4(wpd->shadow_far_plane, planes[2]); - - BoundBox frustum_corners; - DRW_culling_frustum_corners_get(NULL, &frustum_corners); - - mul_v3_mat3_m4v3(wpd->shadow_near_corners[0], wpd->shadow_inv, frustum_corners.vec[0]); - mul_v3_mat3_m4v3(wpd->shadow_near_corners[1], wpd->shadow_inv, frustum_corners.vec[3]); - mul_v3_mat3_m4v3(wpd->shadow_near_corners[2], wpd->shadow_inv, frustum_corners.vec[7]); - mul_v3_mat3_m4v3(wpd->shadow_near_corners[3], wpd->shadow_inv, frustum_corners.vec[4]); - - INIT_MINMAX(wpd->shadow_near_min, wpd->shadow_near_max); - for (int i = 0; i < 4; i++) { - minmax_v3v3_v3(wpd->shadow_near_min, wpd->shadow_near_max, wpd->shadow_near_corners[i]); - } - - compute_parallel_lines_nor_and_dist(wpd->shadow_near_corners[0], - wpd->shadow_near_corners[1], - wpd->shadow_near_corners[2], - wpd->shadow_near_sides[0]); - compute_parallel_lines_nor_and_dist(wpd->shadow_near_corners[1], - wpd->shadow_near_corners[2], - wpd->shadow_near_corners[0], - wpd->shadow_near_sides[1]); -} - -static BoundBox *studiolight_object_shadow_bbox_get(WORKBENCH_PrivateData *wpd, - Object *ob, - WORKBENCH_ObjectData *oed) -{ - if ((oed->shadow_bbox_dirty) || (wpd->shadow_changed)) { - float tmp_mat[4][4]; - mul_m4_m4m4(tmp_mat, wpd->shadow_inv, ob->obmat); - - /* Get AABB in shadow space. */ - INIT_MINMAX(oed->shadow_min, oed->shadow_max); - - /* From object space to shadow space */ - BoundBox *bbox = BKE_object_boundbox_get(ob); - for (int i = 0; i < 8; i++) { - float corner[3]; - mul_v3_m4v3(corner, tmp_mat, bbox->vec[i]); - minmax_v3v3_v3(oed->shadow_min, oed->shadow_max, corner); - } - oed->shadow_depth = oed->shadow_max[2] - oed->shadow_min[2]; - /* Extend towards infinity. */ - oed->shadow_max[2] += 1e4f; - - /* Get extended AABB in world space. */ - BKE_boundbox_init_from_minmax(&oed->shadow_bbox, oed->shadow_min, oed->shadow_max); - for (int i = 0; i < 8; i++) { - mul_m4_v3(wpd->shadow_mat, oed->shadow_bbox.vec[i]); - } - oed->shadow_bbox_dirty = false; - } - - return &oed->shadow_bbox; -} - -bool studiolight_object_cast_visible_shadow(WORKBENCH_PrivateData *wpd, - Object *ob, - WORKBENCH_ObjectData *oed) -{ - BoundBox *shadow_bbox = studiolight_object_shadow_bbox_get(wpd, ob, oed); - const DRWView *default_view = DRW_view_default_get(); - return DRW_culling_box_test(default_view, shadow_bbox); -} - -float studiolight_object_shadow_distance(WORKBENCH_PrivateData *wpd, - Object *ob, - WORKBENCH_ObjectData *oed) -{ - BoundBox *shadow_bbox = studiolight_object_shadow_bbox_get(wpd, ob, oed); - - int corners[4] = {0, 3, 4, 7}; - float dist = 1e4f, dist_isect; - for (int i = 0; i < 4; i++) { - if (isect_ray_plane_v3(shadow_bbox->vec[corners[i]], - wpd->cached_shadow_direction, - wpd->shadow_far_plane, - &dist_isect, - true)) { - if (dist_isect < dist) { - dist = dist_isect; - } - } - else { - /* All rays are parallels. If one fails, the other will too. */ - break; - } - } - return max_ii(dist - oed->shadow_depth, 0); -} - -bool studiolight_camera_in_object_shadow(WORKBENCH_PrivateData *wpd, - Object *ob, - WORKBENCH_ObjectData *oed) -{ - /* Just to be sure the min, max are updated. */ - studiolight_object_shadow_bbox_get(wpd, ob, oed); - - /* Test if near plane is in front of the shadow. */ - if (oed->shadow_min[2] > wpd->shadow_near_max[2]) { - return false; - } - - /* Separation Axis Theorem test */ - - /* Test bbox sides first (faster) */ - if ((oed->shadow_min[0] > wpd->shadow_near_max[0]) || - (oed->shadow_max[0] < wpd->shadow_near_min[0]) || - (oed->shadow_min[1] > wpd->shadow_near_max[1]) || - (oed->shadow_max[1] < wpd->shadow_near_min[1])) { - return false; - } - - /* Test projected near rectangle sides */ - const float pts[4][2] = { - {oed->shadow_min[0], oed->shadow_min[1]}, - {oed->shadow_min[0], oed->shadow_max[1]}, - {oed->shadow_max[0], oed->shadow_min[1]}, - {oed->shadow_max[0], oed->shadow_max[1]}, - }; - - for (int i = 0; i < 2; i++) { - float min_dst = FLT_MAX, max_dst = -FLT_MAX; - for (int j = 0; j < 4; j++) { - float dst = dot_v2v2(wpd->shadow_near_sides[i], pts[j]); - /* Do min max */ - if (min_dst > dst) { - min_dst = dst; - } - if (max_dst < dst) { - max_dst = dst; - } - } - - if ((wpd->shadow_near_sides[i][2] > max_dst) || (wpd->shadow_near_sides[i][3] < min_dst)) { - return false; - } - } - - /* No separation axis found. Both shape intersect. */ - return true; -} diff --git a/source/blender/draw/engines/workbench/workbench_transparent.c b/source/blender/draw/engines/workbench/workbench_transparent.c new file mode 100644 index 00000000000..1e1b751f80d --- /dev/null +++ b/source/blender/draw/engines/workbench/workbench_transparent.c @@ -0,0 +1,180 @@ +/* + * 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. + * + * Copyright 2020, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + * + * Transparent Pieline: + * + * Use Weight Blended Order Independant Transparency to render transparent surfaces. + * + * The rendering is broken down in two passes: + * - the accumulation pass where we render all the surfaces and accumulate all the weights. + * - the resolve pass where we divide the accumulated infos by the weights. + * + * An additional rerender of the transparent surfaces is sometime done in order to have their + * correct depth and object ids correctly written. + */ + +#include "DRW_render.h" + +#include "ED_view3d.h" + +#include "GPU_extensions.h" + +#include "workbench_engine.h" +#include "workbench_private.h" + +void workbench_transparent_engine_init(WORKBENCH_Data *data) +{ + WORKBENCH_FramebufferList *fbl = data->fbl; + WORKBENCH_PrivateData *wpd = data->stl->wpd; + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + DrawEngineType *owner = (DrawEngineType *)&workbench_transparent_engine_init; + + /* Reuse same format as opaque pipeline to reuse the textures. */ + /* Note: Floating point texture is required for the reveal_tex as it is used for + * the alpha accumulation component (see accumulation shader for more explanation). */ + const eGPUTextureFormat accum_tex_format = GPU_RGBA16F; + const eGPUTextureFormat reveal_tex_format = NORMAL_ENCODING_ENABLED() ? GPU_RG16F : GPU_RGBA32F; + + wpd->accum_buffer_tx = DRW_texture_pool_query_fullscreen(accum_tex_format, owner); + wpd->reveal_buffer_tx = DRW_texture_pool_query_fullscreen(reveal_tex_format, owner); + + GPU_framebuffer_ensure_config(&fbl->transp_accum_fb, + { + GPU_ATTACHMENT_TEXTURE(dtxl->depth), + GPU_ATTACHMENT_TEXTURE(wpd->accum_buffer_tx), + GPU_ATTACHMENT_TEXTURE(wpd->reveal_buffer_tx), + }); +} + +static void workbench_transparent_lighting_uniforms(WORKBENCH_PrivateData *wpd, + DRWShadingGroup *grp) +{ + DRW_shgroup_uniform_block_persistent(grp, "world_block", wpd->world_ubo); + DRW_shgroup_uniform_bool_copy(grp, "forceShadowing", false); + + if (STUDIOLIGHT_TYPE_MATCAP_ENABLED(wpd)) { + BKE_studiolight_ensure_flag(wpd->studio_light, + STUDIOLIGHT_MATCAP_DIFFUSE_GPUTEXTURE | + STUDIOLIGHT_MATCAP_SPECULAR_GPUTEXTURE); + struct GPUTexture *diff_tx = wpd->studio_light->matcap_diffuse.gputexture; + struct GPUTexture *spec_tx = wpd->studio_light->matcap_specular.gputexture; + const bool use_spec = workbench_is_specular_highlight_enabled(wpd); + spec_tx = (use_spec && spec_tx) ? spec_tx : diff_tx; + DRW_shgroup_uniform_texture_persistent(grp, "matcapDiffuseImage", diff_tx); + DRW_shgroup_uniform_texture_persistent(grp, "matcapSpecularImage", spec_tx); + } +} + +void workbench_transparent_cache_init(WORKBENCH_Data *data) +{ + WORKBENCH_PassList *psl = data->psl; + WORKBENCH_PrivateData *wpd = data->stl->wpd; + struct GPUShader *sh; + DRWShadingGroup *grp; + + { + int transp = 1; + for (int infront = 0; infront < 2; infront++) { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_OIT; + + DRWPass *pass; + if (infront) { + DRW_PASS_CREATE(psl->transp_accum_infront_ps, state | wpd->cull_state | wpd->clip_state); + pass = psl->transp_accum_infront_ps; + } + else { + DRW_PASS_CREATE(psl->transp_accum_ps, state | wpd->cull_state | wpd->clip_state); + pass = psl->transp_accum_ps; + } + + for (int hair = 0; hair < 2; hair++) { + wpd->prepass[transp][infront][hair].material_hash = BLI_ghash_ptr_new(__func__); + + sh = workbench_shader_transparent_get(wpd, hair); + + wpd->prepass[transp][infront][hair].common_shgrp = grp = DRW_shgroup_create(sh, pass); + DRW_shgroup_uniform_block(grp, "material_block", wpd->material_ubo_curr); + DRW_shgroup_uniform_int_copy(grp, "materialIndex", -1); + workbench_transparent_lighting_uniforms(wpd, grp); + + wpd->prepass[transp][infront][hair].vcol_shgrp = grp = DRW_shgroup_create(sh, pass); + DRW_shgroup_uniform_block_persistent(grp, "material_block", wpd->material_ubo_curr); + DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. (uses vcol) */ + + sh = workbench_shader_transparent_image_get(wpd, hair, false); + + wpd->prepass[transp][infront][hair].image_shgrp = grp = DRW_shgroup_create(sh, pass); + DRW_shgroup_uniform_block_persistent(grp, "material_block", wpd->material_ubo_curr); + DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */ + workbench_transparent_lighting_uniforms(wpd, grp); + + sh = workbench_shader_transparent_image_get(wpd, hair, true); + + wpd->prepass[transp][infront][hair].image_tiled_shgrp = grp = DRW_shgroup_create(sh, pass); + DRW_shgroup_uniform_block_persistent(grp, "material_block", wpd->material_ubo_curr); + DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */ + workbench_transparent_lighting_uniforms(wpd, grp); + } + } + } + { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA; + + DRW_PASS_CREATE(psl->transp_resolve_ps, state); + + sh = workbench_shader_transparent_resolve_get(wpd); + + grp = DRW_shgroup_create(sh, psl->transp_resolve_ps); + DRW_shgroup_uniform_texture(grp, "transparentAccum", wpd->accum_buffer_tx); + DRW_shgroup_uniform_texture(grp, "transparentRevealage", wpd->reveal_buffer_tx); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } +} + +/* Redraw the transparent passes but with depth test + * to output correct outline IDs and depth. */ +void workbench_transparent_draw_depth_pass(WORKBENCH_Data *data) +{ + WORKBENCH_PrivateData *wpd = data->stl->wpd; + WORKBENCH_FramebufferList *fbl = data->fbl; + WORKBENCH_PassList *psl = data->psl; + + const bool do_xray_depth_pass = XRAY_ALPHA(wpd) > 0.0f; + const bool do_transparent_depth_pass = psl->outline_ps || wpd->dof_enabled || do_xray_depth_pass; + + if (do_transparent_depth_pass) { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; + + if (!DRW_pass_is_empty(psl->transp_accum_ps)) { + GPU_framebuffer_bind(fbl->opaque_fb); + /* TODO(fclem) Disable writting to first two buffers. Unecessary waste of bandwidth. */ + DRW_pass_state_set(psl->transp_accum_ps, state | wpd->cull_state | wpd->clip_state); + DRW_draw_pass(psl->transp_accum_ps); + } + + if (!DRW_pass_is_empty(psl->transp_accum_infront_ps)) { + GPU_framebuffer_bind(fbl->opaque_infront_fb); + /* TODO(fclem) Disable writting to first two buffers. Unecessary waste of bandwidth. */ + DRW_pass_state_set(psl->transp_accum_infront_ps, state | wpd->cull_state | wpd->clip_state); + DRW_draw_pass(psl->transp_accum_infront_ps); + } + } +} \ No newline at end of file diff --git a/source/blender/draw/engines/workbench/workbench_volume.c b/source/blender/draw/engines/workbench/workbench_volume.c index 2c61e894e8c..a3072b834bd 100644 --- a/source/blender/draw/engines/workbench/workbench_volume.c +++ b/source/blender/draw/engines/workbench/workbench_volume.c @@ -35,87 +35,23 @@ #include "GPU_draw.h" -enum { - VOLUME_SH_SLICE = 0, - VOLUME_SH_COBA, - VOLUME_SH_CUBIC, -}; - -#define VOLUME_SH_MAX (1 << (VOLUME_SH_CUBIC + 1)) - -static struct { - struct GPUShader *volume_sh[VOLUME_SH_MAX]; - struct GPUShader *volume_coba_sh; - struct GPUTexture *dummy_tex; - struct GPUTexture *dummy_coba_tex; -} e_data = {{NULL}}; - -extern char datatoc_workbench_volume_vert_glsl[]; -extern char datatoc_workbench_volume_frag_glsl[]; -extern char datatoc_common_view_lib_glsl[]; -extern char datatoc_gpu_shader_common_obinfos_lib_glsl[]; - -static GPUShader *volume_shader_get(bool slice, bool coba, bool cubic) +void workbench_volume_engine_init(WORKBENCH_Data *vedata) { - int id = 0; - id += (slice) ? (1 << VOLUME_SH_SLICE) : 0; - id += (coba) ? (1 << VOLUME_SH_COBA) : 0; - id += (cubic) ? (1 << VOLUME_SH_CUBIC) : 0; - - if (!e_data.volume_sh[id]) { - DynStr *ds = BLI_dynstr_new(); - - if (slice) { - BLI_dynstr_append(ds, "#define VOLUME_SLICE\n"); - } - if (coba) { - BLI_dynstr_append(ds, "#define USE_COBA\n"); - } - if (cubic) { - BLI_dynstr_append(ds, "#define USE_TRICUBIC\n"); - } - - char *defines = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - - char *libs = BLI_string_joinN(datatoc_common_view_lib_glsl, - datatoc_gpu_shader_common_obinfos_lib_glsl); - - e_data.volume_sh[id] = DRW_shader_create_with_lib(datatoc_workbench_volume_vert_glsl, - NULL, - datatoc_workbench_volume_frag_glsl, - libs, - defines); - - MEM_freeN(libs); - MEM_freeN(defines); - } - - return e_data.volume_sh[id]; -} + WORKBENCH_TextureList *txl = vedata->txl; -void workbench_volume_engine_init(void) -{ - if (!e_data.dummy_tex) { + if (txl->dummy_volume_tx == NULL) { float pixel[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - e_data.dummy_tex = GPU_texture_create_3d(1, 1, 1, GPU_RGBA8, pixel, NULL); - e_data.dummy_coba_tex = GPU_texture_create_1d(1, GPU_RGBA8, pixel, NULL); + txl->dummy_volume_tx = GPU_texture_create_3d(1, 1, 1, GPU_RGBA8, pixel, NULL); + txl->dummy_coba_tx = GPU_texture_create_1d(1, GPU_RGBA8, pixel, NULL); } } -void workbench_volume_engine_free(void) -{ - for (int i = 0; i < VOLUME_SH_MAX; i++) { - DRW_SHADER_FREE_SAFE(e_data.volume_sh[i]); - } - DRW_TEXTURE_FREE_SAFE(e_data.dummy_tex); - DRW_TEXTURE_FREE_SAFE(e_data.dummy_coba_tex); -} - void workbench_volume_cache_init(WORKBENCH_Data *vedata) { - vedata->psl->volume_pass = DRW_pass_create( + vedata->psl->volume_ps = DRW_pass_create( "Volumes", DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA_PREMUL | DRW_STATE_CULL_FRONT); + + vedata->stl->wpd->volumes_do = false; } void workbench_volume_cache_populate(WORKBENCH_Data *vedata, @@ -125,8 +61,8 @@ void workbench_volume_cache_populate(WORKBENCH_Data *vedata, { FluidModifierData *mmd = (FluidModifierData *)md; FluidDomainSettings *mds = mmd->domain; - WORKBENCH_PrivateData *wpd = vedata->stl->g_data; - WORKBENCH_EffectInfo *effect_info = vedata->stl->effects; + WORKBENCH_PrivateData *wpd = vedata->stl->wpd; + WORKBENCH_TextureList *txl = vedata->txl; DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); DRWShadingGroup *grp = NULL; @@ -154,7 +90,8 @@ void workbench_volume_cache_populate(WORKBENCH_Data *vedata, const bool use_slice = (mds->slice_method == FLUID_DOMAIN_SLICE_AXIS_ALIGNED && mds->axis_slice_method == AXIS_SLICE_SINGLE); const bool cubic_interp = (mds->interp_method == VOLUME_INTERP_CUBIC); - GPUShader *sh = volume_shader_get(use_slice, mds->use_coba, cubic_interp); + + GPUShader *sh = workbench_shader_volume_get(use_slice, mds->use_coba, cubic_interp); if (use_slice) { float invviewmat[4][4]; @@ -168,7 +105,7 @@ void workbench_volume_cache_populate(WORKBENCH_Data *vedata, /* 0.05f to achieve somewhat the same opacity as the full view. */ float step_length = max_ff(1e-16f, dim[axis] * 0.05f); - grp = DRW_shgroup_create(sh, vedata->psl->volume_pass); + grp = DRW_shgroup_create(sh, vedata->psl->volume_ps); DRW_shgroup_uniform_float_copy(grp, "slicePosition", mds->slice_depth); DRW_shgroup_uniform_int_copy(grp, "sliceAxis", axis); DRW_shgroup_uniform_float_copy(grp, "stepLength", step_length); @@ -176,7 +113,7 @@ void workbench_volume_cache_populate(WORKBENCH_Data *vedata, } else { double noise_ofs; - BLI_halton_1d(3, 0.0, effect_info->jitter_index, &noise_ofs); + BLI_halton_1d(3, 0.0, wpd->taa_sample, &noise_ofs); float dim[3], step_length, max_slice; float slice_ct[3] = {mds->res[0], mds->res[1], mds->res[2]}; mul_v3_fl(slice_ct, max_ff(0.001f, mds->slice_per_voxel)); @@ -186,8 +123,8 @@ void workbench_volume_cache_populate(WORKBENCH_Data *vedata, mul_v3_v3(dim, slice_ct); step_length = len_v3(dim); - grp = DRW_shgroup_create(sh, vedata->psl->volume_pass); - DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)wpd->viewvecs, 3); + grp = DRW_shgroup_create(sh, vedata->psl->volume_ps); + DRW_shgroup_uniform_block(grp, "world_block", wpd->world_ubo); DRW_shgroup_uniform_int_copy(grp, "samplesLen", max_slice); DRW_shgroup_uniform_float_copy(grp, "stepLength", step_length); DRW_shgroup_uniform_float_copy(grp, "noiseOfs", noise_ofs); @@ -206,9 +143,9 @@ void workbench_volume_cache_populate(WORKBENCH_Data *vedata, grp, "densityTexture", (mds->tex_color) ? mds->tex_color : mds->tex_density); DRW_shgroup_uniform_texture(grp, "shadowTexture", mds->tex_shadow); DRW_shgroup_uniform_texture( - grp, "flameTexture", (mds->tex_flame) ? mds->tex_flame : e_data.dummy_tex); + grp, "flameTexture", (mds->tex_flame) ? mds->tex_flame : txl->dummy_volume_tx); DRW_shgroup_uniform_texture( - grp, "flameColorTexture", (mds->tex_flame) ? mds->tex_flame_coba : e_data.dummy_coba_tex); + grp, "flameColorTexture", (mds->tex_flame) ? mds->tex_flame_coba : txl->dummy_coba_tx); DRW_shgroup_uniform_vec3( grp, "activeColor", (use_constant_color) ? mds->active_color : white, 1); } @@ -225,8 +162,22 @@ void workbench_volume_cache_populate(WORKBENCH_Data *vedata, BLI_addtail(&wpd->smoke_domains, BLI_genericNodeN(mmd)); } -void workbench_volume_smoke_textures_free(WORKBENCH_PrivateData *wpd) +void workbench_volume_draw_pass(WORKBENCH_Data *vedata) +{ + WORKBENCH_PassList *psl = vedata->psl; + WORKBENCH_PrivateData *wpd = vedata->stl->wpd; + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + + if (wpd->volumes_do) { + GPU_framebuffer_bind(dfbl->color_only_fb); + DRW_draw_pass(psl->volume_ps); + } +} + +void workbench_volume_draw_finish(WORKBENCH_Data *vedata) { + WORKBENCH_PrivateData *wpd = vedata->stl->wpd; + /* Free Smoke Textures after rendering */ /* XXX This is a waste of processing and GPU bandwidth if nothing * is updated. But the problem is since Textures are stored in the @@ -238,4 +189,4 @@ void workbench_volume_smoke_textures_free(WORKBENCH_PrivateData *wpd) GPU_free_smoke(mmd); } BLI_freelistN(&wpd->smoke_domains); -} +} \ No newline at end of file diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index c3e94fda4fc..5122b1ea1ac 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -81,6 +81,7 @@ typedef struct DRWPass DRWPass; typedef struct DRWShadingGroup DRWShadingGroup; typedef struct DRWUniform DRWUniform; typedef struct DRWView DRWView; +typedef struct DRWShaderLibrary DRWShaderLibrary; /* TODO Put it somewhere else? */ typedef struct BoundSphere { @@ -148,6 +149,8 @@ struct GPUTexture *DRW_texture_pool_query_2d(int w, int h, eGPUTextureFormat format, DrawEngineType *engine_type); +struct GPUTexture *DRW_texture_pool_query_fullscreen(eGPUTextureFormat format, + DrawEngineType *engine_type); struct GPUTexture *DRW_texture_create_1d(int w, eGPUTextureFormat format, @@ -246,6 +249,24 @@ void DRW_shader_free(struct GPUShader *shader); } \ } while (0) +DRWShaderLibrary *DRW_shader_library_create(void); + +/* Warning: Each library must be added after all its dependencies. */ +void DRW_shader_library_add_file(DRWShaderLibrary *lib, char *lib_code, const char *lib_name); +#define DRW_SHADER_LIB_ADD(lib, lib_name) \ + DRW_shader_library_add_file(lib, datatoc_##lib_name##_glsl, STRINGIFY(lib_name) ".glsl") + +char *DRW_shader_library_create_shader_string(DRWShaderLibrary *lib, char *shader_code); + +void DRW_shader_library_free(DRWShaderLibrary *lib); +#define DRW_SHADER_LIB_FREE_SAFE(lib) \ + do { \ + if (lib != NULL) { \ + DRW_shader_library_free(lib); \ + lib = NULL; \ + } \ + } while (0) + /* Batches */ typedef enum { @@ -403,6 +424,9 @@ void DRW_buffer_add_entry_array(DRWCallBuffer *buffer, const void *attr[], uint DRW_buffer_add_entry_array(buffer, array, (sizeof(array) / sizeof(*array))); \ } while (0) +/* Can only be called during iter phase. */ +uint32_t DRW_object_resource_id_get(Object *UNUSED(ob)); + void DRW_shgroup_state_enable(DRWShadingGroup *shgroup, DRWState state); void DRW_shgroup_state_disable(DRWShadingGroup *shgroup, DRWState state); @@ -414,7 +438,7 @@ void DRW_shgroup_state_disable(DRWShadingGroup *shgroup, DRWState state); void DRW_shgroup_stencil_set(DRWShadingGroup *shgroup, uint write_mask, uint reference, - uint comp_mask); + uint compare_mask); /* TODO remove this function. Obsolete version. mask is actually reference value. */ void DRW_shgroup_stencil_mask(DRWShadingGroup *shgroup, uint mask); diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h index 24d3b7fa7b6..5818d84a7af 100644 --- a/source/blender/draw/intern/draw_common.h +++ b/source/blender/draw/intern/draw_common.h @@ -177,6 +177,11 @@ struct DRWShadingGroup *DRW_shgroup_hair_create(struct Object *object, struct DRWPass *hair_pass, struct GPUShader *shader); +struct DRWShadingGroup *DRW_shgroup_hair_create_sub(struct Object *object, + struct ParticleSystem *psys, + struct ModifierData *md, + struct DRWShadingGroup *shgrp); + struct DRWShadingGroup *DRW_shgroup_material_hair_create(struct Object *object, struct ParticleSystem *psys, struct ModifierData *md, diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c index 08256b931ba..847f5e7a224 100644 --- a/source/blender/draw/intern/draw_hair.c +++ b/source/blender/draw/intern/draw_hair.c @@ -108,6 +108,7 @@ static DRWShadingGroup *drw_shgroup_create_hair_procedural_ex(Object *object, ParticleSystem *psys, ModifierData *md, DRWPass *hair_pass, + DRWShadingGroup *shgrp_parent, struct GPUMaterial *gpu_mat, GPUShader *gpu_shader) { @@ -127,7 +128,10 @@ static DRWShadingGroup *drw_shgroup_create_hair_procedural_ex(Object *object, object, psys, md, &hair_cache, subdiv, thickness_res); DRWShadingGroup *shgrp; - if (gpu_mat) { + if (shgrp_parent) { + shgrp = DRW_shgroup_create_sub(shgrp_parent); + } + else if (gpu_mat) { shgrp = DRW_shgroup_material_create(gpu_mat, hair_pass); } else if (gpu_shader) { @@ -151,6 +155,17 @@ static DRWShadingGroup *drw_shgroup_create_hair_procedural_ex(Object *object, } } + /* Fix issue with certain driver not drawing anything if there is no texture bound to + * "ac", "au", "u" or "c". */ + if (hair_cache->num_uv_layers == 0) { + DRW_shgroup_uniform_texture(shgrp, "u", hair_cache->final[subdiv].proc_tex); + DRW_shgroup_uniform_texture(shgrp, "au", hair_cache->final[subdiv].proc_tex); + } + if (hair_cache->num_col_layers == 0) { + DRW_shgroup_uniform_texture(shgrp, "c", hair_cache->final[subdiv].proc_tex); + DRW_shgroup_uniform_texture(shgrp, "ac", hair_cache->final[subdiv].proc_tex); + } + if ((dupli_parent != NULL) && (dupli_object != NULL)) { if (dupli_object->type & OB_DUPLICOLLECTION) { copy_m4_m4(dupli_mat, dupli_parent->obmat); @@ -220,7 +235,15 @@ static DRWShadingGroup *drw_shgroup_create_hair_procedural_ex(Object *object, DRWShadingGroup *DRW_shgroup_hair_create( Object *object, ParticleSystem *psys, ModifierData *md, DRWPass *hair_pass, GPUShader *shader) { - return drw_shgroup_create_hair_procedural_ex(object, psys, md, hair_pass, NULL, shader); + return drw_shgroup_create_hair_procedural_ex(object, psys, md, hair_pass, NULL, NULL, shader); +} + +DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object, + ParticleSystem *psys, + ModifierData *md, + DRWShadingGroup *shgrp) +{ + return drw_shgroup_create_hair_procedural_ex(object, psys, md, NULL, shgrp, NULL, NULL); } DRWShadingGroup *DRW_shgroup_material_hair_create(Object *object, @@ -229,7 +252,7 @@ DRWShadingGroup *DRW_shgroup_material_hair_create(Object *object, DRWPass *hair_pass, struct GPUMaterial *material) { - return drw_shgroup_create_hair_procedural_ex(object, psys, md, hair_pass, material, NULL); + return drw_shgroup_create_hair_procedural_ex(object, psys, md, hair_pass, NULL, material, NULL); } void DRW_hair_update(void) diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 85b071f5c89..9553117c179 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -1118,45 +1118,23 @@ static void use_drw_engine(DrawEngineType *engine) BLI_addtail(&DST.enabled_engines, ld); } -/** - * Use for external render engines. - */ -static void drw_engines_enable_external(void) -{ - use_drw_engine(DRW_engine_viewport_external_type.draw_engine); -} - -/* TODO revisit this when proper layering is implemented */ /* Gather all draw engines needed and store them in DST.enabled_engines * That also define the rendering order of engines */ -static void drw_engines_enable_from_engine(RenderEngineType *engine_type, - eDrawType drawtype, - bool use_xray) +static void drw_engines_enable_from_engine(RenderEngineType *engine_type, eDrawType drawtype) { switch (drawtype) { case OB_WIRE: - use_drw_engine(&draw_engine_workbench_transparent); - break; - case OB_SOLID: - if (use_xray) { - use_drw_engine(&draw_engine_workbench_transparent); - } - else { - use_drw_engine(&draw_engine_workbench_solid); - } + use_drw_engine(DRW_engine_viewport_workbench_type.draw_engine); break; - case OB_MATERIAL: case OB_RENDER: default: - /* TODO layers */ if (engine_type->draw_engine != NULL) { use_drw_engine(engine_type->draw_engine); } - - if ((engine_type->flag & RE_INTERNAL) == 0) { - drw_engines_enable_external(); + else if ((engine_type->flag & RE_INTERNAL) == 0) { + use_drw_engine(DRW_engine_viewport_external_type.draw_engine); } break; } @@ -1182,7 +1160,7 @@ static void drw_engines_enable(ViewLayer *UNUSED(view_layer), const eDrawType drawtype = v3d->shading.type; const bool use_xray = XRAY_ENABLED(v3d); - drw_engines_enable_from_engine(engine_type, drawtype, use_xray); + drw_engines_enable_from_engine(engine_type, drawtype); if (gpencil_engine_needed && ((drawtype >= OB_SOLID) || !use_xray)) { use_drw_engine(&draw_engine_gpencil_type); } @@ -1608,6 +1586,9 @@ void DRW_draw_render_loop_offscreen(struct Depsgraph *depsgraph, GPU_blend(true); } + GPU_matrix_identity_set(); + GPU_matrix_identity_projection_set(); + GPU_viewport_unbind_from_offscreen(render_viewport, ofs, do_color_management); if (draw_background) { @@ -2646,9 +2627,6 @@ void DRW_engines_register(void) RE_engines_register(&DRW_engine_viewport_eevee_type); RE_engines_register(&DRW_engine_viewport_workbench_type); - DRW_engine_register(&draw_engine_workbench_solid); - DRW_engine_register(&draw_engine_workbench_transparent); - DRW_engine_register(&draw_engine_gpencil_type); DRW_engine_register(&draw_engine_overlay_type); diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 83142da051a..be0dd7751ed 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -562,6 +562,16 @@ static DRWResourceHandle drw_resource_handle_new(float (*obmat)[4], Object *ob) return handle; } +uint32_t DRW_object_resource_id_get(Object *UNUSED(ob)) +{ + DRWResourceHandle handle = DST.ob_handle; + if (handle == 0) { + /* Handle not yet allocated. Return next handle. */ + handle = DST.resource_handle; + } + return handle; +} + static DRWResourceHandle drw_resource_handle(DRWShadingGroup *shgroup, float (*obmat)[4], Object *ob) @@ -693,14 +703,14 @@ static void drw_command_set_select_id(DRWShadingGroup *shgroup, GPUVertBuf *buf, static void drw_command_set_stencil_mask(DRWShadingGroup *shgroup, uint write_mask, uint reference, - uint comp_mask) + uint compare_mask) { BLI_assert(write_mask <= 0xFF); BLI_assert(reference <= 0xFF); - BLI_assert(comp_mask <= 0xFF); + BLI_assert(compare_mask <= 0xFF); DRWCommandSetStencil *cmd = drw_command_create(shgroup, DRW_CMD_STENCIL); cmd->write_mask = write_mask; - cmd->comp_mask = comp_mask; + cmd->comp_mask = compare_mask; cmd->ref = reference; } @@ -1341,9 +1351,9 @@ void DRW_shgroup_state_disable(DRWShadingGroup *shgroup, DRWState state) void DRW_shgroup_stencil_set(DRWShadingGroup *shgroup, uint write_mask, uint reference, - uint comp_mask) + uint compare_mask) { - drw_command_set_stencil_mask(shgroup, write_mask, reference, comp_mask); + drw_command_set_stencil_mask(shgroup, write_mask, reference, compare_mask); } /* TODO remove this function. */ diff --git a/source/blender/draw/intern/draw_manager_shader.c b/source/blender/draw/intern/draw_manager_shader.c index ed05fb85dc9..7b08f44921f 100644 --- a/source/blender/draw/intern/draw_manager_shader.c +++ b/source/blender/draw/intern/draw_manager_shader.c @@ -24,6 +24,7 @@ #include "DNA_world_types.h" #include "DNA_material_types.h" +#include "BLI_dynstr.h" #include "BLI_listbase.h" #include "BLI_string_utils.h" #include "BLI_threads.h" @@ -282,6 +283,8 @@ void DRW_deferred_shader_remove(GPUMaterial *mat) /* -------------------------------------------------------------------- */ +/** \{ */ + GPUShader *DRW_shader_create(const char *vert, const char *geom, const char *frag, @@ -468,3 +471,129 @@ void DRW_shader_free(GPUShader *shader) { GPU_shader_free(shader); } + +/** \} */ + +/* -------------------------------------------------------------------- */ + +/** \name Shader Library + * + * Simple include system for glsl files. + * + * Usage: Create a DRWShaderLibrary and add the library in the right order. + * You can have nested dependencies but each new library needs to have all its dependencies already + * added to the DRWShaderLibrary. + * Finally you can use DRW_shader_library_create_shader_string to get a shader string that also + * contains the needed libraries for this shader. + * \{ */ + +/* 32 because we use a 32bit bitmap. */ +#define MAX_LIB 32 +#define MAX_LIB_NAME 64 +#define MAX_LIB_DEPS 8 + +struct DRWShaderLibrary { + char *libs[MAX_LIB]; + char libs_name[MAX_LIB][MAX_LIB_NAME]; + uint32_t libs_deps[MAX_LIB]; +}; + +DRWShaderLibrary *DRW_shader_library_create(void) +{ + return MEM_callocN(sizeof(DRWShaderLibrary), "DRWShaderLibrary"); +} + +void DRW_shader_library_free(DRWShaderLibrary *lib) +{ + MEM_SAFE_FREE(lib); +} + +static int drw_shader_library_search(DRWShaderLibrary *lib, const char *name) +{ + for (int i = 0; i < MAX_LIB; i++) { + if (lib->libs[i]) { + if (!strncmp(lib->libs_name[i], name, strlen(lib->libs_name[i]))) { + return i; + } + } + else { + break; + } + } + return -1; +} + +/* Return bitmap of dependencies. */ +static uint32_t drw_shader_dependencies_get(DRWShaderLibrary *lib, char *lib_code) +{ + /* Search dependencies. */ + uint32_t deps = 0; + char *haystack = lib_code; + while ((haystack = strstr(haystack, "BLENDER_REQUIRE("))) { + haystack += 16; + int dep = drw_shader_library_search(lib, haystack); + if (dep == -1) { + printf( + "Error: Dependency not found.\n" + "This might be due to bad lib ordering.\n"); + BLI_assert(0); + } + else { + deps |= 1u << (uint32_t)dep; + } + } + return deps; +} + +void DRW_shader_library_add_file(DRWShaderLibrary *lib, char *lib_code, const char *lib_name) +{ + int index = -1; + for (int i = 0; i < MAX_LIB; i++) { + if (lib->libs[i] == NULL) { + index = i; + break; + } + } + + if (index > -1) { + lib->libs[index] = lib_code; + BLI_strncpy(lib->libs_name[index], lib_name, MAX_LIB_NAME); + } + else { + printf("Error: Too many libraries. Cannot add %s.\n", lib_name); + BLI_assert(0); + } + + lib->libs_deps[index] = drw_shader_dependencies_get(lib, lib_code); +} + +/* Return an allocN'ed string containing the shader code with its dependencies prepended. + * Caller must free the string with MEM_freeN after use. */ +char *DRW_shader_library_create_shader_string(DRWShaderLibrary *lib, char *shader_code) +{ + uint32_t deps = drw_shader_dependencies_get(lib, shader_code); + + DynStr *ds = BLI_dynstr_new(); + /* Add all dependencies recursively. */ + for (int i = MAX_LIB - 1; i > -1; i--) { + if (lib->libs[i] && (deps & (1u << (uint32_t)i))) { + deps |= lib->libs_deps[i]; + } + } + /* Concatenate all needed libs into one string. */ + for (int i = 0; i < MAX_LIB; i++) { + if (deps & 1u) { + BLI_dynstr_append(ds, lib->libs[i]); + } + deps = deps >> 1; + } + + BLI_dynstr_append(ds, shader_code); + + char *str = BLI_dynstr_get_cstring(ds); + BLI_dynstr_free(ds); + + return str; +} + +/** \} */ diff --git a/source/blender/draw/intern/draw_manager_texture.c b/source/blender/draw/intern/draw_manager_texture.c index 373810b2f7f..c75299158a3 100644 --- a/source/blender/draw/intern/draw_manager_texture.c +++ b/source/blender/draw/intern/draw_manager_texture.c @@ -134,6 +134,13 @@ GPUTexture *DRW_texture_pool_query_2d(int w, return tex; } +GPUTexture *DRW_texture_pool_query_fullscreen(eGPUTextureFormat format, + DrawEngineType *engine_type) +{ + const float *size = DRW_viewport_size_get(); + return DRW_texture_pool_query_2d((int)size[0], (int)size[1], format, engine_type); +} + void DRW_texture_ensure_fullscreen_2d(GPUTexture **tex, eGPUTextureFormat format, DRWTextureFlag flags) -- cgit v1.2.3