GPv3: Switch back to the current grease pencil render engine

This makes it so GPv3 objects are rendered using the current grease pencil render engine.

A new `gpencil_next` engine was added at the beginning of the project, but it couldn't
be finished in time. This commit removes the `gpencil_next` engine as it is no longer
used. The current status of the new engine been pushed to the `gpencil-next` branch on
the `blender` repository.

Note: Onion skinning is not supported yet. This work will be done in a separate PR.

Fixes #115467 and #116347.

Pull Request: https://projects.blender.org/blender/blender/pulls/118664
This commit is contained in:
Falk David
2024-03-18 12:01:15 +01:00
committed by Falk David
parent 433d91fca8
commit a3243172bf
25 changed files with 579 additions and 2430 deletions

View File

@@ -182,10 +182,8 @@ set(SRC
engines/gpencil/gpencil_antialiasing.cc
engines/gpencil/gpencil_cache_utils.cc
engines/gpencil/gpencil_draw_data.cc
engines/gpencil/gpencil_engine.cc
engines/gpencil/gpencil_engine_c.cc
engines/gpencil/gpencil_render.cc
engines/gpencil/gpencil_shader.cc
engines/gpencil/gpencil_shader_c.cc
engines/gpencil/gpencil_shader_fx.cc
engines/select/select_draw_utils.cc
@@ -306,14 +304,7 @@ set(SRC
engines/eevee_next/eevee_volume.hh
engines/eevee_next/eevee_world.hh
engines/external/external_engine.h
engines/gpencil/gpencil_antialiasing.hh
engines/gpencil/gpencil_engine.h
engines/gpencil/gpencil_layer.hh
engines/gpencil/gpencil_light.hh
engines/gpencil/gpencil_material.hh
engines/gpencil/gpencil_object.hh
engines/gpencil/gpencil_shader.hh
engines/gpencil/gpencil_vfx.hh
engines/image/image_batches.hh
engines/image/image_buffer_cache.hh
engines/image/image_drawing_mode.hh
@@ -717,9 +708,7 @@ set(GLSL_SRC
intern/draw_shader_shared.h
engines/gpencil/shaders/gpencil_frag.glsl
engines/gpencil/shaders/grease_pencil_frag.glsl
engines/gpencil/shaders/gpencil_vert.glsl
engines/gpencil/shaders/grease_pencil_vert.glsl
engines/gpencil/shaders/gpencil_antialiasing_frag.glsl
engines/gpencil/shaders/gpencil_antialiasing_vert.glsl
engines/gpencil/shaders/gpencil_common_lib.glsl
@@ -727,7 +716,6 @@ set(GLSL_SRC
engines/gpencil/shaders/gpencil_mask_invert_frag.glsl
engines/gpencil/shaders/gpencil_depth_merge_frag.glsl
engines/gpencil/shaders/gpencil_depth_merge_vert.glsl
engines/gpencil/shaders/grease_pencil_depth_merge_vert.glsl
engines/gpencil/shaders/gpencil_vfx_frag.glsl
engines/gpencil/gpencil_defines.h

View File

@@ -1,154 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw
*/
#pragma once
#include "BKE_gpencil_legacy.h"
#include "BKE_image.h"
#include "DRW_gpu_wrapper.hh"
#include "DRW_render.hh"
#include "draw_manager.hh"
#include "draw_pass.hh"
#include "gpencil_shader.hh"
#include "BLI_smaa_textures.h"
namespace blender::draw::greasepencil {
using namespace draw;
/** Final anti-aliasing post processing and compositing on top of render. */
class AntiAliasing {
private:
ShaderModule &shaders_;
Texture smaa_search_tx_ = {"smaa_search",
GPU_R8,
GPU_TEXTURE_USAGE_SHADER_READ,
int2(SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT)};
Texture smaa_area_tx_ = {
"smaa_area", GPU_RG8, GPU_TEXTURE_USAGE_SHADER_READ, int2(AREATEX_WIDTH, AREATEX_HEIGHT)};
TextureFromPool edge_detect_tx_ = {"edge_detect_tx"};
Framebuffer edge_detect_fb_ = {"edge_detect_fb"};
PassSimple edge_detect_ps_ = {"edge_detect_ps"};
TextureFromPool blend_weight_tx_ = {"blend_weight_tx"};
Framebuffer blend_weight_fb_ = {"blend_weight_fb"};
PassSimple blend_weight_ps_ = {"blend_weight_ps"};
PassSimple resolve_ps_ = {"resolve_ps"};
bool draw_wireframe_ = false;
float luma_weight_ = 1.0f;
bool anti_aliasing_enabled_ = true;
public:
AntiAliasing(ShaderModule &shaders) : shaders_(shaders)
{
GPU_texture_update(smaa_search_tx_, GPU_DATA_UBYTE, searchTexBytes);
GPU_texture_update(smaa_area_tx_, GPU_DATA_UBYTE, areaTexBytes);
GPU_texture_filter_mode(smaa_search_tx_, true);
GPU_texture_filter_mode(smaa_area_tx_, true);
}
void init(const View3D *v3d, const Scene *scene)
{
if (v3d) {
draw_wireframe_ = (v3d->shading.type == OB_WIRE);
}
luma_weight_ = scene->grease_pencil_settings.smaa_threshold;
anti_aliasing_enabled_ = true; // GPENCIL_SIMPLIFY_AA(scene);
}
void begin_sync(TextureFromPool &color_tx, Framebuffer &scene_fb, TextureFromPool &reveal_tx)
{
/* TODO(fclem): No global access. */
const float *size = DRW_viewport_size_get();
const float *sizeinv = DRW_viewport_invert_size_get();
const float4 metrics = {sizeinv[0], sizeinv[1], size[0], size[1]};
anti_aliasing_pass(color_tx, reveal_tx, metrics);
/* Resolve pass. */
PassSimple &pass = resolve_ps_;
pass.init();
pass.framebuffer_set(&scene_fb);
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM);
pass.shader_set(shaders_.static_shader_get(ANTIALIASING_RESOLVE));
/** \note use color_tx as dummy if AA is disabled. */
pass.bind_texture("blendTex", anti_aliasing_enabled_ ? &blend_weight_tx_ : &color_tx);
pass.bind_texture("colorTex", &color_tx);
pass.bind_texture("revealTex", &reveal_tx);
pass.push_constant("doAntiAliasing", anti_aliasing_enabled_);
pass.push_constant("onlyAlpha", draw_wireframe_);
pass.push_constant("viewportMetrics", metrics);
pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
}
void draw(Manager &manager, const int2 render_resolution)
{
DRW_stats_group_start("Anti-Aliasing");
if (anti_aliasing_enabled_) {
edge_detect_tx_.acquire(render_resolution, GPU_RG8);
edge_detect_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(edge_detect_tx_));
manager.submit(edge_detect_ps_);
blend_weight_tx_.acquire(render_resolution, GPU_RGBA8);
blend_weight_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(blend_weight_tx_));
manager.submit(blend_weight_ps_);
edge_detect_tx_.release();
}
manager.submit(resolve_ps_);
blend_weight_tx_.release();
DRW_stats_group_end();
}
private:
void anti_aliasing_pass(TextureFromPool &color_tx,
TextureFromPool &reveal_tx,
const float4 metrics)
{
if (!anti_aliasing_enabled_) {
return;
}
/* Stage 1: Edge detection. */
edge_detect_ps_.init();
edge_detect_ps_.framebuffer_set(&edge_detect_fb_);
edge_detect_ps_.state_set(DRW_STATE_WRITE_COLOR);
edge_detect_ps_.shader_set(shaders_.static_shader_get(ANTIALIASING_EDGE_DETECT));
edge_detect_ps_.bind_texture("colorTex", &color_tx);
edge_detect_ps_.bind_texture("revealTex", &reveal_tx);
edge_detect_ps_.push_constant("viewportMetrics", metrics);
edge_detect_ps_.push_constant("lumaWeight", luma_weight_);
edge_detect_ps_.clear_color(float4(0.0f));
edge_detect_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
/* Stage 2: Blend Weight/Coord. */
blend_weight_ps_.init();
blend_weight_ps_.framebuffer_set(&blend_weight_fb_);
blend_weight_ps_.state_set(DRW_STATE_WRITE_COLOR);
blend_weight_ps_.shader_set(shaders_.static_shader_get(ANTIALIASING_BLEND_WEIGHT));
blend_weight_ps_.bind_texture("edgesTex", &edge_detect_tx_);
blend_weight_ps_.bind_texture("areaTex", smaa_area_tx_);
blend_weight_ps_.bind_texture("searchTex", smaa_search_tx_);
blend_weight_ps_.push_constant("viewportMetrics", metrics);
blend_weight_ps_.clear_color(float4(0.0f));
blend_weight_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
}
};
} // namespace blender::draw::greasepencil

View File

@@ -17,6 +17,7 @@
#include "BKE_gpencil_geom_legacy.h"
#include "BKE_gpencil_legacy.h"
#include "BKE_grease_pencil.hh"
#include "BKE_lib_id.hh"
#include "BKE_object.hh"
@@ -36,16 +37,18 @@
/** \name Object
* \{ */
GPENCIL_tObject *gpencil_object_cache_add(GPENCIL_PrivateData *pd, Object *ob)
GPENCIL_tObject *gpencil_object_cache_add(GPENCIL_PrivateData *pd,
Object *ob,
const bool is_stroke_order_3d,
const std::optional<blender::Bounds<float3>> bounds)
{
using namespace blender;
bGPdata *gpd = (bGPdata *)ob->data;
GPENCIL_tObject *tgp_ob = static_cast<GPENCIL_tObject *>(BLI_memblock_alloc(pd->gp_object_pool));
tgp_ob->layers.first = tgp_ob->layers.last = nullptr;
tgp_ob->vfx.first = tgp_ob->vfx.last = nullptr;
tgp_ob->camera_z = dot_v3v3(pd->camera_z_axis, ob->object_to_world().location());
tgp_ob->is_drawmode3d = (gpd->draw_mode == GP_DRAWMODE_3D) || pd->draw_depth_only;
tgp_ob->is_drawmode3d = is_stroke_order_3d || pd->draw_depth_only;
tgp_ob->object_scale = mat4_to_scale(ob->object_to_world().ptr());
/* Check if any material with holdout flag enabled. */
@@ -66,8 +69,6 @@ GPENCIL_tObject *gpencil_object_cache_add(GPENCIL_PrivateData *pd, Object *ob)
* strokes not aligned with the object axes. Maybe we could try to
* compute the minimum axis of all strokes. But this would be more
* computationally heavy and should go into the GPData evaluation. */
const std::optional<Bounds<float3>> bounds = BKE_gpencil_data_minmax(gpd).value_or(
Bounds(float3(0)));
float3 size = (bounds->max - bounds->min) * 0.5f;
float3 center = math::midpoint(bounds->min, bounds->max);
/* Convert bbox to matrix */
@@ -203,6 +204,28 @@ static float gpencil_layer_final_opacity_get(const GPENCIL_PrivateData *pd,
return gpl->opacity;
}
static float grease_pencil_layer_final_opacity_get(const GPENCIL_PrivateData *pd,
const Object *ob,
const GreasePencil &grease_pencil,
const blender::bke::greasepencil::Layer &layer)
{
const bool is_obact = ((pd->obact) && (pd->obact == ob));
const bool is_fade = (pd->fade_layer_opacity > -1.0f) && (is_obact) &&
grease_pencil.is_layer_active(&layer);
/* Defines layer opacity. For active object depends of layer opacity factor, and
* for no active object, depends if the fade grease pencil objects option is enabled. */
if (!pd->is_render) {
if (is_obact && is_fade) {
return layer.opacity * pd->fade_layer_opacity;
}
if (!is_obact && (pd->fade_gp_object_opacity > -1.0f)) {
return layer.opacity * pd->fade_gp_object_opacity;
}
}
return layer.opacity;
}
static void gpencil_layer_final_tint_and_alpha_get(const GPENCIL_PrivateData *pd,
const bGPdata *gpd,
const bGPDlayer *gpl,
@@ -253,6 +276,20 @@ static void gpencil_layer_random_color_get(const Object *ob,
hsv_to_rgb_v(hsv, r_color);
}
static void grease_pencil_layer_random_color_get(const Object *ob,
const blender::bke::greasepencil::Layer &layer,
float r_color[3])
{
const float hsv_saturation = 0.7f;
const float hsv_value = 0.6f;
uint ob_hash = BLI_ghashutil_strhash_p_murmur(ob->id.name);
uint gpl_hash = BLI_ghashutil_strhash_p_murmur(layer.name().c_str());
float hue = BLI_hash_int_01(ob_hash * gpl_hash);
const float hsv[3] = {hue, hsv_saturation, hsv_value};
hsv_to_rgb_v(hsv, r_color);
}
GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd,
const Object *ob,
const bGPDlayer *gpl,
@@ -433,4 +470,179 @@ GPENCIL_tLayer *gpencil_layer_cache_get(GPENCIL_tObject *tgp_ob, int number)
return nullptr;
}
GPENCIL_tLayer *grease_pencil_layer_cache_add(GPENCIL_PrivateData *pd,
const Object *ob,
const blender::bke::greasepencil::Layer &layer,
std::optional<int> /*onion_id*/,
GPENCIL_tObject *tgp_ob)
{
using namespace blender::bke::greasepencil;
const GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
const bool is_in_front = (ob->dtx & OB_DRAW_IN_FRONT);
/* Grease Pencil 3 doesn't have this. */
const bool is_screenspace = false;
const bool override_vertcol = (pd->v3d_color_type != -1);
const bool is_vert_col_mode = (pd->v3d_color_type == V3D_SHADING_VERTEX_COLOR) ||
(ob->mode == OB_MODE_VERTEX_PAINT) || pd->is_render;
const bool is_viewlayer_render = pd->is_render && !layer.view_layer_name().is_empty() &&
STREQ(pd->view_layer->name, layer.view_layer_name().c_str());
const bool disable_masks_render = is_viewlayer_render &&
(layer.base.flag & GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER) != 0;
bool is_masked = !disable_masks_render && layer.use_masks() &&
!BLI_listbase_is_empty(&layer.masks);
float vert_col_opacity = (override_vertcol) ?
(is_vert_col_mode ? pd->vertex_paint_opacity : 0.0f) :
pd->vertex_paint_opacity;
/* Negate thickness sign to tag that strokes are in screen space.
* Convert to world units (by default, 1 meter = 1000 pixels). */
float thickness_scale = (is_screenspace) ? -1.0f : 1.0f / 1000.0f;
float layer_opacity = grease_pencil_layer_final_opacity_get(pd, ob, grease_pencil, layer);
float4 layer_tint(0.0f);
float layer_alpha = pd->xray_alpha;
/* TODO: Onion skinning! */
// gpencil_layer_final_tint_and_alpha_get(pd, gpd, gpl, gpf, layer_tint, &layer_alpha);
/* Create the new layer descriptor. */
GPENCIL_tLayer *tgp_layer = static_cast<GPENCIL_tLayer *>(BLI_memblock_alloc(pd->gp_layer_pool));
BLI_LINKS_APPEND(&tgp_ob->layers, tgp_layer);
tgp_layer->layer_id = *grease_pencil.get_layer_index(layer);
tgp_layer->mask_bits = nullptr;
tgp_layer->mask_invert_bits = nullptr;
tgp_layer->blend_ps = nullptr;
/* Masking: Go through mask list and extract valid masks in a bitmap. */
if (is_masked) {
bool valid_mask = false;
/* WARNING: only #GP_MAX_MASKBITS amount of bits.
* TODO(fclem): Find a better system without any limitation. */
tgp_layer->mask_bits = static_cast<BLI_bitmap *>(BLI_memblock_alloc(pd->gp_maskbit_pool));
tgp_layer->mask_invert_bits = static_cast<BLI_bitmap *>(
BLI_memblock_alloc(pd->gp_maskbit_pool));
BLI_bitmap_set_all(tgp_layer->mask_bits, false, GP_MAX_MASKBITS);
LISTBASE_FOREACH (GreasePencilLayerMask *, mask, &layer.masks) {
if (mask->flag & GP_LAYER_MASK_HIDE) {
continue;
}
const TreeNode *node = grease_pencil.find_node_by_name(mask->layer_name);
if (node == nullptr) {
continue;
}
const Layer &mask_layer = node->as_layer();
if ((&mask_layer == &layer) || !mask_layer.is_visible()) {
continue;
}
const int index = *grease_pencil.get_layer_index(mask_layer);
if (index < GP_MAX_MASKBITS) {
const bool invert = (mask->flag & GP_LAYER_MASK_INVERT) != 0;
BLI_BITMAP_SET(tgp_layer->mask_bits, index, true);
BLI_BITMAP_SET(tgp_layer->mask_invert_bits, index, invert);
valid_mask = true;
}
}
if (valid_mask) {
pd->use_mask_fb = true;
}
else {
tgp_layer->mask_bits = nullptr;
}
is_masked = valid_mask;
}
/* Blending: Force blending for masked layer. */
if (is_masked || (layer.blend_mode != GP_LAYER_BLEND_NONE) || (layer_opacity < 1.0f)) {
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_EQUAL;
switch (layer.blend_mode) {
case GP_LAYER_BLEND_NONE:
state |= DRW_STATE_BLEND_ALPHA_PREMUL;
break;
case GP_LAYER_BLEND_ADD:
state |= DRW_STATE_BLEND_ADD_FULL;
break;
case GP_LAYER_BLEND_SUBTRACT:
state |= DRW_STATE_BLEND_SUB;
break;
case GP_LAYER_BLEND_MULTIPLY:
case GP_LAYER_BLEND_DIVIDE:
case GP_LAYER_BLEND_HARDLIGHT:
state |= DRW_STATE_BLEND_MUL;
break;
}
if (ELEM(layer.blend_mode, GP_LAYER_BLEND_SUBTRACT, GP_LAYER_BLEND_HARDLIGHT)) {
/* For these effect to propagate, we need a signed floating point buffer. */
pd->use_signed_fb = true;
}
tgp_layer->blend_ps = DRW_pass_create("GPencil Blend Layer", state);
GPUShader *sh = GPENCIL_shader_layer_blend_get();
DRWShadingGroup *grp = DRW_shgroup_create(sh, tgp_layer->blend_ps);
DRW_shgroup_uniform_int_copy(grp, "blendMode", layer.blend_mode);
DRW_shgroup_uniform_float_copy(grp, "blendOpacity", layer_opacity);
DRW_shgroup_uniform_texture_ref(grp, "colorBuf", &pd->color_layer_tx);
DRW_shgroup_uniform_texture_ref(grp, "revealBuf", &pd->reveal_layer_tx);
DRW_shgroup_uniform_texture_ref(grp, "maskBuf", (is_masked) ? &pd->mask_tx : &pd->dummy_tx);
DRW_shgroup_stencil_mask(grp, 0xFF);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
if (layer.blend_mode == GP_LAYER_BLEND_HARDLIGHT) {
/* We cannot do custom blending on Multi-Target frame-buffers.
* Workaround by doing 2 passes. */
grp = DRW_shgroup_create(sh, tgp_layer->blend_ps);
DRW_shgroup_state_disable(grp, DRW_STATE_BLEND_MUL);
DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ADD_FULL);
DRW_shgroup_uniform_int_copy(grp, "blendMode", 999);
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
}
pd->use_layer_fb = true;
}
/* Geometry pass */
{
GPUTexture *depth_tex = (is_in_front) ? pd->dummy_tx : pd->scene_depth_tx;
GPUTexture **mask_tex = (is_masked) ? &pd->mask_tx : &pd->dummy_tx;
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_BLEND_ALPHA_PREMUL;
/* For 2D mode, we render all strokes with uniform depth (increasing with stroke id). */
state |= tgp_ob->is_drawmode3d ? DRW_STATE_DEPTH_LESS_EQUAL : DRW_STATE_DEPTH_GREATER;
/* Always write stencil. Only used as optimization for blending. */
state |= DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_ALWAYS;
tgp_layer->geom_ps = DRW_pass_create("GPencil Layer", state);
GPUShader *sh = GPENCIL_shader_geometry_get();
DRWShadingGroup *grp = tgp_layer->base_shgrp = DRW_shgroup_create(sh, tgp_layer->geom_ps);
DRW_shgroup_uniform_texture(grp, "gpSceneDepthTexture", depth_tex);
DRW_shgroup_uniform_texture_ref(grp, "gpMaskTexture", mask_tex);
DRW_shgroup_uniform_vec3_copy(grp, "gpNormal", tgp_ob->plane_normal);
DRW_shgroup_uniform_bool_copy(grp, "gpStrokeOrder3d", tgp_ob->is_drawmode3d);
DRW_shgroup_uniform_float_copy(grp, "gpThicknessScale", tgp_ob->object_scale);
/* Replaced by a modifier in GPv3. */
DRW_shgroup_uniform_float_copy(grp, "gpThicknessOffset", 0.0f);
DRW_shgroup_uniform_float_copy(grp, "gpThicknessWorldScale", thickness_scale);
DRW_shgroup_uniform_float_copy(grp, "gpVertexColorOpacity", vert_col_opacity);
/* If random color type, need color by layer. */
float gpl_color[4];
copy_v4_v4(gpl_color, layer_tint);
if (pd->v3d_color_type == V3D_SHADING_RANDOM_COLOR) {
grease_pencil_layer_random_color_get(ob, layer, gpl_color);
gpl_color[3] = 1.0f;
}
DRW_shgroup_uniform_vec4_copy(grp, "gpLayerTint", gpl_color);
DRW_shgroup_uniform_float_copy(grp, "gpLayerOpacity", layer_alpha);
DRW_shgroup_stencil_mask(grp, 0xFF);
}
return tgp_layer;
}
/** \} */

View File

@@ -157,7 +157,10 @@ static MaterialGPencilStyle *gpencil_viewport_material_overrides(
return gp_style;
}
GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Object *ob, int *ofs)
GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd,
Object *ob,
int *ofs,
const bool is_vertex_mode)
{
GPENCIL_MaterialPool *matpool = pd->last_material_pool;
@@ -175,10 +178,8 @@ GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Obje
}
/* Force vertex color in solid mode with vertex paint mode. Same behavior as meshes. */
bGPdata *gpd = (bGPdata *)ob->data;
int color_type = (pd->v3d_color_type != -1 && GPENCIL_VERTEX_MODE(gpd)) ?
V3D_SHADING_VERTEX_COLOR :
pd->v3d_color_type;
int color_type = (pd->v3d_color_type != -1 && is_vertex_mode) ? V3D_SHADING_VERTEX_COLOR :
pd->v3d_color_type;
const eV3DShadingLightingMode lighting_mode = eV3DShadingLightingMode(
(pd->v3d != nullptr) ? eV3DShadingLightingMode(pd->v3d->shading.light) :
V3D_LIGHTING_STUDIO);

