From 299a581b1b451d6f131e2fe145ffc530948894cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Thu, 3 Apr 2025 16:52:05 +0200 Subject: [PATCH] Grease Pencil: Accumulation Anti-aliasing This adds a new more accurate antialiasing to the Grease Pencil render engine. This is only available for render. This Accumulation AA doesn't replace the SMAA. SMAA is still used by the viewport and for removing aliasing from the depth buffer. However, using both at the same time can lead to overblurred result. Here are some measurements for how much the render time increases compared to the baseline with different (SSAA) sample counts (using an example production file, rendered at 1080p, results might vary depending on the scene complexity): * 8 samples: +0.14 s * 16 samples +0.36 s * 32 samples: +0.58 s Co-authored-by: Falk David Pull Request: https://projects.blender.org/blender/blender/pulls/136551 --- scripts/startup/bl_ui/properties_render.py | 46 +++++++- .../blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenkernel/intern/scene.cc | 3 - .../blenloader/intern/versioning_400.cc | 8 ++ source/blender/draw/CMakeLists.txt | 1 + .../engines/gpencil/gpencil_antialiasing.cc | 94 ++++++++++++++- .../draw/engines/gpencil/gpencil_defines.h | 3 + .../engines/gpencil/gpencil_engine_private.hh | 8 ++ .../draw/engines/gpencil/gpencil_render.cc | 110 +++++++++++------- .../draw/engines/gpencil/gpencil_shader.hh | 2 + .../engines/gpencil/shaders/CMakeLists.txt | 1 + ...pencil_antialiasing_accumulation_frag.glsl | 34 ++++++ .../gpencil/shaders/infos/gpencil_info.hh | 10 ++ source/blender/makesdna/DNA_scene_defaults.h | 9 ++ source/blender/makesdna/DNA_scene_types.h | 4 +- source/blender/makesdna/intern/dna_defaults.c | 1 + source/blender/makesrna/intern/rna_scene.cc | 26 ++++- 17 files changed, 312 insertions(+), 50 deletions(-) create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_accumulation_frag.glsl diff --git a/scripts/startup/bl_ui/properties_render.py b/scripts/startup/bl_ui/properties_render.py index a001f800fb7..9c31af7c8b0 100644 --- a/scripts/startup/bl_ui/properties_render.py +++ b/scripts/startup/bl_ui/properties_render.py @@ -845,6 +845,7 @@ class RENDER_PT_eevee_performance_viewport(RenderButtonsPanel, Panel): col.prop(rd, "preview_pixel_size", text="Pixel Size") +# TODO(falk): To rename for 5.0 class RENDER_PT_gpencil(RenderButtonsPanel, Panel): bl_label = "Grease Pencil" bl_options = {'DEFAULT_CLOSED'} @@ -855,6 +856,24 @@ class RENDER_PT_gpencil(RenderButtonsPanel, Panel): 'BLENDER_WORKBENCH', } + @classmethod + def poll(cls, context): + return (context.engine in cls.COMPAT_ENGINES) + + def draw(self, context): + pass + + +class RENDER_PT_grease_pencil_viewport(RenderButtonsPanel, Panel): + bl_label = "Viewport" + bl_options = {'DEFAULT_CLOSED'} + bl_parent_id = "RENDER_PT_gpencil" + COMPAT_ENGINES = { + 'BLENDER_RENDER', + 'BLENDER_EEVEE_NEXT', + 'BLENDER_WORKBENCH', + } + def draw(self, context): layout = self.layout layout.use_property_split = True @@ -864,7 +883,30 @@ class RENDER_PT_gpencil(RenderButtonsPanel, Panel): props = scene.grease_pencil_settings col = layout.column() - col.prop(props, "antialias_threshold") + col.prop(props, "antialias_threshold", text="SMAA Threshold") + + +class RENDER_PT_grease_pencil_render(RenderButtonsPanel, Panel): + bl_label = "Render" + bl_options = {'DEFAULT_CLOSED'} + bl_parent_id = "RENDER_PT_gpencil" + COMPAT_ENGINES = { + 'BLENDER_RENDER', + 'BLENDER_EEVEE_NEXT', + 'BLENDER_WORKBENCH', + } + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + scene = context.scene + props = scene.grease_pencil_settings + + col = layout.column() + col.prop(props, "antialias_threshold_render", text="SMAA Threshold") + col.prop(props, "aa_samples", text="SSAA Samples") class RENDER_PT_opengl_sampling(RenderButtonsPanel, Panel): @@ -1081,6 +1123,8 @@ classes = ( RENDER_PT_gpencil, + RENDER_PT_grease_pencil_viewport, + RENDER_PT_grease_pencil_render, RENDER_PT_opengl_sampling, RENDER_PT_opengl_lighting, RENDER_PT_opengl_color, diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index e20b51232dc..c67e4efa105 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -27,7 +27,7 @@ /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 15 +#define BLENDER_FILE_SUBVERSION 16 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index 4b71ba562f4..bffac40c1cf 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -195,9 +195,6 @@ static void scene_init_data(ID *id) scene->unit.temperature_unit = uchar( BKE_unit_base_of_type_get(USER_UNIT_METRIC, B_UNIT_TEMPERATURE)); - /* Anti-Aliasing threshold. */ - scene->grease_pencil_settings.smaa_threshold = 1.0f; - { ParticleEditSettings *pset; pset = &scene->toolsettings->particle; diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index 2b6fb102206..cd3c7b9e544 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -6634,6 +6634,14 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) FOREACH_NODETREE_END; } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 16)) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + scene->grease_pencil_settings.smaa_threshold_render = + scene->grease_pencil_settings.smaa_threshold; + scene->grease_pencil_settings.aa_samples = 1; + } + } + /* Always run this versioning; meshes are written with the legacy format which always needs to * be converted to the new format on file load. Can be moved to a subversion check in a larger * breaking release. */ diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 21e9923341d..3f071e78790 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -584,6 +584,7 @@ set(GLSL_SRC engines/gpencil/shaders/gpencil_frag.glsl engines/gpencil/shaders/gpencil_vert.glsl + engines/gpencil/shaders/gpencil_antialiasing_accumulation_frag.glsl engines/gpencil/shaders/gpencil_antialiasing_frag.glsl engines/gpencil/shaders/gpencil_antialiasing_vert.glsl engines/gpencil/shaders/gpencil_common_lib.glsl diff --git a/source/blender/draw/engines/gpencil/gpencil_antialiasing.cc b/source/blender/draw/engines/gpencil/gpencil_antialiasing.cc index 2f3c37d8e78..767be299079 100644 --- a/source/blender/draw/engines/gpencil/gpencil_antialiasing.cc +++ b/source/blender/draw/engines/gpencil/gpencil_antialiasing.cc @@ -6,6 +6,7 @@ * \ingroup draw */ +#include "BLI_rand.h" #include "DNA_scene_types.h" #include "DRW_render.hh" @@ -58,6 +59,10 @@ void Instance::antialiasing_init() this->smaa_weight_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(this->smaa_weight_tx)); } + SceneGpencil gpencil_settings = this->scene->grease_pencil_settings; + const float luma_weight = this->is_viewport ? gpencil_settings.smaa_threshold : + gpencil_settings.smaa_threshold_render; + { /* Stage 1: Edge detection. */ PassSimple &pass = this->smaa_edge_ps; @@ -67,7 +72,7 @@ void Instance::antialiasing_init() pass.bind_texture("colorTex", &this->color_tx); pass.bind_texture("revealTex", &this->reveal_tx); pass.push_constant("viewportMetrics", metrics); - pass.push_constant("lumaWeight", this->scene->grease_pencil_settings.smaa_threshold); + pass.push_constant("lumaWeight", luma_weight); pass.clear_color(float4(0.0f)); pass.draw_procedural(GPU_PRIM_TRIS, 1, 3); } @@ -114,4 +119,91 @@ void Instance::antialiasing_draw(Manager &manager) manager.submit(this->smaa_resolve_ps); } +static float erfinv_approx(const float x) +{ + /* From: Approximating the `erfinv` function by Mike Giles. */ + /* To avoid trouble at the limit, clamp input to 1-epsilon. */ + const float a = math::min(fabsf(x), 0.99999994f); + float w = -logf((1.0f - a) * (1.0f + a)); + float p; + if (w < 5.0f) { + w = w - 2.5f; + p = 2.81022636e-08f; + p = p * w + 3.43273939e-07f; + p = p * w + -3.5233877e-06f; + p = p * w + -4.39150654e-06f; + p = p * w + 0.00021858087f; + p = p * w + -0.00125372503f; + p = p * w + -0.00417768164f; + p = p * w + 0.246640727f; + p = p * w + 1.50140941f; + } + else { + w = sqrtf(w) - 3.0f; + p = -0.000200214257f; + p = p * w + 0.000100950558f; + p = p * w + 0.00134934322f; + p = p * w + -0.00367342844f; + p = p * w + 0.00573950773f; + p = p * w + -0.0076224613f; + p = p * w + 0.00943887047f; + p = p * w + 1.00167406f; + p = p * w + 2.83297682f; + } + return p * x; +} + +float2 Instance::antialiasing_sample_get(const int sample_index, const int sample_count) +{ + if (sample_count < 2) { + return float2(0.0f); + } + + double halton[2]; + { + uint primes[2] = {2, 3}; + double ofs[2] = {0, 0}; + BLI_halton_2d(primes, ofs, sample_index, halton); + } + /* Uniform distribution [0..1]. */ + const float2 rand = float2(halton[0], halton[1]); + /* Uniform distribution [-1..1]. */ + const float2 rand_remap = rand * 2.0f - 1.0f; + /* Limit sampling region to avoid outliers. */ + const float2 rand_adjusted = rand_remap * 0.93f; + /* Gaussian distribution [-1..1]. */ + const float2 offset = float2(erfinv_approx(rand_adjusted.x), erfinv_approx(rand_adjusted.y)); + /* Gaussian fitted to Blackman-Harris (follows EEVEE). */ + const float sigma = 0.284f; + /* NOTE(fclem): Not sure where this sqrt comes from but is needed to match EEVEE. */ + return offset * sqrt(sigma); +} + +void Instance::antialiasing_accumulate(Manager &manager, const float alpha) +{ + BLI_assert_msg(this->render_color_tx.gpu_texture() != nullptr, + "This should only be called during render"); + const int2 size = this->render_color_tx.size().xy(); + + const eGPUTextureUsage usage = GPU_TEXTURE_USAGE_HOST_READ | GPU_TEXTURE_USAGE_SHADER_READ | + GPU_TEXTURE_USAGE_SHADER_WRITE | GPU_TEXTURE_USAGE_ATTACHMENT; + accumulation_tx.ensure_2d(GPENCIL_ACCUM_FORMAT, size, usage); + + { + PassSimple &pass = this->accumulate_ps; + pass.init(); + pass.state_set(DRW_STATE_WRITE_DEPTH /* There is no depth, but avoid blank state. */); + pass.shader_set(ShaderCache::get().accumulation.get()); + pass.bind_image("src_img", &this->render_color_tx); + pass.bind_image("dst_img", &this->accumulation_tx); + pass.push_constant("weight_src", alpha); + pass.push_constant("weight_dst", 1.0f - alpha); + pass.draw_procedural(GPU_PRIM_TRIS, 1, 3); + } + + accumulation_fb.ensure(size); + GPU_framebuffer_bind(this->accumulation_fb); + manager.submit(this->accumulate_ps); +} + } // namespace blender::draw::gpencil diff --git a/source/blender/draw/engines/gpencil/gpencil_defines.h b/source/blender/draw/engines/gpencil/gpencil_defines.h index b94ef809d3b..f489d5e4278 100644 --- a/source/blender/draw/engines/gpencil/gpencil_defines.h +++ b/source/blender/draw/engines/gpencil/gpencil_defines.h @@ -21,3 +21,6 @@ #define GPENCIL_LIGHT_SLOT 3 /* UBOs */ #define GPENCIL_SCENE_SLOT 2 + +#define GPENCIL_RENDER_FORMAT GPU_RGBA16F +#define GPENCIL_ACCUM_FORMAT GPU_RGBA16F diff --git a/source/blender/draw/engines/gpencil/gpencil_engine_private.hh b/source/blender/draw/engines/gpencil/gpencil_engine_private.hh index faa7dcce280..3d6101c08ab 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine_private.hh +++ b/source/blender/draw/engines/gpencil/gpencil_engine_private.hh @@ -124,6 +124,7 @@ struct Instance final : public DrawEngine { PassSimple smaa_edge_ps = {"smaa_edge"}; PassSimple smaa_weight_ps = {"smaa_weight"}; PassSimple smaa_resolve_ps = {"smaa_resolve"}; + PassSimple accumulate_ps = {"aa_accumulate"}; /* Composite the object depth to the default depth buffer to occlude overlays. */ PassSimple merge_depth_ps = {"merge_depth_ps"}; /* Invert mask buffer content. */ @@ -206,6 +207,9 @@ struct Instance final : public DrawEngine { /* Pointer to dtxl->depth */ GPUTexture *scene_depth_tx; GPUFrameBuffer *scene_fb; + /* Used for render accumulation antialiasing. */ + Texture accumulation_tx = {"gp_accumulation_tx"}; + Framebuffer accumulation_fb = {"gp_accumulation_fb"}; /* Copy of txl->dummy_tx */ GPUTexture *dummy_tx; /* Copy of v3d->shading.single_color. */ @@ -314,6 +318,10 @@ struct Instance final : public DrawEngine { void draw(Manager &manager) final; + void antialiasing_accumulate(Manager &manager, float alpha); + + static float2 antialiasing_sample_get(int sample_index, int sample_count); + private: tObject *object_sync_do(Object *ob, ResourceHandle res_handle); diff --git a/source/blender/draw/engines/gpencil/gpencil_render.cc b/source/blender/draw/engines/gpencil/gpencil_render.cc index 315ec39a81f..07ffe1e816c 100644 --- a/source/blender/draw/engines/gpencil/gpencil_render.cc +++ b/source/blender/draw/engines/gpencil/gpencil_render.cc @@ -5,6 +5,7 @@ /** \file * \ingroup draw */ +#include "BLI_math_geom.h" #include "BLI_math_matrix.h" #include "BLI_rect.h" @@ -23,26 +24,52 @@ namespace blender::draw::gpencil { -static void render_init(const DRWContext *draw_ctx, - Instance &inst, - RenderEngine *engine, - RenderLayer *render_layer, - const Depsgraph *depsgraph, - const rcti *rect) +/* Remap depth from viewspace to [0..1] to be able to use it with as GPU depth buffer. */ +static void remap_depth(const View &view, MutableSpan pix_z) +{ + if (view.is_persp()) { + const float4x4 &winmat = view.winmat(); + for (auto &pix : pix_z) { + pix = (-winmat[3][2] / -pix) - winmat[2][2]; + pix = clamp_f(pix * 0.5f + 0.5f, 0.0f, 1.0f); + } + } + else { + /* Keep in mind, near and far distance are negatives. */ + const float near = view.near_clip(); + const float far = view.far_clip(); + const float range_inv = 1.0f / fabsf(far - near); + for (auto &pix : pix_z) { + pix = (pix + near) * range_inv; + pix = clamp_f(pix, 0.0f, 1.0f); + } + } +} + +static void render_set_view(RenderEngine *engine, + const Depsgraph *depsgraph, + float2 aa_offset = float2{0.0f}) +{ + Object *camera = DEG_get_evaluated_object(depsgraph, RE_GetCamera(engine->re)); + + float4x4 winmat, viewinv; + RE_GetCameraWindow(engine->re, camera, winmat.ptr()); + RE_GetCameraModelMatrix(engine->re, camera, viewinv.ptr()); + + window_translate_m4(winmat.ptr(), winmat.ptr(), UNPACK2(aa_offset)); + + View::default_set(math::invert(viewinv), winmat); +} + +static void render_init_buffers(const DRWContext *draw_ctx, + Instance &inst, + RenderEngine *engine, + RenderLayer *render_layer, + const Depsgraph *depsgraph, + const rcti *rect) { Scene *scene = DEG_get_evaluated_scene(depsgraph); const int2 size = int2(draw_ctx->viewport_size_get()); - - /* Set the perspective & view matrix. */ - float winmat[4][4], viewmat[4][4], viewinv[4][4]; - - Object *camera = DEG_get_evaluated_object(depsgraph, RE_GetCamera(engine->re)); - RE_GetCameraWindow(engine->re, camera, winmat); - RE_GetCameraModelMatrix(engine->re, camera, viewinv); - - invert_m4_m4(viewmat, viewinv); - - View::default_set(float4x4(viewmat), float4x4(winmat)); View &view = View::default_get(); /* Create depth texture & color texture from render result. */ @@ -61,25 +88,7 @@ static void render_init(const DRWContext *draw_ctx, if (pix_z) { /* Depth need to be remapped to [0..1] range. */ pix_z = static_cast(MEM_dupallocN(pix_z)); - - int pix_num = rpass_z_src->rectx * rpass_z_src->recty; - - if (view.is_persp()) { - for (int i = 0; i < pix_num; i++) { - pix_z[i] = (-winmat[3][2] / -pix_z[i]) - winmat[2][2]; - pix_z[i] = clamp_f(pix_z[i] * 0.5f + 0.5f, 0.0f, 1.0f); - } - } - else { - /* Keep in mind, near and far distance are negatives. */ - float near = view.near_clip(); - float far = view.far_clip(); - float range_inv = 1.0f / fabsf(far - near); - for (int i = 0; i < pix_num; i++) { - pix_z[i] = (pix_z[i] + near) * range_inv; - pix_z[i] = clamp_f(pix_z[i], 0.0f, 1.0f); - } - } + remap_depth(view, {pix_z, rpass_z_src->rectx * rpass_z_src->recty}); } const bool do_region = (scene->r.mode & R_BORDER) != 0; @@ -203,8 +212,10 @@ static void render_result_combined(RenderLayer *rl, { RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_COMBINED, viewname); - GPU_framebuffer_bind(instance.render_fb); - GPU_framebuffer_read_color(instance.render_fb, + Framebuffer read_fb; + read_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(instance.accumulation_tx)); + GPU_framebuffer_bind(read_fb); + GPU_framebuffer_read_color(read_fb, rect->xmin, rect->ymin, BLI_rcti_size_x(rect), @@ -226,7 +237,8 @@ void Engine::render_to_image(RenderEngine *engine, RenderLayer *render_layer, co Manager &manager = *DRW_manager_get(); - render_init(draw_ctx, inst, engine, render_layer, depsgraph, &rect); + render_set_view(engine, depsgraph); + render_init_buffers(draw_ctx, inst, engine, render_layer, depsgraph, &rect); inst.init(); inst.camera = DEG_get_evaluated_object(depsgraph, RE_GetCamera(engine->re)); @@ -248,8 +260,24 @@ void Engine::render_to_image(RenderEngine *engine, RenderLayer *render_layer, co manager.end_sync(); - /* Render the gpencil object and merge the result to the underlying render. */ - inst.draw(manager); + const float aa_radius = clamp_f(draw_ctx->scene->r.gauss, 0.0f, 100.0f); + const int sample_count = draw_ctx->scene->grease_pencil_settings.aa_samples; + for (auto i : IndexRange(sample_count)) { + float2 aa_offset = Instance::antialiasing_sample_get(i, sample_count) * aa_radius; + aa_offset = 2.0f * aa_offset / float2(inst.render_color_tx.size()); + render_set_view(engine, depsgraph, aa_offset); + render_init_buffers(draw_ctx, inst, engine, render_layer, depsgraph, &rect); + + /* Render the gpencil object and merge the result to the underlying render. */ + inst.draw(manager); + + /* Weight of this render SSAA sample. The sum of previous samples is weighted by `1 - weight`. + * This diminishes after each new sample as we want all samples to be equally weighted inside + * the final result (inside the combined buffer). This weighting scheme allows to always store + * the resolved result making it ready for in-progress display or readback. */ + const float weight = 1.0f / (1.0f + i); + inst.antialiasing_accumulate(manager, weight); + } render_result_combined(render_layer, viewname, inst, &rect); render_result_z(draw_ctx, render_layer, viewname, inst, &rect); diff --git a/source/blender/draw/engines/gpencil/gpencil_shader.hh b/source/blender/draw/engines/gpencil/gpencil_shader.hh index b37505e2116..69dedf1248e 100644 --- a/source/blender/draw/engines/gpencil/gpencil_shader.hh +++ b/source/blender/draw/engines/gpencil/gpencil_shader.hh @@ -36,6 +36,8 @@ class ShaderCache { StaticShader antialiasing[3] = {{"gpencil_antialiasing_stage_0"}, {"gpencil_antialiasing_stage_1"}, {"gpencil_antialiasing_stage_2"}}; + /* Accumulation antialiasing */ + StaticShader accumulation = {"gpencil_antialiasing_accumulation"}; /* GPencil Object rendering */ StaticShader geometry = {"gpencil_geometry"}; /* All layer blend types in one shader! */ diff --git a/source/blender/draw/engines/gpencil/shaders/CMakeLists.txt b/source/blender/draw/engines/gpencil/shaders/CMakeLists.txt index 0545c3ebd99..9afc15f0939 100644 --- a/source/blender/draw/engines/gpencil/shaders/CMakeLists.txt +++ b/source/blender/draw/engines/gpencil/shaders/CMakeLists.txt @@ -26,6 +26,7 @@ set(SRC_GLSL_VERT ) set(SRC_GLSL_FRAG + gpencil_antialiasing_accumulation_frag.glsl gpencil_antialiasing_frag.glsl gpencil_depth_merge_frag.glsl gpencil_frag.glsl diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_accumulation_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_accumulation_frag.glsl new file mode 100644 index 00000000000..ca345bda1ae --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_accumulation_frag.glsl @@ -0,0 +1,34 @@ +/* SPDX-FileCopyrightText: 2020-2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "infos/gpencil_info.hh" + +FRAGMENT_SHADER_CREATE_INFO(gpencil_antialiasing_accumulation) + +vec4 colorspace_scene_to_perceptual(vec4 color) +{ + return vec4(log2(color.rgb + 0.5), color.a); +} + +vec4 colorspace_perceptual_to_scene(vec4 color) +{ + return vec4(exp2(color.rgb) - 0.5, color.a); +} + +void main() +{ + ivec2 texel = ivec2(gl_FragCoord.xy); + vec4 data_src = colorspace_scene_to_perceptual(max(vec4(0.0), imageLoadFast(src_img, texel))); + vec4 data_dst = colorspace_scene_to_perceptual(max(vec4(0.0), imageLoadFast(dst_img, texel))); + vec4 result = data_src * weight_src; + if (weight_dst > 0.0) { + /* Avoid uncleared data to mess with the result value. */ + result += data_dst * weight_dst; + } + if (data_src.a == 1.0 && data_dst.a == 1.0) { + /* Avoid float imprecision leading to non fully opaque renders. */ + result.a = 1.0; + } + imageStoreFast(dst_img, texel, colorspace_perceptual_to_scene(result)); +} diff --git a/source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh b/source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh index 2dee4a70578..9ebea7b04d1 100644 --- a/source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh +++ b/source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh @@ -181,4 +181,14 @@ ADDITIONAL_INFO(gpencil_antialiasing) DO_STATIC_COMPILATION() GPU_SHADER_CREATE_END() +GPU_SHADER_CREATE_INFO(gpencil_antialiasing_accumulation) +IMAGE(0, GPENCIL_RENDER_FORMAT, READ, FLOAT_2D, src_img) +IMAGE(1, GPENCIL_ACCUM_FORMAT, READ_WRITE, FLOAT_2D, dst_img) +PUSH_CONSTANT(FLOAT, weight_src) +PUSH_CONSTANT(FLOAT, weight_dst) +FRAGMENT_SOURCE("gpencil_antialiasing_accumulation_frag.glsl") +ADDITIONAL_INFO(draw_fullscreen) +DO_STATIC_COMPILATION() +GPU_SHADER_CREATE_END() + /** \} */ diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index 8033b7bb989..e50730c7a1a 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -236,6 +236,13 @@ .flag = SCE_EEVEE_TAA_REPROJECTION, \ } +#define _DNA_DEFAULT_SceneGreasePencil \ + { \ + .smaa_threshold = 1.0f, \ + .smaa_threshold_render = 0.25f, \ + .aa_samples = 8, \ + } + #define _DNA_DEFAULT_SceneHydra \ { \ .export_method = SCE_HYDRA_EXPORT_HYDRA, \ @@ -254,6 +261,8 @@ .safe_areas = _DNA_DEFAULT_DisplaySafeAreas, \ \ .eevee = _DNA_DEFAULT_SceneEEVEE, \ + \ + .grease_pencil_settings = _DNA_DEFAULT_SceneGreasePencil, \ \ .hydra = _DNA_DEFAULT_SceneHydra, \ .simulation_frame_start = 1, \ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index f72d3f157b6..5ecae861002 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1966,7 +1966,9 @@ typedef struct SceneEEVEE { typedef struct SceneGpencil { float smaa_threshold; - char _pad[4]; + float smaa_threshold_render; + int aa_samples; + char _pad0[4]; } SceneGpencil; typedef struct SceneHydra { diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c index 3763b0fb18c..fe72758c5d1 100644 --- a/source/blender/makesdna/intern/dna_defaults.c +++ b/source/blender/makesdna/intern/dna_defaults.c @@ -496,6 +496,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = { SDNA_DEFAULT_DECL_EX(SceneDisplay, Scene.display), SDNA_DEFAULT_DECL_EX(SceneEEVEE, Scene.eevee), SDNA_DEFAULT_DECL_EX(RaytraceEEVEE, Scene.eevee.ray_tracing_options), + SDNA_DEFAULT_DECL_EX(SceneGpencil, Scene.grease_pencil_settings), SDNA_DEFAULT_DECL(ToolSettings), SDNA_DEFAULT_DECL_EX(CurvePaintSettings, ToolSettings.curve_paint_settings), diff --git a/source/blender/makesrna/intern/rna_scene.cc b/source/blender/makesrna/intern/rna_scene.cc index 8220c238e43..099dccb397d 100644 --- a/source/blender/makesrna/intern/rna_scene.cc +++ b/source/blender/makesrna/intern/rna_scene.cc @@ -8491,13 +8491,35 @@ static void rna_def_scene_gpencil(BlenderRNA *brna) prop = RNA_def_property(srna, "antialias_threshold", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, nullptr, "smaa_threshold"); - RNA_def_property_float_default(prop, 1.0f); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_range(prop, 0.0f, 2.0f, 1, 3); RNA_def_property_ui_text(prop, - "Anti-Aliasing Threshold", + "SMAA Threshold Viewport", "Threshold for edge detection algorithm (higher values might over-blur " "some part of the image)"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr); + + prop = RNA_def_property(srna, "antialias_threshold_render", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, nullptr, "smaa_threshold_render"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 2.0f, 1, 3); + RNA_def_property_ui_text(prop, + "SMAA Threshold Render", + "Threshold for edge detection algorithm (higher values might over-blur " + "some part of the image). Only applies to final render"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr); + + prop = RNA_def_property(srna, "aa_samples", PROP_INT, PROP_NONE); + RNA_def_property_ui_text( + prop, + "Anti-Aliasing Samples", + "Number of supersampling anti-aliasing samples per pixel for final render"); + RNA_def_property_range(prop, 1, INT_MAX); + RNA_def_property_ui_range(prop, 1, 256, 1, 3); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr); + RNA_def_property_flag(prop, PROP_ANIMATABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr); }