View File

@@ -1,449 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw
*/
#include "BKE_gpencil_modifier_legacy.h"
#include "BLI_listbase_wrapper.hh"
#include "DEG_depsgraph_query.hh"
#include "DNA_shader_fx_types.h"
#include "DRW_engine.hh"
#include "DRW_render.hh"
#include "ED_screen.hh"
#include "ED_view3d.hh"
#include "GPU_capabilities.h"
#include "IMB_imbuf_types.hh"
#include "RE_pipeline.h"
#include "draw_manager.hh"
#include "draw_pass.hh"
#define GP_LIGHT
#include "gpencil_antialiasing.hh"
#include "gpencil_defines.h"
#include "gpencil_engine.h"
#include "gpencil_layer.hh"
#include "gpencil_light.hh"
#include "gpencil_material.hh"
#include "gpencil_object.hh"
#include "gpencil_shader.hh"
#include "gpencil_shader_shared.h"
#include "gpencil_vfx.hh"
namespace blender::draw::greasepencil {
using namespace draw;
class Instance {
private:
ShaderModule &shaders;
LayerModule layers;
MaterialModule materials;
ObjectModule objects;
LightModule lights;
VfxModule vfx;
AntiAliasing anti_aliasing;
/** Contains all gpencil objects in the scene as well as their effect sub-passes. */
PassSortable main_ps_ = {"gp_main_ps"};
/** Contains all composited GPencil object. */
TextureFromPool depth_tx_ = {"gp_depth_tx"};
TextureFromPool color_tx_ = {"gp_color_tx"};
TextureFromPool reveal_tx_ = {"gp_reveal_tx"};
Framebuffer main_fb_ = {"gp_main_fb"};
/** Underlying scene pixel. Used to composite the output of the grease pencil render onto the
* scene (including merging the depth buffers). */
Framebuffer scene_fb_ = {"gp_scene_fb"};
/** Texture format for all intermediate buffers. */
eGPUTextureFormat texture_format_ = GPU_RGBA16F;
UniformBuffer<gpScene> scene_buf_;
/** Dummy textures. */
static constexpr float dummy_px_[4] = {1.0f, 0.0f, 1.0f, 1.0f};
Texture dummy_depth_tx_ = {"dummy_depth",
GPU_DEPTH_COMPONENT32F,
GPU_TEXTURE_USAGE_SHADER_READ,
int2(1),
(float *)dummy_px_};
Texture dummy_color_tx_ = {
"dummy_color", GPU_RGBA16F, GPU_TEXTURE_USAGE_SHADER_READ, int2(1), (float *)dummy_px_};
/** Scene depth used for manual depth testing. Default to dummy depth to skip depth test. */
GPUTexture *scene_depth_tx_ = dummy_depth_tx_;
/** Context. */
Depsgraph *depsgraph_ = nullptr;
Object *camera_ = nullptr;
Manager *manager_ = nullptr;
draw::View view_ = {"MainView"};
/** \note Needs not to be temporary variable since it is dereferenced later. */
std::array<float4, 2> clear_colors_ = {float4(0.0f, 0.0f, 0.0f, 0.0f),
float4(1.0f, 1.0f, 1.0f, 1.0f)};
public:
Instance()
: shaders(*ShaderModule::module_get()),
objects(layers, materials, shaders),
vfx(shaders),
anti_aliasing(shaders){};
void init(Depsgraph *depsgraph,
Manager *manager,
const DRWView *viewport_draw_view,
const View3D *v3d,
const RegionView3D *rv3d)
{
depsgraph_ = depsgraph;
manager_ = manager;
if (viewport_draw_view != nullptr) {
view_.sync(viewport_draw_view);
}
const Scene *scene = DEG_get_evaluated_scene(depsgraph_);
const bool is_viewport = (v3d != nullptr);
if (is_viewport) {
/* Use lower precision for viewport. */
texture_format_ = GPU_R11F_G11F_B10F;
camera_ = (rv3d->persp == RV3D_CAMOB) ? v3d->camera : nullptr;
}
objects.init(v3d, scene);
lights.init(v3d);
/* TODO(@fclem): VFX. */
// vfx.init(use_vfx_, camera_, rv3d);
anti_aliasing.init(v3d, scene);
}
void begin_sync()
{
objects.begin_sync(depsgraph_, view_);
layers.begin_sync();
materials.begin_sync();
lights.begin_sync(depsgraph_);
main_ps_.init();
PassMain::Sub &sub = main_ps_.sub("InitSubpass", -FLT_MAX);
sub.framebuffer_set(&main_fb_);
sub.clear_multi(clear_colors_);
/* TODO(fclem): Textures. */
sub.bind_texture(GPENCIL_SCENE_DEPTH_TEX_SLOT, &dummy_depth_tx_);
sub.bind_texture(GPENCIL_MASK_TEX_SLOT, &dummy_color_tx_);
sub.bind_texture(GPENCIL_FILL_TEX_SLOT, &dummy_color_tx_);
sub.bind_texture(GPENCIL_STROKE_TEX_SLOT, &dummy_color_tx_);
sub.bind_ubo(GPENCIL_SCENE_SLOT, &scene_buf_);
objects.bind_resources(sub);
layers.bind_resources(sub);
materials.bind_resources(sub);
lights.bind_resources(sub);
anti_aliasing.begin_sync(color_tx_, scene_fb_, reveal_tx_);
}
void object_sync(ObjectRef &object_ref)
{
switch (object_ref.object->type) {
case OB_GREASE_PENCIL:
objects.sync_grease_pencil(
*manager_, object_ref, main_fb_, scene_fb_, depth_tx_, main_ps_);
break;
case OB_LAMP:
lights.sync(object_ref);
break;
default:
break;
}
}
void end_sync()
{
objects.end_sync();
layers.end_sync();
materials.end_sync();
lights.end_sync();
}
void render_sync(RenderEngine *engine, Depsgraph *depsgraph)
{
/* TODO: Remove old draw manager calls. */
DRW_cache_restart();
manager_->begin_sync();
begin_sync();
auto object_sync_render =
[](void *vedata, Object *ob, RenderEngine * /*engine*/, Depsgraph * /*depsgraph*/) {
Instance &inst = *reinterpret_cast<Instance *>(vedata);
ObjectRef ob_ref = DRW_object_ref_get(ob);
inst.object_sync(ob_ref);
};
/* HACK: We pass `this` here so we have access to the `Instance` in `object_sync_render`. */
DRW_render_object_iter(this, engine, depsgraph, object_sync_render);
end_sync();
manager_->end_sync();
/* TODO: Remove old draw manager calls. */
DRW_render_instance_buffer_finish();
}
void draw(GPUTexture *dst_color_tx, GPUTexture *dst_depth_tx, const int2 render_resolution)
{
if (!objects.scene_has_visible_gpencil_object()) {
return;
}
scene_fb_.ensure(GPU_ATTACHMENT_TEXTURE(dst_depth_tx), GPU_ATTACHMENT_TEXTURE(dst_color_tx));
depth_tx_.acquire(render_resolution, GPU_DEPTH24_STENCIL8);
color_tx_.acquire(render_resolution, texture_format_);
reveal_tx_.acquire(render_resolution, texture_format_);
main_fb_.ensure(GPU_ATTACHMENT_TEXTURE(depth_tx_),
GPU_ATTACHMENT_TEXTURE(color_tx_),
GPU_ATTACHMENT_TEXTURE(reveal_tx_));
scene_buf_.render_size = float2(render_resolution);
scene_buf_.push_update();
objects.acquire_temporary_buffers(render_resolution, texture_format_);
manager_->submit(main_ps_, view_);
objects.release_temporary_buffers();
anti_aliasing.draw(*manager_, render_resolution);
depth_tx_.release();
color_tx_.release();
reveal_tx_.release();
}
draw::View &view()
{
return view_;
}
};
} // namespace blender::draw::greasepencil
/* -------------------------------------------------------------------- */
/** \name Interface with legacy C DRW manager
* \{ */
using namespace blender;
struct GPENCIL_NEXT_Data {
DrawEngineType *engine_type;
DRWViewportEmptyList *fbl;
DRWViewportEmptyList *txl;
DRWViewportEmptyList *psl;
DRWViewportEmptyList *stl;
draw::greasepencil::Instance *instance;
char info[GPU_INFO_SIZE];
};
static void gpencil_engine_init(void *vedata)
{
GPENCIL_NEXT_Data *ved = reinterpret_cast<GPENCIL_NEXT_Data *>(vedata);
if (ved->instance == nullptr) {
ved->instance = new draw::greasepencil::Instance();
}
draw::Manager *manager = DRW_manager_get();
const DRWContextState *ctx_state = DRW_context_state_get();
const DRWView *default_view = DRW_view_default_get();
ved->instance->init(
ctx_state->depsgraph, manager, default_view, ctx_state->v3d, ctx_state->rv3d);
}
static void gpencil_draw_scene(void *vedata)
{
GPENCIL_NEXT_Data *ved = reinterpret_cast<GPENCIL_NEXT_Data *>(vedata);
if (DRW_state_is_select() || DRW_state_is_depth()) {
return;
}
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
const DRWView *default_view = DRW_view_default_get();
const float2 viewport_size = DRW_viewport_size_get();
ved->instance->view().sync(default_view);
ved->instance->draw(dtxl->color, dtxl->depth, int2(viewport_size));
}
static void gpencil_cache_init(void *vedata)
{
reinterpret_cast<GPENCIL_NEXT_Data *>(vedata)->instance->begin_sync();
}
static void gpencil_cache_populate(void *vedata, Object *object)
{
draw::ObjectRef ref;
ref.object = object;
ref.dupli_object = DRW_object_get_dupli(object);
ref.dupli_parent = DRW_object_get_dupli_parent(object);
reinterpret_cast<GPENCIL_NEXT_Data *>(vedata)->instance->object_sync(ref);
}
static void gpencil_cache_finish(void *vedata)
{
reinterpret_cast<GPENCIL_NEXT_Data *>(vedata)->instance->end_sync();
}
static void gpencil_instance_free(void *instance)
{
delete reinterpret_cast<draw::greasepencil::Instance *>(instance);
}
static void gpencil_engine_free()
{
blender::draw::greasepencil::ShaderModule::module_free();
}
/** Get the color and depth textures of the render result in the render layer. */
static void get_render_result_textures(RenderEngine *engine,
RenderLayer *render_layer,
const draw::View &view,
const int2 render_resolution,
draw::Texture &r_color_tx,
draw::Texture &r_depth_tx)
{
/* Create depth texture & color texture from render result. */
const char *viewname = RE_GetActiveRenderView(engine->re);
RenderPass *rpass_z_src = RE_pass_find_by_name(render_layer, RE_PASSNAME_Z, viewname);
RenderPass *rpass_col_src = RE_pass_find_by_name(render_layer, RE_PASSNAME_COMBINED, viewname);
float *pix_z = (rpass_z_src) ? rpass_z_src->ibuf->float_buffer.data : nullptr;
float *pix_col = (rpass_col_src) ? rpass_col_src->ibuf->float_buffer.data : nullptr;
if (!pix_z || !pix_col) {
RE_engine_set_error_message(engine,
"Warning: To render grease pencil, enable Combined and Z passes.");
}
if (pix_z) {
/* Depth need to be remapped to [0..1] range. */
pix_z = static_cast<float *>(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] = (-view.winmat()[3][2] / -pix_z[i]) - view.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);
}
}
}
/* FIXME(fclem): we have a precision loss in the depth buffer because of this re-upload.
* Find where it comes from! */
const eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_HOST_READ;
r_depth_tx.ensure_2d(GPU_DEPTH_COMPONENT24, render_resolution, usage, pix_z);
r_color_tx.ensure_2d(GPU_RGBA16F, render_resolution, usage, pix_col);
}
static void gpencil_render_to_image(void * /*vedata*/,
RenderEngine *engine,
RenderLayer *render_layer,
const rcti * /*rect*/)
{
draw::greasepencil::Instance instance;
draw::Manager &manager = *DRW_manager_get();
Render *render = engine->re;
Depsgraph *depsgraph = DRW_context_state_get()->depsgraph;
Object *camera_original_ob = RE_GetCamera(render);
const char *viewname = RE_GetActiveRenderView(render);
const int2 render_resolution = int2(engine->resolution_x, engine->resolution_y);
instance.init(depsgraph, &manager, nullptr, nullptr, nullptr);
float4x4 viewinv, winmat;
Object *camera_eval = DEG_get_evaluated_object(depsgraph, camera_original_ob);
RE_GetCameraModelMatrix(render, camera_eval, viewinv.ptr());
float4x4 viewmat = math::invert(viewinv);
RE_GetCameraWindow(render, camera_eval, winmat.ptr());
instance.view().sync(viewmat, winmat);
instance.render_sync(engine, depsgraph);
draw::Texture color_tx;
draw::Texture depth_tx;
/* TODO: Support `R_BORDER` render mode. */
get_render_result_textures(
engine, render_layer, instance.view(), render_resolution, color_tx, depth_tx);
instance.draw(color_tx, depth_tx, render_resolution);
RenderPass *rp = RE_pass_find_by_name(render_layer, RE_PASSNAME_COMBINED, viewname);
if (!rp) {
return;
}
float *result = reinterpret_cast<float *>(color_tx.read<float4>(GPU_DATA_FLOAT));
if (result) {
BLI_mutex_lock(&engine->update_render_passes_mutex);
/* WORKAROUND: We use texture read to avoid using a frame-buffer to get the render result.
* However, on some implementation, we need a buffer with a few extra bytes for the read to
* happen correctly (see #GLTexture::read()). So we need a custom memory allocation. */
/* Avoid `memcpy()`, replace the pointer directly. */
RE_pass_set_buffer_data(rp, result);
BLI_mutex_unlock(&engine->update_render_passes_mutex);
}
}
extern "C" {
static const DrawEngineDataSize gpencil_data_size = DRW_VIEWPORT_DATA_SIZE(GPENCIL_NEXT_Data);
DrawEngineType draw_engine_gpencil_next_type = {
/*next*/ nullptr,
/*prev*/ nullptr,
/*idname*/ N_("Gpencil"),
/*vedata_size*/ &gpencil_data_size,
/*engine_init*/ &gpencil_engine_init,
/*engine_free*/ &gpencil_engine_free,
/*instance_free*/ &gpencil_instance_free,
/*cache_init*/ &gpencil_cache_init,
/*cache_populate*/ &gpencil_cache_populate,
/*cache_finish*/ &gpencil_cache_finish,
/*draw_scene*/ &gpencil_draw_scene,
/*view_update*/ nullptr,
/*id_update*/ nullptr,
/*render_to_image*/ &gpencil_render_to_image,
/*store_metadata*/ nullptr,
};
}
/** \} */

View File

@@ -13,6 +13,9 @@
#include "DRW_render.hh"
#include "BLI_bitmap.h"
#include "BLI_bounds.hh"
#include "BKE_grease_pencil.hh"
#include "GPU_batch.h"
@@ -26,7 +29,6 @@ extern "C" {
#include "gpencil_shader_shared.h"
extern DrawEngineType draw_engine_gpencil_type;
extern DrawEngineType draw_engine_gpencil_next_type;
struct GPENCIL_Data;
struct GPENCIL_StorageList;
@@ -322,7 +324,10 @@ typedef struct GPENCIL_PrivateData {
/* geometry batch cache functions */
struct GpencilBatchCache *gpencil_batch_cache_get(struct Object *ob, int cfra);
GPENCIL_tObject *gpencil_object_cache_add(GPENCIL_PrivateData *pd, Object *ob);
GPENCIL_tObject *gpencil_object_cache_add(GPENCIL_PrivateData *pd,
Object *ob,
bool is_stroke_order_3d,
std::optional<blender::Bounds<float3>> bounds);
void gpencil_object_cache_sort(GPENCIL_PrivateData *pd);
GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd,
@@ -332,12 +337,20 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd,
GPENCIL_tObject *tgp_ob);
GPENCIL_tLayer *gpencil_layer_cache_get(GPENCIL_tObject *tgp_ob, int number);
GPENCIL_tLayer *grease_pencil_layer_cache_add(GPENCIL_PrivateData *pd,
const Object *ob,
const blender::bke::greasepencil::Layer &layer,
std::optional<int> onion_id,
GPENCIL_tObject *tgp_ob);
/**
* Creates a linked list of material pool containing all materials assigned for a given object.
* We merge the material pools together if object does not contain a huge amount of materials.
* Also return an offset to the first material of the object in the UBO.
*/
GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Object *ob, int *ofs);
GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd,
Object *ob,
int *ofs,
bool is_vertex_mode);
void gpencil_material_resources_get(GPENCIL_MaterialPool *first_pool,
int mat_id,
struct GPUTexture **r_tex_stroke,
@@ -353,7 +366,10 @@ GPENCIL_LightPool *gpencil_light_pool_add(GPENCIL_PrivateData *pd);
GPENCIL_LightPool *gpencil_light_pool_create(GPENCIL_PrivateData *pd, Object *ob);
/* effects */
void gpencil_vfx_cache_populate(GPENCIL_Data *vedata, Object *ob, GPENCIL_tObject *tgp_ob);
void gpencil_vfx_cache_populate(GPENCIL_Data *vedata,
Object *ob,
GPENCIL_tObject *tgp_ob,
const bool is_edit_mode);
/* Shaders */
struct GPUShader *GPENCIL_shader_antialiasing(int stage);

View File

@@ -8,8 +8,12 @@
#include "DRW_engine.hh"
#include "DRW_render.hh"
#include "BKE_curves.hh"
#include "BKE_gpencil_geom_legacy.h"
#include "BKE_gpencil_legacy.h"
#include "BKE_gpencil_modifier_legacy.h"
#include "BKE_grease_pencil.h"
#include "BKE_grease_pencil.hh"
#include "BKE_lib_id.hh"
#include "BKE_main.hh"
#include "BKE_object.hh"
@@ -22,6 +26,7 @@
#include "BLI_link_utils.h"
#include "BLI_listbase.h"
#include "BLI_memblock.h"
#include "BLI_virtual_array.hh"
#include "DNA_camera_types.h"
#include "DNA_gpencil_legacy_types.h"
@@ -35,6 +40,7 @@
#include "DEG_depsgraph_query.hh"
#include "ED_grease_pencil.hh"
#include "ED_screen.hh"
#include "ED_view3d.hh"
@@ -113,8 +119,10 @@ void GPENCIL_engine_init(void *ved)
stl->pd->v3d_color_type = (v3d->shading.type == OB_SOLID) ? v3d->shading.color_type : -1;
/* Special case: If Vertex Paint mode, use always Vertex mode. */
if (v3d->shading.type == OB_SOLID && ctx->obact && ctx->obact->type == OB_GPENCIL_LEGACY &&
ctx->obact->mode == OB_MODE_VERTEX_GPENCIL_LEGACY)
if (v3d->shading.type == OB_SOLID && ctx->obact &&
((ctx->obact->type == OB_GPENCIL_LEGACY &&
ctx->obact->mode == OB_MODE_VERTEX_GPENCIL_LEGACY) ||
(ctx->obact->type == OB_GREASE_PENCIL && ctx->obact->mode == OB_MODE_VERTEX_PAINT)))
{
stl->pd->v3d_color_type = V3D_SHADING_VERTEX_COLOR;
}
@@ -559,7 +567,10 @@ static void gpencil_sbuffer_cache_populate_fast(GPENCIL_Data *vedata, gpIterPopu
GPUTexture *depth_texture = iter->pd->scene_depth_tx;
GPENCIL_tObject *last_tgp_ob = iter->pd->tobjects.last;
/* Create another temp object that only contain the stroke. */
iter->tgp_ob = gpencil_object_cache_add(iter->pd, iter->ob);
const std::optional<blender::Bounds<float3>> bounds = BKE_gpencil_data_minmax(gpd).value_or(
blender::Bounds(float3(0)));
iter->tgp_ob = gpencil_object_cache_add(
iter->pd, iter->ob, (gpd->draw_mode == GP_DRAWMODE_3D), bounds);
/* Remove from the main list. */
iter->pd->tobjects.last = last_tgp_ob;
last_tgp_ob->next = nullptr;
@@ -582,13 +593,202 @@ static void gpencil_sbuffer_cache_populate_fast(GPENCIL_Data *vedata, gpIterPopu
gpencil_stroke_cache_populate(nullptr, nullptr, iter->pd->sbuffer_stroke, iter);
gpencil_drawcall_flush(iter);
gpencil_vfx_cache_populate(vedata, iter->ob, iter->tgp_ob);
gpencil_vfx_cache_populate(
vedata, iter->ob, iter->tgp_ob, (gpd != nullptr && GPENCIL_ANY_EDIT_MODE(gpd)));
/* Restore state. */
iter->do_sbuffer_call = 0;
iter->pd->scene_depth_tx = depth_texture;
}
static GPENCIL_tObject *grease_pencil_object_cache_populate(GPENCIL_PrivateData *pd,
GPENCIL_TextureList *txl,
Object *ob)
{
using namespace blender;
using namespace blender::ed::greasepencil;
using namespace blender::bke::greasepencil;
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
const bool is_vertex_mode = (ob->mode & OB_MODE_VERTEX_PAINT) != 0;
const std::optional<blender::Bounds<float3>> bounds = grease_pencil.bounds_min_max_eval();
const bool use_stroke_order_3d = (grease_pencil.flag & GREASE_PENCIL_STROKE_ORDER_3D) != 0;
GPENCIL_tObject *tgp_ob = gpencil_object_cache_add(pd, ob, use_stroke_order_3d, bounds);
int mat_ofs = 0;
GPENCIL_MaterialPool *matpool = gpencil_material_pool_create(pd, ob, &mat_ofs, is_vertex_mode);
GPUTexture *tex_fill = txl->dummy_texture;
GPUTexture *tex_stroke = txl->dummy_texture;
GPUBatch *iter_geom = nullptr;
DRWShadingGroup *grp;
int vfirst = 0;
int vcount = 0;
const auto drawcall_flush = [&]() {
#if !DISABLE_BATCHING
if (iter_geom != nullptr) {
DRW_shgroup_call_range(grp, ob, iter_geom, vfirst, vcount);
}
#endif
iter_geom = nullptr;
vfirst = -1;
vcount = 0;
};
const auto drawcall_add = [&](GPUBatch *draw_geom, int v_first, int v_count) {
#if DISABLE_BATCHING
DRW_shgroup_call_range(grp, ob, geom, v_first, v_count);
return;
#endif
int last = vfirst + vcount;
/* Interrupt draw-call grouping if the sequence is not consecutive. */
if ((draw_geom != iter_geom) || (v_first - last > 0)) {
drawcall_flush();
}
iter_geom = draw_geom;
if (vfirst == -1) {
vfirst = v_first;
}
vcount = v_first + v_count - vfirst;
};
int t_offset = 0;
const Vector<DrawingInfo> drawings = retrieve_visible_drawings(*pd->scene, grease_pencil);
const Span<const Layer *> layers = grease_pencil.layers();
for (const DrawingInfo info : drawings) {
const Layer &layer = *layers[info.layer_index];
drawcall_flush();
GPENCIL_tLayer *tgp_layer = grease_pencil_layer_cache_add(pd, ob, layer, {}, tgp_ob);
const bool use_lights = pd->use_lighting &&
((layer.base.flag & GP_LAYER_TREE_NODE_USE_LIGHTS) != 0) &&
(ob->dtx & OB_USE_GPENCIL_LIGHTS);
GPUUniformBuf *lights_ubo = (use_lights) ? pd->global_light_pool->ubo :
pd->shadeless_light_pool->ubo;
GPUUniformBuf *ubo_mat;
gpencil_material_resources_get(matpool, 0, nullptr, nullptr, &ubo_mat);
grp = tgp_layer->base_shgrp;
DRW_shgroup_uniform_block(grp, "gp_lights", lights_ubo);
DRW_shgroup_uniform_block(grp, "gp_materials", ubo_mat);
DRW_shgroup_uniform_texture(grp, "gpFillTexture", tex_fill);
DRW_shgroup_uniform_texture(grp, "gpStrokeTexture", tex_stroke);
DRW_shgroup_uniform_int_copy(grp, "gpMaterialOffset", mat_ofs);
/* Since we don't use the sbuffer in GPv3, this is always 0. */
DRW_shgroup_uniform_float_copy(grp, "gpStrokeIndexOffset", 0.0f);
DRW_shgroup_uniform_vec2_copy(grp, "viewportSize", DRW_viewport_size_get());
const bke::CurvesGeometry &curves = info.drawing.strokes();
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
const bke::AttributeAccessor attributes = curves.attributes();
const VArray<int> stroke_materials = *attributes.lookup_or_default<int>(
"material_index", bke::AttrDomain::Curve, 0);
const VArray<bool> cyclic = *attributes.lookup_or_default<bool>(
"cyclic", bke::AttrDomain::Curve, false);
IndexMaskMemory memory;
const IndexMask visible_strokes = ed::greasepencil::retrieve_visible_strokes(
*ob, info.drawing, memory);
visible_strokes.foreach_index([&](const int stroke_i) {
const IndexRange points = points_by_curve[stroke_i];
const int material_index = stroke_materials[stroke_i];
MaterialGPencilStyle *gp_style = BKE_object_material_get(ob, material_index + 1)->gp_style;
const bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0;
const bool show_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0);
const bool show_fill = (points.size() >= 3) &&
((gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0) &&
(!pd->simplify_fill);
const bool only_lines = !ELEM(ob->mode,
OB_MODE_PAINT_GREASE_PENCIL,
OB_MODE_WEIGHT_PAINT,
OB_MODE_VERTEX_PAINT) &&
info.frame_number != pd->cfra && pd->use_multiedit_lines_only;
/* bool is_onion = gpl && gpf && gpf->runtime.onion_id != 0; */
const bool is_onion = false;
const bool hide_onion = is_onion && ((gp_style->flag & GP_MATERIAL_HIDE_ONIONSKIN) != 0);
const int num_stroke_triangles = (points.size() >= 3) ? (points.size() - 2) : 0;
const int num_stroke_vertices = (points.size() +
int(cyclic[stroke_i] && (points.size() >= 3)));
const bool skip_stroke = hide_material || (!show_stroke && !show_fill) ||
(only_lines && !is_onion) || hide_onion;
if (skip_stroke) {
t_offset += num_stroke_triangles;
t_offset += num_stroke_vertices * 2;
return;
}
GPUUniformBuf *new_ubo_mat;
GPUTexture *new_tex_fill = nullptr;
GPUTexture *new_tex_stroke = nullptr;
gpencil_material_resources_get(
matpool, mat_ofs + material_index, &new_tex_stroke, &new_tex_fill, &new_ubo_mat);
bool resource_changed = (ubo_mat != new_ubo_mat) ||
(new_tex_fill && (new_tex_fill != tex_fill)) ||
(new_tex_stroke && (new_tex_stroke != tex_stroke));
if (resource_changed) {
drawcall_flush();
grp = DRW_shgroup_create_sub(grp);
if (new_ubo_mat != ubo_mat) {
DRW_shgroup_uniform_block(grp, "gp_materials", new_ubo_mat);
ubo_mat = new_ubo_mat;
}
if (new_tex_fill) {
DRW_shgroup_uniform_texture(grp, "gpFillTexture", new_tex_fill);
tex_fill = new_tex_fill;
}
if (new_tex_stroke) {
DRW_shgroup_uniform_texture(grp, "gpStrokeTexture", new_tex_stroke);
tex_stroke = new_tex_stroke;
}
}
GPUBatch *geom = draw::DRW_cache_grease_pencil_get(pd->scene, ob);
if (iter_geom != geom) {
drawcall_flush();
GPUVertBuf *position_tx = draw::DRW_cache_grease_pencil_position_buffer_get(pd->scene, ob);
GPUVertBuf *color_tx = draw::DRW_cache_grease_pencil_color_buffer_get(pd->scene, ob);
DRW_shgroup_buffer_texture(grp, "gp_pos_tx", position_tx);
DRW_shgroup_buffer_texture(grp, "gp_col_tx", color_tx);
}
if (show_fill) {
int v_first = t_offset * 3;
int v_count = num_stroke_triangles * 3;
drawcall_add(geom, v_first, v_count);
}
t_offset += num_stroke_triangles;
if (show_stroke) {
int v_first = t_offset * 3;
int v_count = num_stroke_vertices * 2 * 3;
drawcall_add(geom, v_first, v_count);
}
t_offset += num_stroke_vertices * 2;
});
}
drawcall_flush();
return tgp_ob;
}
void GPENCIL_cache_populate(void *ved, Object *ob)
{
GPENCIL_Data *vedata = (GPENCIL_Data *)ved;
@@ -602,16 +802,18 @@ void GPENCIL_cache_populate(void *ved, Object *ob)
}
if (ob->data && (ob->type == OB_GPENCIL_LEGACY) && (ob->dt >= OB_SOLID)) {
bGPdata *gpd = (bGPdata *)ob->data;
const std::optional<blender::Bounds<float3>> bounds = BKE_gpencil_data_minmax(gpd).value_or(
blender::Bounds(float3(0)));
gpIterPopulateData iter = {nullptr};
iter.ob = ob;
iter.pd = pd;
iter.tgp_ob = gpencil_object_cache_add(pd, ob);
iter.matpool = gpencil_material_pool_create(pd, ob, &iter.mat_ofs);
iter.tgp_ob = gpencil_object_cache_add(pd, ob, (gpd->draw_mode == GP_DRAWMODE_3D), bounds);
iter.matpool = gpencil_material_pool_create(pd, ob, &iter.mat_ofs, GPENCIL_VERTEX_MODE(gpd));
iter.tex_fill = txl->dummy_texture;
iter.tex_stroke = txl->dummy_texture;
/* Special case for rendering onion skin. */
bGPdata *gpd = (bGPdata *)ob->data;
bool do_onion = (!pd->is_render) ? pd->do_onion : (gpd->onion_flag & GP_ONION_GHOST_ALWAYS);
gpd->runtime.playing = short(pd->playing);
@@ -646,12 +848,18 @@ void GPENCIL_cache_populate(void *ved, Object *ob)
gpencil_sbuffer_cache_populate(&iter);
}
gpencil_vfx_cache_populate(vedata, ob, iter.tgp_ob);
gpencil_vfx_cache_populate(
vedata, ob, iter.tgp_ob, (gpd != nullptr && GPENCIL_ANY_EDIT_MODE(gpd)));
if (pd->do_fast_drawing) {
gpencil_sbuffer_cache_populate_fast(vedata, &iter);
}
}
else if (ob->data && (ob->type == OB_GREASE_PENCIL) && (ob->dt >= OB_SOLID)) {
GPENCIL_tObject *tgp_ob = grease_pencil_object_cache_populate(pd, txl, ob);
gpencil_vfx_cache_populate(
vedata, ob, tgp_ob, ELEM(ob->mode, OB_MODE_EDIT, OB_MODE_SCULPT, OB_MODE_WEIGHT_PAINT));
}
if (ob->type == OB_LAMP && pd->use_lights) {
gpencil_light_pool_populate(pd->global_light_pool, ob);
@@ -928,7 +1136,7 @@ void GPENCIL_draw_scene(void *ved)
/* Fade 3D objects. */
if ((!pd->is_render) && (pd->fade_3d_object_opacity > -1.0f) && (pd->obact != nullptr) &&
(pd->obact->type == OB_GPENCIL_LEGACY))
(ELEM(pd->obact->type, OB_GPENCIL_LEGACY, OB_GREASE_PENCIL)))
{
float background_color[3];
ED_view3d_background_color_get(pd->scene, pd->v3d, background_color);

View File

@@ -1,69 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw
*/
#pragma once
#include "BKE_grease_pencil.hh"
#include "DRW_gpu_wrapper.hh"
#include "DRW_render.hh"
#include "draw_manager.hh"
#include "draw_pass.hh"
namespace blender::draw::greasepencil {
using namespace draw;
class LayerModule {
private:
/** Contains all Objects in the scene. Indexed by gpObject.layer_offset + layer_id. */
StorageVectorBuffer<gpLayer> layers_buf_ = "gp_layers_buf";
public:
void begin_sync()
{
layers_buf_.clear();
}
void sync(const Object * /*object*/,
const bke::greasepencil::Layer &layer,
bool &do_layer_blending)
{
/* TODO(fclem): All of this is placeholder. */
gpLayer gp_layer;
// gp_layer.vertex_color_opacity = 0.0f; unused
gp_layer.thickness_offset = 0.0f;
gp_layer.tint = float4(1.0f, 1.0f, 1.0f, 0.0f);
gp_layer.stroke_index_offset = 0.0f;
gp_layer.opacity = layer.opacity;
if (layer.opacity != 1.0f) {
do_layer_blending = true;
}
layers_buf_.append(gp_layer);
}
void end_sync()
{
layers_buf_.push_update();
}
void bind_resources(PassMain::Sub &sub)
{
sub.bind_ssbo(GPENCIL_LAYER_SLOT, &layers_buf_);
}
uint object_offset_get() const
{
return layers_buf_.size();
}
};
} // namespace blender::draw::greasepencil

View File

@@ -1,131 +0,0 @@
/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw
*/
#pragma once
#include "BKE_gpencil_legacy.h"
#include "BKE_image.h"
#include "DRW_gpu_wrapper.hh"
#include "DRW_render.hh"
#include "draw_manager.hh"
#include "draw_pass.hh"
namespace blender::draw::greasepencil {
using namespace draw;
class LightModule {
private:
/** Contains all lights in the scene. */
StorageVectorBuffer<gpLight> lights_buf_ = "gp_lights_buf";
float studiolight_intensity_ = 1.0f;
bool use_scene_lights_ = true;
bool use_scene_world_ = true;
public:
void init(const View3D *v3d)
{
if (v3d != nullptr) {
use_scene_lights_ = V3D_USES_SCENE_LIGHTS(v3d);
use_scene_world_ = V3D_USES_SCENE_WORLD(v3d);
studiolight_intensity_ = v3d->shading.studiolight_intensity;
}
}
void begin_sync(Depsgraph *depsgraph)
{
lights_buf_.clear();
World *world = DEG_get_evaluated_scene(depsgraph)->world;
if (world != nullptr && use_scene_world_) {
ambient_sync(float3(world->horr, world->horg, world->horb));
}
else {
ambient_sync(float3(studiolight_intensity_));
}
}
void sync(ObjectRef &object_ref)
{
if (!use_scene_lights_) {
return;
}
const Object *ob = object_ref.object;
const Light *la = static_cast<Light *>(ob->data);
float light_power;
if (la->type == LA_AREA) {
light_power = 1.0f / (4.0f * M_PI);
}
else if (ELEM(la->type, LA_SPOT, LA_LOCAL)) {
light_power = 1.0f / (4.0f * M_PI * M_PI);
}
else {
light_power = 1.0f / M_PI;
}
gpLight light;
float4x4 &mat = *reinterpret_cast<float4x4 *>(&light.right);
switch (la->type) {
case LA_SPOT:
light.type = GP_LIGHT_TYPE_SPOT;
light.spot_size = cosf(la->spotsize * 0.5f);
light.spot_blend = (1.0f - light.spot_size) * la->spotblend;
mat = ob->world_to_object();
break;
case LA_AREA:
/* Simulate area lights using a spot light. */
light.type = GP_LIGHT_TYPE_SPOT;
light.spot_size = cosf(M_PI_2);
light.spot_blend = (1.0f - light.spot_size) * 1.0f;
normalize_m4_m4(mat.ptr(), ob->object_to_world().ptr());
invert_m4(mat.ptr());
break;
case LA_SUN:
light.forward = math::normalize(float3(ob->object_to_world().ptr()[2]));
light.type = GP_LIGHT_TYPE_SUN;
break;
default:
light.type = GP_LIGHT_TYPE_POINT;
break;
}
light.position = float3(object_ref.object->object_to_world().location());
light.color = float3(la->r, la->g, la->b) * (la->energy * light_power);
lights_buf_.append(light);
}
void end_sync()
{
/* Tag light list end. */
gpLight light;
light.color[0] = -1.0f;
lights_buf_.append(light);
lights_buf_.push_update();
}
void bind_resources(PassMain::Sub &sub)
{
sub.bind_ssbo(GPENCIL_LIGHT_SLOT, &lights_buf_);
}
private:
void ambient_sync(float3 color)
{
gpLight light;
light.type = GP_LIGHT_TYPE_AMBIENT;
light.color = color;
lights_buf_.append(light);
}
};
} // namespace blender::draw::greasepencil

View File

@@ -1,319 +0,0 @@
/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw
*/
#pragma once
#include "BKE_gpencil_legacy.h"
#include "BKE_image.h"
#include "DRW_gpu_wrapper.hh"
#include "DRW_render.hh"
#include "draw_manager.hh"
#include "draw_pass.hh"
namespace blender::draw::greasepencil {
using namespace draw;
class MaterialModule {
private:
/** Contains all materials in the scene. Indexed by gpObject.material_offset + mat_id. */
StorageVectorBuffer<gpMaterial> materials_buf_ = "gp_materials_buf";
/** List of all the texture used. */
Vector<GPUTexture *> texture_pool_;
int v3d_color_type_ = -1;
int v3d_lighting_mode_ = V3D_LIGHTING_STUDIO;
float v3d_xray_alpha_ = 1.0f;
float3 v3d_single_color_ = {1.0f, 1.0f, 1.0f};
public:
void init(const View3D *v3d)
{
if (v3d != nullptr) {
const bool shading_mode_supports_xray = (v3d->shading.type <= OB_SOLID);
v3d_color_type_ = (v3d->shading.type == OB_SOLID) ? v3d->shading.color_type : -1;
v3d_lighting_mode_ = v3d->shading.light;
v3d_xray_alpha_ = (shading_mode_supports_xray && XRAY_ENABLED(v3d)) ? XRAY_ALPHA(v3d) : 1.0f;
v3d_single_color_ = float3(v3d->shading.single_color);
}
}
void begin_sync()
{
materials_buf_.clear();
texture_pool_.clear();
}
void sync(const Object *object, const int mat_slot, bool &do_mat_holdout)
{
const MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings((Object *)object,
mat_slot + 1);
MaterialGPencilStyle gp_style_override;
gp_style = material_override(object, &gp_style_override, gp_style);
/* Material with holdout. */
if (gp_style->flag & GP_MATERIAL_IS_STROKE_HOLDOUT) {
do_mat_holdout = true;
}
if (gp_style->flag & GP_MATERIAL_IS_FILL_HOLDOUT) {
do_mat_holdout = true;
}
materials_buf_.append(material_sync(gp_style));
}
void end_sync()
{
materials_buf_.push_update();
}
void bind_resources(PassMain::Sub &sub)
{
sub.bind_ssbo(GPENCIL_MATERIAL_SLOT, &materials_buf_);
}
uint object_offset_get() const
{
return materials_buf_.size();
}
private:
/* Returns the correct flag for this texture. */
gpMaterialFlag texture_sync(::Image *image, gpMaterialFlag use_flag, gpMaterialFlag premul_flag)
{
ImageUser iuser = {nullptr};
GPUTexture *gpu_tex = nullptr;
bool premul = false;
if (image == nullptr) {
texture_pool_.append(nullptr);
return GP_FLAG_NONE;
}
gpu_tex = BKE_image_get_gpu_texture(image, &iuser);
if (gpu_tex) {
premul = (image->alpha_mode == IMA_ALPHA_PREMUL) != 0;
}
texture_pool_.append(gpu_tex);
return gpMaterialFlag(use_flag | (premul ? premul_flag : GP_FLAG_NONE));
}
void uv_transform_sync(const float ofs[2],
const float scale[2],
const float rotation,
float r_rot_scale[2][2],
float r_offset[2])
{
/* OPTI this could use 3x2 matrices and reduce the number of operations drastically. */
float mat[4][4];
unit_m4(mat);
/* Offset to center. */
translate_m4(mat, 0.5f, 0.5f, 0.0f);
/* Reversed order. */
float3 tmp = {1.0f / scale[0], 1.0f / scale[1], 0.0};
rescale_m4(mat, tmp);
rotate_m4(mat, 'Z', -rotation);
translate_m4(mat, ofs[0], ofs[1], 0.0f);
/* Convert to 3x2 */
copy_v2_v2(r_rot_scale[0], mat[0]);
copy_v2_v2(r_rot_scale[1], mat[1]);
copy_v2_v2(r_offset, mat[3]);
}
/* Amend object fill color in order to avoid completely flat look. */
void material_shade_color(float color[3])
{
if (v3d_lighting_mode_ == V3D_LIGHTING_FLAT) {
return;
}
/* This is scene referred color, not gamma corrected and not per perceptual.
* So we lower the threshold a bit. (1.0 / 3.0) */
if (color[0] + color[1] + color[2] > 1.1) {
add_v3_fl(color, -0.25f);
}
else {
add_v3_fl(color, 0.15f);
}
CLAMP3(color, 0.0f, 1.0f);
}
const MaterialGPencilStyle *material_override(const Object *object,
MaterialGPencilStyle *gp_style_override,
const MaterialGPencilStyle *gp_style)
{
switch (v3d_color_type_) {
case V3D_SHADING_MATERIAL_COLOR:
case V3D_SHADING_RANDOM_COLOR:
/* Random uses a random color per layer and this is done using the layer tint.
* A simple color by object, like meshes, is not practical in grease pencil. */
copy_v4_v4(gp_style_override->stroke_rgba, gp_style->stroke_rgba);
copy_v4_v4(gp_style_override->fill_rgba, gp_style->fill_rgba);
gp_style = gp_style_override;
gp_style_override->stroke_style = GP_MATERIAL_STROKE_STYLE_SOLID;
gp_style_override->fill_style = GP_MATERIAL_FILL_STYLE_SOLID;
break;
case V3D_SHADING_TEXTURE_COLOR:
*gp_style_override = blender::dna::shallow_copy(*gp_style);
gp_style = gp_style_override;
if ((gp_style_override->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) &&
(gp_style_override->sima))
{
copy_v4_fl(gp_style_override->stroke_rgba, 1.0f);
gp_style_override->mix_stroke_factor = 0.0f;
}
if ((gp_style_override->fill_style == GP_MATERIAL_FILL_STYLE_TEXTURE) &&
(gp_style_override->ima))
{
copy_v4_fl(gp_style_override->fill_rgba, 1.0f);
gp_style_override->mix_factor = 0.0f;
}
else if (gp_style_override->fill_style == GP_MATERIAL_FILL_STYLE_GRADIENT) {
/* gp_style_override->fill_rgba is needed for correct gradient. */
gp_style_override->mix_factor = 0.0f;
}
break;
case V3D_SHADING_SINGLE_COLOR:
gp_style = gp_style_override;
gp_style_override->stroke_style = GP_MATERIAL_STROKE_STYLE_SOLID;
gp_style_override->fill_style = GP_MATERIAL_FILL_STYLE_SOLID;
copy_v3_v3(gp_style_override->fill_rgba, v3d_single_color_);
gp_style_override->fill_rgba[3] = 1.0f;
copy_v4_v4(gp_style_override->stroke_rgba, gp_style_override->fill_rgba);
material_shade_color(gp_style_override->fill_rgba);
break;
case V3D_SHADING_OBJECT_COLOR:
gp_style = gp_style_override;
gp_style_override->stroke_style = GP_MATERIAL_STROKE_STYLE_SOLID;
gp_style_override->fill_style = GP_MATERIAL_FILL_STYLE_SOLID;
copy_v4_v4(gp_style_override->fill_rgba, object->color);
copy_v4_v4(gp_style_override->stroke_rgba, object->color);
material_shade_color(gp_style_override->fill_rgba);
break;
case V3D_SHADING_VERTEX_COLOR:
gp_style = gp_style_override;
gp_style_override->stroke_style = GP_MATERIAL_STROKE_STYLE_SOLID;
gp_style_override->fill_style = GP_MATERIAL_FILL_STYLE_SOLID;
copy_v4_fl(gp_style_override->fill_rgba, 1.0f);
copy_v4_fl(gp_style_override->stroke_rgba, 1.0f);
break;
default:
break;
}
return gp_style;
}
gpMaterial material_sync(const MaterialGPencilStyle *gp_style)
{
gpMaterial material;
material.flag = 0;
/* Dots/Square alignment. */
if (gp_style->mode != GP_MATERIAL_MODE_LINE) {
switch (gp_style->alignment_mode) {
case GP_MATERIAL_FOLLOW_PATH:
material.flag = GP_STROKE_ALIGNMENT_STROKE;
break;
case GP_MATERIAL_FOLLOW_OBJ:
material.flag = GP_STROKE_ALIGNMENT_OBJECT;
break;
case GP_MATERIAL_FOLLOW_FIXED:
default:
material.flag = GP_STROKE_ALIGNMENT_FIXED;
break;
}
if (gp_style->mode == GP_MATERIAL_MODE_DOT) {
material.flag |= GP_STROKE_DOTS;
}
}
/* Overlap. */
if ((gp_style->mode != GP_MATERIAL_MODE_LINE) ||
(gp_style->flag & GP_MATERIAL_DISABLE_STENCIL))
{
material.flag |= GP_STROKE_OVERLAP;
}
/* Material with holdout. */
if (gp_style->flag & GP_MATERIAL_IS_STROKE_HOLDOUT) {
material.flag |= GP_STROKE_HOLDOUT;
}
if (gp_style->flag & GP_MATERIAL_IS_FILL_HOLDOUT) {
material.flag |= GP_FILL_HOLDOUT;
}
/* Dots or Squares rotation. */
material.alignment_rot[0] = cosf(gp_style->alignment_rotation);
material.alignment_rot[1] = sinf(gp_style->alignment_rotation);
if (gp_style->flag & GP_MATERIAL_STROKE_SHOW) {
material.flag |= GP_SHOW_STROKE;
}
if (gp_style->flag & GP_MATERIAL_FILL_SHOW) {
material.flag |= GP_SHOW_FILL;
}
/* Stroke Style */
if ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) && (gp_style->sima)) {
material.flag |= texture_sync(
gp_style->sima, GP_STROKE_TEXTURE_USE, GP_STROKE_TEXTURE_PREMUL);
copy_v4_v4(material.stroke_color, gp_style->stroke_rgba);
material.stroke_texture_mix = 1.0f - gp_style->mix_stroke_factor;
material.stroke_u_scale = 500.0f / gp_style->texture_pixsize;
}
else /* if (gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_SOLID) */ {
texture_sync(nullptr, GP_FLAG_NONE, GP_FLAG_NONE);
material.flag &= ~GP_STROKE_TEXTURE_USE;
copy_v4_v4(material.stroke_color, gp_style->stroke_rgba);
material.stroke_texture_mix = 0.0f;
}
/* Fill Style */
if ((gp_style->fill_style == GP_MATERIAL_FILL_STYLE_TEXTURE) && (gp_style->ima)) {
material.flag |= texture_sync(gp_style->ima, GP_FILL_TEXTURE_USE, GP_FILL_TEXTURE_PREMUL);
material.flag |= (gp_style->flag & GP_MATERIAL_TEX_CLAMP) ? GP_FILL_TEXTURE_CLIP : 0;
uv_transform_sync(gp_style->texture_offset,
gp_style->texture_scale,
gp_style->texture_angle,
(float(*)[2]) & material.fill_uv_rot_scale[0],
material.fill_uv_offset);
copy_v4_v4(material.fill_color, gp_style->fill_rgba);
material.fill_texture_mix = 1.0f - gp_style->mix_factor;
}
else if (gp_style->fill_style == GP_MATERIAL_FILL_STYLE_GRADIENT) {
texture_sync(nullptr, GP_FLAG_NONE, GP_FLAG_NONE);
bool use_radial = (gp_style->gradient_type == GP_MATERIAL_GRADIENT_RADIAL);
material.flag |= GP_FILL_GRADIENT_USE;
material.flag |= use_radial ? GP_FILL_GRADIENT_RADIAL : 0;
uv_transform_sync(gp_style->texture_offset,
gp_style->texture_scale,
gp_style->texture_angle,
(float(*)[2]) & material.fill_uv_rot_scale[0],
material.fill_uv_offset);
copy_v4_v4(material.fill_color, gp_style->fill_rgba);
copy_v4_v4(material.fill_mix_color, gp_style->mix_rgba);
material.fill_texture_mix = 1.0f - gp_style->mix_factor;
if (gp_style->flag & GP_MATERIAL_FLIP_FILL) {
swap_v4_v4(material.fill_color, material.fill_mix_color);
}
}
else /* if (gp_style->fill_style == GP_MATERIAL_FILL_STYLE_SOLID) */ {
texture_sync(nullptr, GP_FLAG_NONE, GP_FLAG_NONE);
copy_v4_v4(material.fill_color, gp_style->fill_rgba);
material.fill_texture_mix = 0.0f;
}
return material;
}
};
} // namespace blender::draw::greasepencil

View File

@@ -1,324 +0,0 @@
/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw
*/
#pragma once
#include "BLI_math_quaternion_types.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_image.h"
#include "DRW_gpu_wrapper.hh"
#include "DRW_render.hh"
#include "draw_manager.hh"
#include "draw_pass.hh"
#include "gpencil_layer.hh"
#include "gpencil_material.hh"
#include "gpencil_shader.hh"
namespace blender::draw::greasepencil {
using namespace draw;
class ObjectModule {
private:
LayerModule &layers_;
MaterialModule &materials_;
ShaderModule &shaders_;
/** Contains all Objects in the scene. Indexed by drw_ResourceID. */
StorageArrayBuffer<gpObject> objects_buf_ = "gp_objects_buf";
/** Contains all gpencil objects in the scene as well as their effect sub-passes. */
PassSortable main_ps_ = {"gp_main_ps"};
/** Contains all composited GPencil layers from one object if is uses VFX. */
TextureFromPool object_color_tx_ = {"gp_color_object_tx"};
TextureFromPool object_reveal_tx_ = {"gp_reveal_object_tx"};
Framebuffer object_fb_ = {"gp_object_fb"};
bool is_object_fb_needed_ = false;
/** Contains all strokes from one layer if is uses blending. (also used as target for VFX) */
TextureFromPool layer_color_tx_ = {"gp_color_layer_tx"};
TextureFromPool layer_reveal_tx_ = {"gp_reveal_layer_tx"};
Framebuffer layer_fb_ = {"gp_layer_fb"};
bool is_layer_fb_needed_ = false;
bool use_onion_ = true;
bool use_stroke_fill_ = true;
bool use_vfx_ = true;
bool is_render_ = true;
bool is_persp_ = true;
/** Forward vector used to sort gpencil objects. */
float3 camera_forward_;
float3 camera_pos_;
const Scene *scene_ = nullptr;
/** \note Needs not to be temporary variable since it is dereferenced later. */
std::array<float4, 2> clear_colors_ = {float4(0.0f, 0.0f, 0.0f, 0.0f),
float4(1.0f, 1.0f, 1.0f, 1.0f)};
public:
ObjectModule(LayerModule &layers, MaterialModule &materials, ShaderModule &shaders)
: layers_(layers), materials_(materials), shaders_(shaders){};
void init(const View3D *v3d, const Scene *scene)
{
const bool is_viewport = (v3d != nullptr);
scene_ = scene;
if (is_viewport) {
/* TODO(fclem): Avoid access to global DRW. */
const bContext *evil_C = DRW_context_state_get()->evil_C;
const bool playing = (evil_C != nullptr) ?
ED_screen_animation_playing(CTX_wm_manager(evil_C)) != nullptr :
false;
const bool hide_overlay = ((v3d->flag2 & V3D_HIDE_OVERLAYS) != 0);
const bool show_onion = ((v3d->gp_flag & V3D_GP_SHOW_ONION_SKIN) != 0);
use_onion_ = show_onion && !hide_overlay && !playing;
use_stroke_fill_ = GPENCIL_SIMPLIFY_FILL(scene, playing);
use_vfx_ = GPENCIL_SIMPLIFY_FX(scene, playing);
is_render_ = false;
}
else {
use_stroke_fill_ = GPENCIL_SIMPLIFY_FILL(scene, false);
use_vfx_ = GPENCIL_SIMPLIFY_FX(scene, false);
}
}
void begin_sync(Depsgraph * /*depsgraph*/, const View &main_view)
{
camera_forward_ = main_view.forward();
camera_pos_ = main_view.location();
is_object_fb_needed_ = false;
is_layer_fb_needed_ = false;
is_persp_ = main_view.is_persp();
/* TODO(fclem): Shrink buffer. */
// objects_buf_.shrink();
}
void sync_grease_pencil(Manager &manager,
ObjectRef &object_ref,
Framebuffer &main_fb,
Framebuffer &scene_fb,
TextureFromPool &depth_tx,
PassSortable &main_ps)
{
using namespace blender::bke::greasepencil;
Object *object = object_ref.object;
const GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
if (grease_pencil.drawings().is_empty()) {
return;
}
const bool is_stroke_order_3d = false; /* TODO */
bool do_material_holdout = false;
bool do_layer_blending = false;
bool object_has_vfx = false; /* TODO: `vfx.object_has_vfx(gpd);`. */
uint material_offset = materials_.object_offset_get();
for (const int i : IndexRange(BKE_object_material_count_eval(object))) {
materials_.sync(object, i, do_material_holdout);
}
uint layer_offset = layers_.object_offset_get();
for (const Layer *layer : grease_pencil.layers()) {
layers_.sync(object, *layer, do_layer_blending);
}
/* Order rendering using camera Z distance. */
float3 position = float3(object->object_to_world().location());
float camera_z = math::dot(position, camera_forward_);
PassMain::Sub &object_subpass = main_ps.sub("GPObject", camera_z);
object_subpass.framebuffer_set((object_has_vfx) ? &object_fb_ : &main_fb);
object_subpass.clear_depth(is_stroke_order_3d ? 1.0f : 0.0f);
if (object_has_vfx) {
object_subpass.clear_multi(clear_colors_);
}
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_BLEND_ALPHA_PREMUL;
/* For 2D mode, we render all strokes with uniform depth (increasing with stroke id). */
state |= (is_stroke_order_3d) ? DRW_STATE_DEPTH_LESS_EQUAL : DRW_STATE_DEPTH_GREATER;
/* Always write stencil. Only used as optimization for blending. */
state |= DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_ALWAYS;
object_subpass.state_set(state);
object_subpass.shader_set(shaders_.static_shader_get(GREASE_PENCIL));
GPUVertBuf *position_tx = DRW_cache_grease_pencil_position_buffer_get(scene_, object);
GPUVertBuf *color_tx = DRW_cache_grease_pencil_color_buffer_get(scene_, object);
GPUBatch *geom = DRW_cache_grease_pencil_get(scene_, object);
/* TODO(fclem): Pass per frame object matrix here. */
ResourceHandle handle = manager.resource_handle(object_ref);
gpObject &ob = objects_buf_.get_or_resize(handle.resource_index());
ob.is_shadeless = false;
ob.stroke_order3d = false;
ob.tint = float4(1.0); // frame_tint_get(gpd, frame.gpf, current_frame_);
ob.layer_offset = layer_offset;
ob.material_offset = material_offset;
if (do_layer_blending) {
/* TODO: Do layer blending. */
// for (const LayerData &layer : frame.layers) {
// UNUSED_VARS(layer);
// if (has_blending(layer)) {
// object_subpass.framebuffer_set(*vfx_fb.current());
// }
/* TODO(fclem): Only draw subrange of geometry for this layer. */
object_subpass.bind_texture("gp_pos_tx", position_tx);
object_subpass.bind_texture("gp_col_tx", color_tx);
object_subpass.draw(geom, handle);
/* TODO: Do layer blending. */
// if (has_blending(layer)) {
// layer_blend_sync(object_ref, object_subpass);
// }
// }
}
else {
/* Fast path. */
object_subpass.bind_texture("gp_pos_tx", position_tx);
object_subpass.bind_texture("gp_col_tx", color_tx);
object_subpass.draw(geom, handle);
}
/** Merging the object depth buffer into the scene depth buffer. */
float4x4 plane_mat = get_object_plane_mat(*object);
ResourceHandle handle_plane_mat = manager.resource_handle(plane_mat);
object_subpass.framebuffer_set(&scene_fb);
object_subpass.state_set(DRW_STATE_DEPTH_LESS | DRW_STATE_WRITE_DEPTH);
object_subpass.shader_set(shaders_.static_shader_get(DEPTH_MERGE));
object_subpass.bind_texture("depthBuf", (object_has_vfx) ? nullptr : &depth_tx);
object_subpass.draw(DRW_cache_quad_get(), handle_plane_mat);
/* TODO: Do object VFX. */
#if 0
if (object_has_vfx) {
VfxContext vfx_ctx(object_subpass,
layer_fb_,
object_fb_,
object_color_tx_,
layer_color_tx_,
object_reveal_tx_,
layer_reveal_tx_,
is_render_);
/* \note Update this boolean as the actual number of vfx drawn might differ. */
object_has_vfx = vfx.object_sync(main_fb_, object_ref, vfx_ctx, do_material_holdout);
if (object_has_vfx || do_layer_blending) {
is_layer_fb_needed_ = true;
}
}
#endif
}
void end_sync()
{
objects_buf_.push_update();
}
void bind_resources(PassMain::Sub &sub)
{
sub.bind_ssbo(GPENCIL_OBJECT_SLOT, &objects_buf_);
}
void acquire_temporary_buffers(int2 render_size, eGPUTextureFormat format)
{
object_color_tx_.acquire(render_size, format);
object_reveal_tx_.acquire(render_size, format);
object_fb_.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(object_color_tx_),
GPU_ATTACHMENT_TEXTURE(object_reveal_tx_));
if (is_layer_fb_needed_) {
layer_color_tx_.acquire(render_size, format);
layer_reveal_tx_.acquire(render_size, format);
layer_fb_.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(layer_color_tx_),
GPU_ATTACHMENT_TEXTURE(layer_reveal_tx_));
}
}
void release_temporary_buffers()
{
object_color_tx_.release();
object_reveal_tx_.release();
layer_color_tx_.release();
layer_reveal_tx_.release();
}
bool scene_has_visible_gpencil_object() const
{
return objects_buf_.size() > 0;
}
/**
* Define a matrix that will be used to render a triangle to merge the depth of the rendered
* gpencil object with the rest of the scene.
*/
float4x4 get_object_plane_mat(const Object &object)
{
using namespace math;
/* Find the normal most likely to represent the gpObject. */
/* TODO: This does not work quite well if you use
* strokes not aligned with the object axes. Maybe we could try to
* compute the minimum axis of all strokes. But this would be more
* computationally heavy and should go into the GPData evaluation. */
BLI_assert(object.type == OB_GREASE_PENCIL);
const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(object.data);
const std::optional<Bounds<float3>> bounds = grease_pencil.bounds_min_max_eval();
if (!bounds) {
return float4x4::identity();
}
/* Convert bbox to matrix */
const float3 size = float3(bounds->max - bounds->min) + 1e-8f;
const float3 center = midpoint(bounds->min, bounds->max);
/* BBox space to World. */
const float4x4 &object_to_world = object.object_to_world();
float4x4 bbox_mat = object_to_world *
from_loc_rot_scale<float4x4>(center, Quaternion::identity(), size);
float3 plane_normal;
if (is_persp_) {
/* BBox center to camera vector. */
plane_normal = camera_pos_ - bbox_mat.location();
}
else {
plane_normal = camera_forward_;
}
/* World to BBox space. */
float4x4 bbox_mat_inv = invert(bbox_mat);
/* mat_inv_t is a "normal" matrix which will transform
* BBox normal space to world space. */
float4x4 bbox_mat_inv_t = transpose(bbox_mat_inv);
/* Normalize the vector in BBox space. */
plane_normal = normalize(transform_direction(bbox_mat_inv, plane_normal));
plane_normal = normalize(transform_direction(bbox_mat_inv_t, plane_normal));
float4x4 plane_mat = from_up_axis<float4x4>(plane_normal);
float radius = length(transform_direction(object_to_world, size));
plane_mat = scale(plane_mat, float3(radius));
plane_mat.location() = transform_point(object_to_world, center);
return plane_mat;
}
};
} // namespace blender::draw::greasepencil

View File

@@ -146,7 +146,7 @@ static void GPENCIL_render_cache(void *vedata,
RenderEngine * /*engine*/,
Depsgraph * /*depsgraph*/)
{
if (ob && ELEM(ob->type, OB_GPENCIL_LEGACY, OB_LAMP)) {
if (ob && ELEM(ob->type, OB_GPENCIL_LEGACY, OB_GREASE_PENCIL, OB_LAMP)) {
if (DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF) {
GPENCIL_cache_populate(vedata, ob);
}

View File

@@ -1,119 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw
*/
#include "gpencil_shader.hh"
#include <iostream>
namespace blender::draw::greasepencil {
ShaderModule *ShaderModule::g_shader_module = nullptr;
ShaderModule *ShaderModule::module_get()
{
if (g_shader_module == nullptr) {
/* TODO(@fclem) thread-safety. */
g_shader_module = new ShaderModule();
}
return g_shader_module;
}
void ShaderModule::module_free()
{
if (g_shader_module != nullptr) {
/* TODO(@fclem) thread-safety. */
delete g_shader_module;
g_shader_module = nullptr;
}
}
ShaderModule::ShaderModule()
{
for (GPUShader *&shader : shaders_) {
shader = nullptr;
}
#ifndef NDEBUG
/* Ensure all shader are described. */
for (auto i : IndexRange(MAX_SHADER_TYPE)) {
const char *name = static_shader_create_info_name_get(eShaderType(i));
if (name == nullptr) {
std::cerr << "GPencil: Missing case for eShaderType(" << i
<< ") in static_shader_create_info_name_get()." << std::endl;
BLI_assert(0);
}
const GPUShaderCreateInfo *create_info = GPU_shader_create_info_get(name);
BLI_assert_msg(create_info != nullptr, "GPencil: Missing create info for static shader.");
}
#endif
}
ShaderModule::~ShaderModule()
{
for (GPUShader *&shader : shaders_) {
DRW_SHADER_FREE_SAFE(shader);
}
}
const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_type)
{
switch (shader_type) {
case ANTIALIASING_EDGE_DETECT:
return "gpencil_antialiasing_stage_0";
case ANTIALIASING_BLEND_WEIGHT:
return "gpencil_antialiasing_stage_1";
case ANTIALIASING_RESOLVE:
return "gpencil_antialiasing_stage_2";
case GREASE_PENCIL:
return "gpencil_geometry_next";
case LAYER_BLEND:
return "gpencil_layer_blend";
case DEPTH_MERGE:
return "grease_pencil_depth_merge";
case MASK_INVERT:
return "gpencil_mask_invert";
case FX_COMPOSITE:
return "gpencil_fx_composite";
case FX_COLORIZE:
return "gpencil_fx_colorize";
case FX_BLUR:
return "gpencil_fx_blur";
case FX_GLOW:
return "gpencil_fx_glow";
case FX_PIXEL:
return "gpencil_fx_pixelize";
case FX_RIM:
return "gpencil_fx_rim";
case FX_SHADOW:
return "gpencil_fx_shadow";
case FX_TRANSFORM:
return "gpencil_fx_transform";
/* To avoid compiler warning about missing case. */
case MAX_SHADER_TYPE:
return "";
}
return "";
}
GPUShader *ShaderModule::static_shader_get(eShaderType shader_type)
{
if (shaders_[shader_type] == nullptr) {
const char *shader_name = static_shader_create_info_name_get(shader_type);
shaders_[shader_type] = GPU_shader_create_from_info_name(shader_name);
if (shaders_[shader_type] == nullptr) {
std::cerr << "GPencil: error: Could not compile static shader \"" << shader_name << "\""
<< std::endl;
}
BLI_assert(shaders_[shader_type] != nullptr);
}
return shaders_[shader_type];
}
} // namespace blender::draw::greasepencil

View File

@@ -1,66 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw
*/
#pragma once
#include "DRW_render.hh"
namespace blender::draw::greasepencil {
enum eShaderType {
/* SMAA anti-aliasing. */
ANTIALIASING_EDGE_DETECT = 0,
ANTIALIASING_BLEND_WEIGHT,
ANTIALIASING_RESOLVE,
/* GPencil Object rendering */
GREASE_PENCIL,
/* All layer blend types in one shader! */
LAYER_BLEND,
/* Merge the final object depth to the depth buffer. */
DEPTH_MERGE,
/* Invert the content of the mask buffer. */
MASK_INVERT,
/* Final Compositing over rendered background. */
FX_COMPOSITE,
/* Effects. */
FX_COLORIZE,
FX_BLUR,
FX_GLOW,
FX_PIXEL,
FX_RIM,
FX_SHADOW,
FX_TRANSFORM,
MAX_SHADER_TYPE,
};
/**
* Shader module. shared between instances.
*/
class ShaderModule {
private:
std::array<GPUShader *, MAX_SHADER_TYPE> shaders_;
/** Shared shader module across all engine instances. */
static ShaderModule *g_shader_module;
public:
ShaderModule();
~ShaderModule();
GPUShader *static_shader_get(eShaderType shader_type);
/** Only to be used by Instance constructor. */
static ShaderModule *module_get();
static void module_free();
private:
const char *static_shader_create_info_name_get(eShaderType shader_type);
};
} // namespace blender::draw::greasepencil

View File

@@ -22,17 +22,12 @@
#include "gpencil_engine.h"
/* verify if this fx is active */
static bool effect_is_active(bGPdata *gpd, ShaderFxData *fx, bool is_viewport)
static bool effect_is_active(ShaderFxData *fx, bool is_edit, bool is_viewport)
{
if (fx == nullptr) {
return false;
}
if (gpd == nullptr) {
return false;
}
bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd);
if (((fx->mode & eShaderFxMode_Editmode) == 0) && (is_edit) && (is_viewport)) {
return false;
}
@@ -590,9 +585,11 @@ static void gpencil_vfx_swirl(SwirlShaderFxData *fx, Object * /*ob*/, gpIterVfxD
DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
}
void gpencil_vfx_cache_populate(GPENCIL_Data *vedata, Object *ob, GPENCIL_tObject *tgp_ob)
void gpencil_vfx_cache_populate(GPENCIL_Data *vedata,
Object *ob,
GPENCIL_tObject *tgp_ob,
const bool is_edit_mode)
{
bGPdata *gpd = (bGPdata *)ob->data;
GPENCIL_FramebufferList *fbl = vedata->fbl;
GPENCIL_PrivateData *pd = vedata->stl->pd;
@@ -610,7 +607,7 @@ void gpencil_vfx_cache_populate(GPENCIL_Data *vedata, Object *ob, GPENCIL_tObjec
/* If simplify enabled, nothing more to do. */
if (!pd->simplify_fx) {
LISTBASE_FOREACH (ShaderFxData *, fx, &ob->shader_fx) {
if (effect_is_active(gpd, fx, pd->is_viewport)) {
if (effect_is_active(fx, is_edit_mode, pd->is_viewport)) {
switch (fx->type) {
case eShaderFxType_Blur:
gpencil_vfx_blur((BlurShaderFxData *)fx, ob, &iter);

View File

@@ -8,11 +8,8 @@
# include "GPU_shader_shared_utils.h"
# ifndef __cplusplus
typedef struct gpScene gpScene;
typedef struct gpMaterial gpMaterial;
typedef struct gpLight gpLight;
typedef struct gpObject gpObject;
typedef struct gpLayer gpLayer;
typedef enum gpMaterialFlag gpMaterialFlag;
# ifdef GP_LIGHT
typedef enum gpLightType gpLightType;
@@ -38,8 +35,6 @@ enum gpMaterialFlag {
GP_FILL_TEXTURE_CLIP = (1u << 12u),
GP_FILL_GRADIENT_USE = (1u << 13u),
GP_FILL_GRADIENT_RADIAL = (1u << 14u),
GP_SHOW_STROKE = (1u << 15u),
GP_SHOW_FILL = (1u << 16u),
GP_FILL_FLAGS = (GP_FILL_TEXTURE_USE | GP_FILL_TEXTURE_PREMUL | GP_FILL_TEXTURE_CLIP |
GP_FILL_GRADIENT_USE | GP_FILL_GRADIENT_RADIAL | GP_FILL_HOLDOUT),
};
@@ -60,12 +55,6 @@ enum gpLightType {
# define gpLightType uint
#endif
struct gpScene {
float2 render_size;
float2 _pad0;
};
BLI_STATIC_ASSERT_ALIGN(gpScene, 16)
struct gpMaterial {
float4 stroke_color;
float4 fill_color;
@@ -132,38 +121,6 @@ struct gpLight {
BLI_STATIC_ASSERT_ALIGN(gpLight, 16)
#endif
struct gpObject {
/** Weather or not to apply lighting to the GPencil object. */
bool32_t is_shadeless;
/** Switch between 2d and 3D stroke order. */
bool32_t stroke_order3d;
/** Offset inside the layer buffer to the first layer data of this object. */
uint layer_offset;
/** Offset inside the material buffer to the first material data of this object. */
uint material_offset;
/** Color to multiply to the final mixed color. */
float4 tint;
/** Color to multiply to the final mixed color. */
float3 normal;
float _pad0;
};
BLI_STATIC_ASSERT_ALIGN(gpObject, 16)
struct gpLayer {
/** Amount of vertex color to blend with actual material color. */
float vertex_color_opacity;
/** Thickness change of all the strokes. */
float thickness_offset;
/** Thickness change of all the strokes. */
float opacity;
/** Offset to apply to stroke index to be able to insert a currently drawn stroke in between. */
float stroke_index_offset;
/** Color to multiply to the final mixed color. */
float4 tint;
};
BLI_STATIC_ASSERT_ALIGN(gpLayer, 16)
#ifndef GPU_SHADER
# undef gpMaterialFlag
# undef gpLightType

View File

@@ -1,336 +0,0 @@
/* SPDX-FileCopyrightText: 2017 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup draw
*/
#include "BKE_camera.h"
#include "BLI_listbase_wrapper.hh"
#include "DNA_camera_types.h"
#include "DNA_gpencil_legacy_types.h"
#include "DNA_shader_fx_types.h"
#include "draw_manager.hh"
#include "draw_pass.hh"
#include "gpencil_engine.h"
#include "gpencil_shader.hh"
#include "gpencil_shader_shared.h"
namespace blender::draw::greasepencil {
using namespace draw;
struct VfxContext {
PassMain::Sub *object_subpass;
SwapChain<GPUFrameBuffer **, 2> vfx_fb;
SwapChain<GPUTexture **, 2> color_tx;
SwapChain<GPUTexture **, 2> reveal_tx;
bool is_viewport;
VfxContext(PassMain::Sub &object_subpass_,
Framebuffer &layer_fb,
Framebuffer &object_fb,
TextureFromPool &object_color_tx,
TextureFromPool &layer_color_tx,
TextureFromPool &object_reveal_tx,
TextureFromPool &layer_reveal_tx,
bool is_render_)
{
object_subpass = &object_subpass_;
/* These may not be allocated yet, use address of future pointer. */
vfx_fb.current() = &layer_fb;
vfx_fb.next() = &object_fb;
color_tx.current() = &object_color_tx;
color_tx.next() = &layer_color_tx;
reveal_tx.current() = &object_reveal_tx;
reveal_tx.next() = &layer_reveal_tx;
is_viewport = (is_render_ == false);
}
PassMain::Sub &create_vfx_pass(const char *name, GPUShader *shader)
{
PassMain::Sub &sub = object_subpass->sub(name);
sub.framebuffer_set(vfx_fb.current());
sub.shader_set(shader);
sub.bind_texture("colorBuf", color_tx.current());
sub.bind_texture("revealBuf", reveal_tx.current());
vfx_fb.swap();
color_tx.swap();
reveal_tx.swap();
return sub;
}
/* Verify if the given fx is active. */
bool effect_is_active(const bGPdata *gpd, const ShaderFxData *fx)
{
if (fx == NULL) {
return false;
}
if (gpd == NULL) {
return false;
}
bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd);
if (((fx->mode & eShaderFxMode_Editmode) == 0) && (is_edit) && (is_viewport)) {
return false;
}
if (((fx->mode & eShaderFxMode_Realtime) && (is_viewport == true)) ||
((fx->mode & eShaderFxMode_Render) && (is_viewport == false)))
{
return true;
}
return false;
}
};
class VfxModule {
private:
ShaderModule &shaders;
/* Global switch for all vfx. */
bool vfx_enabled_ = false;
/* Global switch for all Depth Of Field blur. */
bool dof_enabled_ = false;
/* Pseudo depth of field parameter. Used to scale blur radius. */
float dof_parameters_[2];
public:
VfxModule(ShaderModule &shaders_) : shaders(shaders_){};
void init(bool enable, const Object *camera_object, const RegionView3D *rv3d)
{
vfx_enabled_ = enable;
const Camera *camera = (camera_object != nullptr) ?
static_cast<const Camera *>(camera_object->data) :
nullptr;
/* Pseudo DOF setup. */
if (camera && (camera->dof.flag & CAM_DOF_ENABLED)) {
const float *vp_size = DRW_viewport_size_get();
float fstop = camera->dof.aperture_fstop;
float sensor = BKE_camera_sensor_size(
camera->sensor_fit, camera->sensor_x, camera->sensor_y);
float focus_dist = BKE_camera_object_dof_distance(camera_object);
float focal_len = camera->lens;
const float scale_camera = 0.001f;
/* We want radius here for the aperture number. */
float aperture = 0.5f * scale_camera * focal_len / fstop;
float focal_len_scaled = scale_camera * focal_len;
float sensor_scaled = scale_camera * sensor;
if (rv3d != nullptr) {
sensor_scaled *= rv3d->viewcamtexcofac[0];
}
dof_parameters_[1] = aperture * fabsf(focal_len_scaled / (focus_dist - focal_len_scaled));
dof_parameters_[1] *= vp_size[0] / sensor_scaled;
dof_parameters_[0] = -focus_dist * dof_parameters_[1];
}
else {
/* Disable DoF blur scaling. Produce Circle of Confusion of 0 pixel. */
dof_parameters_[0] = dof_parameters_[1] = 0.0f;
}
}
/* Return true if any vfx is needed */
bool object_sync(Framebuffer &main_fb,
ObjectRef &object_ref,
VfxContext &vfx_ctx,
bool do_material_holdout)
{
Object *object = object_ref.object;
bGPdata *gpd = (bGPdata *)object->data;
int vfx_count = 0;
if (vfx_enabled_) {
for (const ShaderFxData *fx : ListBaseWrapper<const ShaderFxData>(&object->shader_fx)) {
if (!vfx_ctx.effect_is_active(gpd, fx)) {
continue;
}
switch (fx->type) {
case eShaderFxType_Blur:
vfx_count += vfx_blur(*(const BlurShaderFxData *)fx, object, vfx_ctx);
break;
case eShaderFxType_Colorize:
vfx_count += vfx_colorize(*(const ColorizeShaderFxData *)fx, object, vfx_ctx);
break;
case eShaderFxType_Flip:
vfx_count += vfx_flip(*(const FlipShaderFxData *)fx, object, vfx_ctx);
break;
case eShaderFxType_Pixel:
vfx_count += vfx_pixelize(*(const PixelShaderFxData *)fx, object, vfx_ctx);
break;
case eShaderFxType_Rim:
vfx_count += vfx_rim(*(const RimShaderFxData *)fx, object, vfx_ctx);
break;
case eShaderFxType_Shadow:
vfx_count += vfx_shadow(*(const ShadowShaderFxData *)fx, object, vfx_ctx);
break;
case eShaderFxType_Glow:
vfx_count += vfx_glow(*(const GlowShaderFxData *)fx, object, vfx_ctx);
break;
case eShaderFxType_Swirl:
vfx_count += vfx_swirl(*(const SwirlShaderFxData *)fx, object, vfx_ctx);
break;
case eShaderFxType_Wave:
vfx_count += vfx_wave(*(const WaveShaderFxData *)fx, object, vfx_ctx);
break;
default:
break;
}
}
}
if (do_material_holdout) {
vfx_count += 1;
}
if (vfx_count > 0) {
/* We need an extra pass to combine result to main buffer. */
merge_sync(main_fb, vfx_ctx);
}
return vfx_count > 0;
}
private:
int vfx_blur(const BlurShaderFxData &fx, const Object *object, VfxContext &vfx_ctx)
{
if ((fx.flag & FX_BLUR_DOF_MODE) && !dof_enabled_) {
/* No blur outside camera view (or when DOF is disabled on the camera). */
return 0;
}
float winmat[4][4], persmat[4][4];
float2 blur_size = {fx.radius[0], fx.radius[1]};
/* TODO(fclem): Replace by draw::View. */
DRW_view_persmat_get(nullptr, persmat, false);
const float w = fabsf(mul_project_m4_v3_zfac(persmat, object->object_to_world().location()));
if (fx.flag & FX_BLUR_DOF_MODE) {
/* Compute circle of confusion size. */
float coc = (dof_parameters_[0] / -w) - dof_parameters_[1];
blur_size = float2(fabsf(coc));
}
else {
/* Modify by distance to camera and object scale. */
/* TODO(fclem): Replace by draw::View. */
DRW_view_winmat_get(nullptr, winmat, false);
/* TODO(fclem): Replace by this->render_size. */
const float *vp_size = DRW_viewport_size_get();
float world_pixel_scale = 1.0f / GPENCIL_PIXEL_FACTOR;
float scale = mat4_to_scale(object->object_to_world().ptr());
float distance_factor = world_pixel_scale * scale * winmat[1][1] * vp_size[1] / w;
blur_size *= distance_factor;
}
if ((fx.samples == 0.0f) || (blur_size[0] == 0.0f && blur_size[1] == 0.0f)) {
return 0;
}
GPUShader *sh = shaders.static_shader_get(eShaderType::FX_BLUR);
const float rot_sin = sin(fx.rotation);
const float rot_cos = cos(fx.rotation);
if (blur_size[0] > 0.0f) {
PassMain::Sub &sub = vfx_ctx.create_vfx_pass("Fx Blur H", sh);
sub.state_set(DRW_STATE_WRITE_COLOR);
sub.push_constant("offset", float2(blur_size[0] * rot_cos, blur_size[0] * rot_sin));
sub.push_constant("sampCount", max_ii(1, min_ii(fx.samples, blur_size[0])));
sub.draw_procedural(GPU_PRIM_TRIS, 1, 3);
}
if (blur_size[1] > 0.0f) {
PassMain::Sub &sub = vfx_ctx.create_vfx_pass("Fx Blur V", sh);
sub.state_set(DRW_STATE_WRITE_COLOR);
sub.push_constant("offset", float2(-blur_size[1] * rot_sin, blur_size[1] * rot_cos));
sub.push_constant("sampCount", max_ii(1, min_ii(fx.samples, blur_size[1])));
sub.draw_procedural(GPU_PRIM_TRIS, 1, 3);
}
/* Return number of passes. */
return int(blur_size[0] > 0.0f) + int(blur_size[1] > 0.0f);
}
int vfx_colorize(const ColorizeShaderFxData &fx, const Object *object, VfxContext &vfx_ctx)
{
UNUSED_VARS(fx, object, vfx_ctx);
return 0;
}
int vfx_flip(const FlipShaderFxData &fx, const Object *object, VfxContext &vfx_ctx)
{
UNUSED_VARS(fx, object, vfx_ctx);
return 0;
}
int vfx_pixelize(const PixelShaderFxData &fx, const Object *object, VfxContext &vfx_ctx)
{
UNUSED_VARS(fx, object, vfx_ctx);
return 0;
}
int vfx_rim(const RimShaderFxData &fx, const Object *object, VfxContext &vfx_ctx)
{
UNUSED_VARS(fx, object, vfx_ctx);
return 0;
}
int vfx_shadow(const ShadowShaderFxData &fx, const Object *object, VfxContext &vfx_ctx)
{
UNUSED_VARS(fx, object, vfx_ctx);
return 0;
}
int vfx_glow(const GlowShaderFxData &fx, const Object *object, VfxContext &vfx_ctx)
{
UNUSED_VARS(fx, object, vfx_ctx);
return 0;
}
int vfx_swirl(const SwirlShaderFxData &fx, const Object *object, VfxContext &vfx_ctx)
{
UNUSED_VARS(fx, object, vfx_ctx);
return 0;
}
int vfx_wave(const WaveShaderFxData &fx, const Object *object, VfxContext &vfx_ctx)
{
UNUSED_VARS(fx, object, vfx_ctx);
return 0;
}
void merge_sync(Framebuffer &main_fb, VfxContext &vfx_ctx)
{
PassMain::Sub &sub = vfx_ctx.object_subpass->sub("GPencil Object Composite");
sub.framebuffer_set(&main_fb);
sub.shader_set(shaders.static_shader_get(FX_COMPOSITE));
sub.bind_texture("colorBuf", vfx_ctx.color_tx.current());
sub.bind_texture("revealBuf", vfx_ctx.reveal_tx.current());
sub.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_MUL);
sub.push_constant("isFirstPass", true);
sub.draw_procedural(GPU_PRIM_TRIS, 1, 3);
/* We cannot do custom blending on multi-target frame-buffers.
* Workaround by doing 2 passes. */
sub.state_set(DRW_STATE_WRITE_COLOR, DRW_STATE_BLEND_ADD_FULL);
sub.push_constant("isFirstPass", false);
sub.draw_procedural(GPU_PRIM_TRIS, 1, 3);
}
};
} // namespace blender::draw::greasepencil

View File

@@ -1,9 +0,0 @@
/* SPDX-FileCopyrightText: 2020-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(draw_model_lib.glsl)
void main()
{
gl_Position = drw_point_object_to_homogenous(pos);
}

View File

@@ -1,153 +0,0 @@
/* SPDX-FileCopyrightText: 2020-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(common_grease_pencil_lib.glsl)
#pragma BLENDER_REQUIRE(common_colormanagement_lib.glsl)
float length_squared(vec2 v)
{
return dot(v, v);
}
float length_squared(vec3 v)
{
return dot(v, v);
}
vec3 gpencil_lighting(void)
{
vec3 light_accum = vec3(0.0);
for (int i = 0; i < GPENCIL_LIGHT_BUFFER_LEN; i++) {
if (gp_lights[i]._color.x == -1.0) {
break;
}
vec3 L = gp_lights[i]._position - gp_interp.pos;
float vis = 1.0;
gpLightType type = floatBitsToUint(gp_lights[i]._type);
/* Spot Attenuation. */
if (type == GP_LIGHT_TYPE_SPOT) {
mat3 rot_scale = mat3(gp_lights[i]._right, gp_lights[i]._up, gp_lights[i]._forward);
vec3 local_L = rot_scale * L;
local_L /= abs(local_L.z);
float ellipse = inversesqrt(length_squared(local_L));
vis *= smoothstep(0.0, 1.0, (ellipse - gp_lights[i]._spot_size) / gp_lights[i]._spot_blend);
/* Also mask +Z cone. */
vis *= step(0.0, local_L.z);
}
/* Inverse square decay. Skip for suns. */
float L_len_sqr = length_squared(L);
if (type < GP_LIGHT_TYPE_SUN) {
vis /= L_len_sqr;
}
else {
L = gp_lights[i]._forward;
L_len_sqr = 1.0;
}
/* Lambertian falloff */
if (type != GP_LIGHT_TYPE_AMBIENT) {
L /= sqrt(L_len_sqr);
vis *= clamp(dot(gpNormal, L), 0.0, 1.0);
}
light_accum += vis * gp_lights[i]._color;
}
/* Clamp to avoid NaNs. */
return clamp(light_accum, 0.0, 1e10);
}
/* TODO: Remove this once we can render textures. */
vec4 debug_texture(vec2 uv)
{
vec4 col = vec4(mod(uv.xy, 1.0), 0.0, 1.0);
return col * min(length(uv.xy * 2.0 - 1.0), 1.0);
}
void main()
{
vec4 col;
if (flag_test(gp_interp_flat.mat_flag, GP_STROKE_TEXTURE_USE)) {
bool premul = flag_test(gp_interp_flat.mat_flag, GP_STROKE_TEXTURE_PREMUL);
col = texture_read_as_linearrgb(gpStrokeTexture, premul, gp_interp.uv);
/* TODO: Remove this once we can render textures. */
/* Debug color. (Because textures are not yet implemented) */
col = debug_texture(gp_interp.uv);
}
else if (flag_test(gp_interp_flat.mat_flag, GP_FILL_TEXTURE_USE)) {
bool use_clip = flag_test(gp_interp_flat.mat_flag, GP_FILL_TEXTURE_CLIP);
vec2 uvs = (use_clip) ? clamp(gp_interp.uv, 0.0, 1.0) : gp_interp.uv;
bool premul = flag_test(gp_interp_flat.mat_flag, GP_FILL_TEXTURE_PREMUL);
col = texture_read_as_linearrgb(gpFillTexture, premul, uvs);
}
else if (flag_test(gp_interp_flat.mat_flag, GP_FILL_GRADIENT_USE)) {
bool radial = flag_test(gp_interp_flat.mat_flag, GP_FILL_GRADIENT_RADIAL);
float fac = clamp(radial ? length(gp_interp.uv * 2.0 - 1.0) : gp_interp.uv.x, 0.0, 1.0);
uint matid = gp_interp_flat.mat_flag >> GPENCIl_MATID_SHIFT;
col = mix(gp_materials[matid].fill_color, gp_materials[matid].fill_mix_color, fac);
}
else /* SOLID */ {
col = vec4(1.0);
}
col.rgb *= col.a;
/* Composite all other colors on top of texture color.
* Everything is pre-multiply by `col.a` to have the stencil effect. */
fragColor = col * gp_interp.color_mul + col.a * gp_interp.color_add;
fragColor.rgb *= gpencil_lighting();
fragColor *= gpencil_stroke_round_cap_mask(gp_interp_flat.sspos.xy,
gp_interp_flat.sspos.zw,
gp_interp_flat.aspect,
gp_interp_noperspective.thickness.x,
gp_interp_noperspective.hardness);
/* To avoid aliasing artifacts, we reduce the opacity of small strokes. */
fragColor *= smoothstep(0.0, 1.0, gp_interp_noperspective.thickness.y);
/* Holdout materials. */
if (flag_test(gp_interp_flat.mat_flag, GP_STROKE_HOLDOUT | GP_FILL_HOLDOUT)) {
revealColor = fragColor.aaaa;
}
else {
/* NOT holdout materials.
* For compatibility with colored alpha buffer.
* Note that we are limited to mono-chromatic alpha blending here
* because of the blend equation and the limit of 1 color target
* when using custom color blending. */
revealColor = vec4(0.0, 0.0, 0.0, fragColor.a);
if (fragColor.a < 0.001) {
discard;
return;
}
}
vec2 fb_size = max(vec2(textureSize(gpSceneDepthTexture, 0).xy),
vec2(textureSize(gpMaskTexture, 0).xy));
vec2 uvs = gl_FragCoord.xy / fb_size;
/* Manual depth test */
float scene_depth = texture(gpSceneDepthTexture, uvs).r;
if (gl_FragCoord.z > scene_depth) {
discard;
return;
}
/* FIXME(@fclem): Alas. This is bad for performance but it's the easiest way to not get
* depth written where the mask obliterate the layer. */
float mask = texture(gpMaskTexture, uvs).r;
if (mask < 0.001) {
discard;
return;
}
/* We override the fragment depth using the fragment shader to ensure a constant value.
* This has a cost as the depth test cannot happen early.
* We could do this in the vertex shader but then perspective interpolation of uvs and
* fragment clipping gets really complicated. */
if (gp_interp_flat.depth >= 0.0) {
gl_FragDepth = gp_interp_flat.depth;
}
else {
gl_FragDepth = gl_FragCoord.z;
}
}

View File

@@ -1,139 +0,0 @@
/* SPDX-FileCopyrightText: 2020-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(common_grease_pencil_lib.glsl)
void gpencil_color_output(vec4 stroke_col, vec4 vert_col, float vert_strength, float mix_tex)
{
/* Mix stroke with other colors. */
vec4 mixed_col = stroke_col;
mixed_col.rgb = mix(mixed_col.rgb, vert_col.rgb, vert_col.a);
mixed_col.rgb = mix(mixed_col.rgb, gpLayerTint.rgb, gpLayerTint.a);
mixed_col.a *= vert_strength * gpLayerOpacity;
/**
* This is what the fragment shader looks like.
* out = col * gp_interp.color_mul + col.a * gp_interp.color_add.
* gp_interp.color_mul is how much of the texture color to keep.
* gp_interp.color_add is how much of the mixed color to add.
* Note that we never add alpha. This is to keep the texture act as a stencil.
* We do however, modulate the alpha (reduce it).
*/
/* We add the mixed color. This is 100% mix (no texture visible). */
gp_interp.color_mul = vec4(mixed_col.aaa, mixed_col.a);
gp_interp.color_add = vec4(mixed_col.rgb * mixed_col.a, 0.0);
/* Then we blend according to the texture mix factor.
* Note that we keep the alpha modulation. */
gp_interp.color_mul.rgb *= mix_tex;
gp_interp.color_add.rgb *= 1.0 - mix_tex;
}
void main()
{
PASS_RESOURCE_ID
float vert_strength;
vec4 vert_color;
vec3 vert_N;
ivec4 ma1 = floatBitsToInt(texelFetch(gp_pos_tx, gpencil_stroke_point_id() * 3 + 1));
gpMaterial gp_mat = gp_materials[ma1.x + gpMaterialOffset];
gpMaterialFlag gp_flag = floatBitsToUint(gp_mat._flag);
gl_Position = gpencil_vertex(vec4(viewportSize, 1.0 / viewportSize),
gp_flag,
gp_mat._alignment_rot,
gp_interp.pos,
vert_N,
vert_color,
vert_strength,
gp_interp.uv,
gp_interp_flat.sspos,
gp_interp_flat.aspect,
gp_interp_noperspective.thickness,
gp_interp_noperspective.hardness);
if (gpencil_is_stroke_vertex()) {
if (!flag_test(gp_flag, GP_STROKE_ALIGNMENT)) {
gp_interp.uv.x *= gp_mat._stroke_u_scale;
}
/* Special case: We don't use vertex color if material Holdout. */
if (flag_test(gp_flag, GP_STROKE_HOLDOUT)) {
vert_color = vec4(0.0);
}
gpencil_color_output(
gp_mat.stroke_color, vert_color, vert_strength, gp_mat._stroke_texture_mix);
gp_interp_flat.mat_flag = gp_flag & ~GP_FILL_FLAGS;
if (gpStrokeOrder3d) {
/* Use the fragment depth (see fragment shader). */
gp_interp_flat.depth = -1.0;
}
else if (flag_test(gp_flag, GP_STROKE_OVERLAP)) {
/* Use the index of the point as depth.
* This means the stroke can overlap itself. */
float point_index = float(ma1.z);
gp_interp_flat.depth = (point_index + gpStrokeIndexOffset + 2.0) * 0.0000002;
}
else {
/* Use the index of first point of the stroke as depth.
* We render using a greater depth test this means the stroke
* cannot overlap itself.
* We offset by one so that the fill can be overlapped by its stroke.
* The offset is ok since we pad the strokes data because of adjacency infos. */
float stroke_index = float(ma1.y);
gp_interp_flat.depth = (stroke_index + gpStrokeIndexOffset + 2.0) * 0.0000002;
}
}
else {
int stroke_point_id = gpencil_stroke_point_id();
vec4 uv1 = texelFetch(gp_pos_tx, stroke_point_id * 3 + 2);
vec4 fcol1 = texelFetch(gp_col_tx, stroke_point_id * 2 + 1);
vec4 fill_col = gp_mat.fill_color;
/* Special case: We don't modulate alpha in gradient mode. */
if (flag_test(gp_flag, GP_FILL_GRADIENT_USE)) {
fill_col.a = 1.0;
}
/* Decode fill opacity. */
vec4 fcol_decode = vec4(fcol1.rgb, floor(fcol1.a / 10.0));
float fill_opacity = fcol1.a - (fcol_decode.a * 10);
fcol_decode.a /= 10000.0;
/* Special case: We don't use vertex color if material Holdout. */
if (flag_test(gp_flag, GP_FILL_HOLDOUT)) {
fcol_decode = vec4(0.0);
}
/* Apply opacity. */
fill_col.a *= fill_opacity;
/* If factor is > 1 force opacity. */
if (fill_opacity > 1.0) {
fill_col.a += fill_opacity - 1.0;
}
fill_col.a = clamp(fill_col.a, 0.0, 1.0);
gpencil_color_output(fill_col, fcol_decode, 1.0, gp_mat._fill_texture_mix);
gp_interp_flat.mat_flag = gp_flag & GP_FILL_FLAGS;
gp_interp_flat.mat_flag |= uint(ma1.x + gpMaterialOffset) << GPENCIl_MATID_SHIFT;
gp_interp.uv = mat2(gp_mat.fill_uv_rot_scale.xy, gp_mat.fill_uv_rot_scale.zw) * uv1.xy +
gp_mat._fill_uv_offset;
if (gpStrokeOrder3d) {
/* Use the fragment depth (see fragment shader). */
gp_interp_flat.depth = -1.0;
}
else {
/* Use the index of first point of the stroke as depth. */
float stroke_index = float(ma1.y);
gp_interp_flat.depth = (stroke_index + gpStrokeIndexOffset + 1.0) * 0.0000002;
}
}
}

View File

@@ -54,41 +54,6 @@ GPU_SHADER_CREATE_INFO(gpencil_geometry)
.depth_write(DepthWrite::ANY)
.additional_info("draw_gpencil");
GPU_SHADER_CREATE_INFO(gpencil_geometry_next)
.do_static_compilation(true)
.define("GP_LIGHT")
.typedef_source("gpencil_defines.h")
.sampler(GPENCIL_SCENE_DEPTH_TEX_SLOT, ImageType::DEPTH_2D, "gpSceneDepthTexture")
.sampler(GPENCIL_MASK_TEX_SLOT, ImageType::FLOAT_2D, "gpMaskTexture")
.sampler(GPENCIL_FILL_TEX_SLOT, ImageType::FLOAT_2D, "gpFillTexture")
.sampler(GPENCIL_STROKE_TEX_SLOT, ImageType::FLOAT_2D, "gpStrokeTexture")
.storage_buf(GPENCIL_OBJECT_SLOT, Qualifier::READ, "gpObject", "gp_object[]")
.storage_buf(GPENCIL_LAYER_SLOT, Qualifier::READ, "gpLayer", "gp_layer[]")
.storage_buf(GPENCIL_MATERIAL_SLOT, Qualifier::READ, "gpMaterial", "gp_materials[]")
.storage_buf(GPENCIL_LIGHT_SLOT, Qualifier::READ, "gpLight", "gp_lights[]")
.uniform_buf(GPENCIL_SCENE_SLOT, "gpScene", "gp_scene")
/* Per Scene */
.define("viewportSize", "gp_scene.render_size")
/* Per Object */
.define("gpNormal", "gp_object[resource_id].normal")
.define("gpStrokeOrder3d", "gp_object[resource_id].stroke_order3d")
.define("gpMaterialOffset", "gp_object[resource_id].material_offset")
/* Per Layer */
.define("layer_id", "gp_object[resource_id].layer_offset") /* TODO */
.define("gpVertexColorOpacity", "gp_layer[layer_id].vertex_color_opacity")
.define("gpLayerTint", "gp_layer[layer_id].tint")
.define("gpLayerOpacity", "gp_layer[layer_id].opacity")
.define("gpStrokeIndexOffset", "gp_layer[layer_id].stroke_index_offset")
.fragment_out(0, Type::VEC4, "fragColor")
.fragment_out(1, Type::VEC4, "revealColor")
.vertex_out(gpencil_geometry_iface)
.vertex_out(gpencil_geometry_flat_iface)
.vertex_out(gpencil_geometry_noperspective_iface)
.vertex_source("grease_pencil_vert.glsl")
.fragment_source("grease_pencil_frag.glsl")
.additional_info("draw_gpencil_new")
.depth_write(DepthWrite::ANY);
/** \} */
/* -------------------------------------------------------------------- */
@@ -126,16 +91,6 @@ GPU_SHADER_CREATE_INFO(gpencil_depth_merge)
.depth_write(DepthWrite::ANY)
.additional_info("draw_view");
GPU_SHADER_CREATE_INFO(grease_pencil_depth_merge)
.do_static_compilation(true)
.define("strokeOrder3d", "false")
.sampler(0, ImageType::DEPTH_2D, "depthBuf")
.vertex_in(0, Type::VEC3, "pos")
.vertex_source("grease_pencil_depth_merge_vert.glsl")
.fragment_source("gpencil_depth_merge_frag.glsl")
.depth_write(DepthWrite::ANY)
.additional_info("draw_modelmat_new", "draw_view");
/** \} */
/* -------------------------------------------------------------------- */

View File

@@ -444,9 +444,7 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob)
OVERLAY_edit_curves_cache_populate(data, ob);
break;
case OB_GREASE_PENCIL:
if (U.experimental.use_grease_pencil_version3) {
OVERLAY_edit_grease_pencil_cache_populate(data, ob);
}
OVERLAY_edit_grease_pencil_cache_populate(data, ob);
break;
}
}

View File

@@ -10,13 +10,17 @@
#include "DRW_render.hh"
#include "BKE_curves.hh"
#include "BKE_global.hh"
#include "BKE_gpencil_legacy.h"
#include "BKE_grease_pencil.hh"
#include "BKE_object.hh"
#include "DNA_gpencil_legacy_types.h"
#include "ED_grease_pencil.hh"
#include "UI_resources.hh"
#include "overlay_private.hh"
@@ -273,6 +277,92 @@ static void OVERLAY_outline_gpencil(OVERLAY_PrivateData *pd, Object *ob)
pd->cfra);
}
static void OVERLAY_outline_grease_pencil(OVERLAY_PrivateData *pd, Scene *scene, Object *ob)
{
using namespace blender;
using namespace blender::ed::greasepencil;
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
/* Outlines only in object mode. */
if (ob->mode != OB_MODE_OBJECT) {
return;
}
float plane[4] = {0.0f, 0.0f, 0.0f, 0.0f};
if ((grease_pencil.flag & GREASE_PENCIL_STROKE_ORDER_3D) == 0) {
gpencil_depth_plane(ob, plane);
}
int t_offset = 0;
const Vector<DrawingInfo> drawings = retrieve_visible_drawings(*scene, grease_pencil);
for (const DrawingInfo info : drawings) {
const bool is_screenspace = false;
const bool is_stroke_order_3d = (grease_pencil.flag & GREASE_PENCIL_STROKE_ORDER_3D) != 0;
float object_scale = mat4_to_scale(ob->object_to_world().ptr());
/* Negate thickness sign to tag that strokes are in screen space.
* Convert to world units (by default, 1 meter = 1000 pixels). */
float thickness_scale = (is_screenspace) ? -1.0f : 1.0f / 1000.0f;
GPUVertBuf *position_tx = draw::DRW_cache_grease_pencil_position_buffer_get(scene, ob);
GPUVertBuf *color_tx = draw::DRW_cache_grease_pencil_color_buffer_get(scene, ob);
DRWShadingGroup *grp = DRW_shgroup_create_sub(pd->outlines_gpencil_grp);
DRW_shgroup_uniform_bool_copy(grp, "gpStrokeOrder3d", is_stroke_order_3d);
DRW_shgroup_uniform_float_copy(grp, "gpThicknessScale", object_scale);
DRW_shgroup_uniform_float_copy(grp, "gpThicknessOffset", 0.0f);
DRW_shgroup_uniform_float_copy(grp, "gpThicknessWorldScale", thickness_scale);
DRW_shgroup_uniform_vec4_copy(grp, "gpDepthPlane", plane);
DRW_shgroup_buffer_texture(grp, "gp_pos_tx", position_tx);
DRW_shgroup_buffer_texture(grp, "gp_col_tx", color_tx);
const bke::CurvesGeometry &curves = info.drawing.strokes();
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
const bke::AttributeAccessor attributes = curves.attributes();
const VArray<int> stroke_materials = *attributes.lookup_or_default<int>(
"material_index", bke::AttrDomain::Curve, 0);
const VArray<bool> cyclic = *attributes.lookup_or_default<bool>(
"cyclic", bke::AttrDomain::Curve, false);
IndexMaskMemory memory;
const IndexMask visible_strokes = ed::greasepencil::retrieve_visible_strokes(
*ob, info.drawing, memory);
visible_strokes.foreach_index([&](const int stroke_i) {
const IndexRange points = points_by_curve[stroke_i];
const int material_index = stroke_materials[stroke_i];
MaterialGPencilStyle *gp_style = BKE_object_material_get(ob, material_index + 1)->gp_style;
const bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0;
if (hide_material) {
return;
}
GPUBatch *geom = draw::DRW_cache_grease_pencil_get(scene, ob);
const bool show_stroke = (gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0;
const bool show_fill = (points.size() >= 3) && (gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0;
const bool is_cyclic = cyclic[stroke_i] && (points.size() > 2);
const int num_stroke_triangles = points.size() - 2;
const int num_stroke_vertices = (points.size() + int(is_cyclic));
if (show_fill) {
int vfirst = t_offset * 3;
int vcount = num_stroke_triangles * 3;
DRW_shgroup_call_range(grp, ob, geom, vfirst, vcount);
}
t_offset += num_stroke_triangles;
if (show_stroke) {
int vfirst = t_offset * 3;
int vcount = num_stroke_vertices * 2 * 3;
DRW_shgroup_call_range(grp, ob, geom, vfirst, vcount);
}
t_offset += num_stroke_vertices * 2;
});
}
}
static void OVERLAY_outline_volume(OVERLAY_PrivateData *pd, Object *ob)
{
using namespace blender::draw;
@@ -326,6 +416,11 @@ void OVERLAY_outline_cache_populate(OVERLAY_Data *vedata,
return;
}
if (ob->type == OB_GREASE_PENCIL) {
OVERLAY_outline_grease_pencil(pd, draw_ctx->scene, ob);
return;
}
if (ob->type == OB_VOLUME) {
OVERLAY_outline_volume(pd, ob);
return;

View File

@@ -558,6 +558,9 @@ static void grease_pencil_geom_batch_ensure(Object &object,
copy_v3_v3(s_vert.pos,
math::transform_point(layer_space_to_object_space, positions[point_i]));
s_vert.radius = radii[point_i] * ((end_cap == GP_STROKE_CAP_TYPE_ROUND) ? 1.0f : -1.0f);
/* Convert to legacy "pixel" space. The shader expects the values to be in this space.
* Otherwise the values will get clamped. */
s_vert.radius *= 1000.0f;
s_vert.opacity = opacities[point_i] *
((start_cap == GP_STROKE_CAP_TYPE_ROUND) ? 1.0f : -1.0f);
s_vert.point_id = verts_range[idx];
@@ -620,7 +623,7 @@ static void grease_pencil_geom_batch_ensure(Object &object,
cols_slice[idx]);
}
if (is_cyclic) {
if (is_cyclic && points.size() >= 3) {
const int idx = points.size() + 1;
const float length = points.size() > 1 ? lengths[points.size() - 1] : 0.0f;
populate_point(verts_range,

View File

@@ -1271,8 +1271,7 @@ static void drw_engines_enable(ViewLayer * /*view_layer*/,
drw_engines_enable_from_engine(engine_type, drawtype);
if (gpencil_engine_needed && ((drawtype >= OB_SOLID) || !use_xray)) {
use_drw_engine(U.experimental.use_grease_pencil_version3 ? &draw_engine_gpencil_next_type :
&draw_engine_gpencil_type);
use_drw_engine(&draw_engine_gpencil_type);
}
if (is_compositor_enabled()) {
@@ -1302,16 +1301,12 @@ static void drw_engines_data_validate()
* For slow exact check use `DRW_render_check_grease_pencil` */
static bool drw_gpencil_engine_needed(Depsgraph *depsgraph, View3D *v3d)
{
if (U.experimental.use_grease_pencil_version3) {
const bool exclude_gpencil_rendering = v3d ? (v3d->object_type_exclude_viewport &
(1 << OB_GREASE_PENCIL)) != 0 :
false;
return (!exclude_gpencil_rendering) && DEG_id_type_any_exists(depsgraph, ID_GP);
}
const bool exclude_gpencil_rendering = v3d ? (v3d->object_type_exclude_viewport &
(1 << OB_GPENCIL_LEGACY)) != 0 :
false;
return (!exclude_gpencil_rendering) && DEG_id_type_any_exists(depsgraph, ID_GD_LEGACY);
const bool exclude_gpencil_rendering =
v3d ? ((v3d->object_type_exclude_viewport & (1 << OB_GPENCIL_LEGACY)) != 0) ||
((v3d->object_type_exclude_viewport & (1 << OB_GREASE_PENCIL)) != 0) :
false;
return (!exclude_gpencil_rendering) && (DEG_id_type_any_exists(depsgraph, ID_GD_LEGACY) ||
DEG_id_type_any_exists(depsgraph, ID_GP));
}
/* -------------------------------------------------------------------- */
@@ -1870,9 +1865,7 @@ bool DRW_render_check_grease_pencil(Depsgraph *depsgraph)
deg_iter_settings.depsgraph = depsgraph;
deg_iter_settings.flags = DEG_OBJECT_ITER_FOR_RENDER_ENGINE_FLAGS;
DEG_OBJECT_ITER_BEGIN (&deg_iter_settings, ob) {
if (ob->type == OB_GPENCIL_LEGACY ||
(U.experimental.use_grease_pencil_version3 && ob->type == OB_GREASE_PENCIL))
{
if (ob->type == OB_GPENCIL_LEGACY || ob->type == OB_GREASE_PENCIL) {
if (DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF) {
return true;
}
@@ -1887,9 +1880,7 @@ static void DRW_render_gpencil_to_image(RenderEngine *engine,
RenderLayer *render_layer,
const rcti *rect)
{
DrawEngineType *draw_engine = U.experimental.use_grease_pencil_version3 ?
&draw_engine_gpencil_next_type :
&draw_engine_gpencil_type;
DrawEngineType *draw_engine = &draw_engine_gpencil_type;
if (draw_engine->render_to_image) {
ViewportEngineData *gpdata = DRW_view_data_engine_data_get_ensure(DST.view_data_active,
draw_engine);
@@ -2486,8 +2477,7 @@ void DRW_draw_select_loop(Depsgraph *depsgraph,
else if (!draw_surface) {
/* grease pencil selection */
if (drw_gpencil_engine_needed(depsgraph, v3d)) {
use_drw_engine(U.experimental.use_grease_pencil_version3 ? &draw_engine_gpencil_next_type :
&draw_engine_gpencil_type);
use_drw_engine(&draw_engine_gpencil_type);
}
drw_engines_enable_overlays();
@@ -2497,8 +2487,7 @@ void DRW_draw_select_loop(Depsgraph *depsgraph,
drw_engines_enable_basic();
/* grease pencil selection */
if (drw_gpencil_engine_needed(depsgraph, v3d)) {
use_drw_engine(U.experimental.use_grease_pencil_version3 ? &draw_engine_gpencil_next_type :
&draw_engine_gpencil_type);
use_drw_engine(&draw_engine_gpencil_type);
}
drw_engines_enable_overlays();
@@ -2671,8 +2660,7 @@ void DRW_draw_depth_loop(Depsgraph *depsgraph,
drw_manager_init(&DST, viewport, nullptr);
if (use_gpencil) {
use_drw_engine(U.experimental.use_grease_pencil_version3 ? &draw_engine_gpencil_next_type :
&draw_engine_gpencil_type);
use_drw_engine(&draw_engine_gpencil_type);
}
if (use_basic) {
drw_engines_enable_basic();
@@ -3062,7 +3050,6 @@ void DRW_engines_register()
RE_engines_register(&DRW_engine_viewport_workbench_type);
DRW_engine_register(&draw_engine_gpencil_type);
DRW_engine_register(&draw_engine_gpencil_next_type);
DRW_engine_register(&draw_engine_overlay_type);
DRW_engine_register(&draw_engine_overlay_next_type);