Initial Grease Pencil 3.0 stage #106848

Merged
Falk David merged 224 commits from filedescriptor/blender:grease-pencil-v3 into main 2023-05-30 11:14:22 +02:00
23 changed files with 1944 additions and 25 deletions
Showing only changes of commit a7707c8956 - Show all commits

View File

@ -2318,6 +2318,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel):
({"property": "use_full_frame_compositor"}, "T88150"),
({"property": "enable_eevee_next"}, "T93220"),
({"property": "use_draw_manager_acquire_lock"}, "T98016"),
({"property": "enable_gpencil_next"}, ""),
),
)

View File

@ -172,9 +172,11 @@ set(SRC
engines/gpencil/gpencil_cache_utils.c
engines/gpencil/gpencil_draw_data.c
engines/gpencil/gpencil_engine.c
engines/gpencil/gpencil_engine.cc
engines/gpencil/gpencil_engine.h
engines/gpencil/gpencil_render.c
engines/gpencil/gpencil_shader.c
engines/gpencil/gpencil_shader.cc
engines/gpencil/gpencil_shader_fx.c
engines/select/select_draw_utils.c
engines/select/select_engine.c
@ -772,4 +774,3 @@ if(WITH_GTESTS)
blender_add_test_lib(bf_draw_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}")
endif()
endif()

View File

@ -0,0 +1,150 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2022 Blender Foundation. */
filedescriptor marked this conversation as resolved Outdated

2023

2023
/** \file
* \ingroup draw
*/
#pragma once
#include "BKE_gpencil.h"
#include "BKE_image.h"
#include "DRW_gpu_wrapper.hh"
#include "DRW_render.h"
#include "draw_manager.hh"
#include "draw_pass.hh"
#include "gpencil_shader.hh"
#include "smaa_textures.h"
namespace blender::gpencil {

It looks like this should be in the blender::draw:: namespace first, given the file path. Since greasepencil isn't a top-level module, a blender::greasepencil namespace probably doesn't make sense in general.

Same with elsewhere in this diff.

It looks like this should be in the `blender::draw::` namespace first, given the file path. Since `greasepencil` isn't a top-level module, a `blender::greasepencil` namespace probably doesn't make sense in general. Same with elsewhere in this diff.
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, int2(SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT)};
Texture smaa_area_tx_ = {"smaa_area", GPU_RG8, 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"};
Framebuffer output_fb_ = {"output_fb"};
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_ = GPENCIL_SIMPLIFY_AA(scene);
}
void begin_sync(TextureFromPool &color_tx, 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]};
if (anti_aliasing_enabled_) {
/* Stage 1: Edge detection. */
PassSimple &pass = edge_detect_ps_;
pass.init();
pass.framebuffer_set(&edge_detect_fb_);

I would totally split the code into smaller functions. I do not see what are we winning from such inlined code.

I would totally split the code into smaller functions. I do not see what are we winning from such inlined code.
pass.state_set(DRW_STATE_WRITE_COLOR);
pass.shader_set(shaders_.static_shader_get(ANTIALIASING_EDGE_DETECT));
pass.bind_texture("colorTex", &color_tx);
pass.bind_texture("revealTex", &reveal_tx);
pass.push_constant("viewportMetrics", metrics);
pass.push_constant("lumaWeight", luma_weight_);
pass.clear_color(float4(0.0f));
pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
}
if (anti_aliasing_enabled_) {
/* Stage 2: Blend Weight/Coord. */
PassSimple &pass = blend_weight_ps_;
pass.init();
pass.framebuffer_set(&blend_weight_fb_);
pass.state_set(DRW_STATE_WRITE_COLOR);
pass.shader_set(shaders_.static_shader_get(ANTIALIASING_BLEND_WEIGHT));
pass.bind_texture("edgesTex", &edge_detect_tx_);
pass.bind_texture("areaTex", smaa_area_tx_);
pass.bind_texture("searchTex", smaa_search_tx_);
pass.push_constant("viewportMetrics", metrics);
pass.clear_color(float4(0.0f));
pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
}
{
/* Stage 3: Resolve. */
PassSimple &pass = resolve_ps_;
pass.init();
pass.framebuffer_set(&output_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 diabled. */
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, GPUTexture *dst_color_tx)
{
int2 render_size = {GPU_texture_width(dst_color_tx), GPU_texture_height(dst_color_tx)};
DRW_stats_group_start("Anti-Aliasing");
if (anti_aliasing_enabled_) {
edge_detect_tx_.acquire(render_size, GPU_RG8);
edge_detect_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(edge_detect_tx_));
manager.submit(edge_detect_ps_);
}
if (anti_aliasing_enabled_) {
blend_weight_tx_.acquire(render_size, GPU_RGBA8);
blend_weight_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(blend_weight_tx_));
manager.submit(blend_weight_ps_);
edge_detect_tx_.release();

Why this extra check is needed?

Why this extra check is needed?
}
output_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(dst_color_tx));
manager.submit(resolve_ps_);
blend_weight_tx_.release();
DRW_stats_group_end();
}
};
} // namespace blender::gpencil

View File

@ -6,3 +6,16 @@
/* High bits are used to pass material ID to fragment shader. */
#define GPENCIl_MATID_SHIFT 16u
/* Textures */
#define GPENCIL_SCENE_DEPTH_TEX_SLOT 2
#define GPENCIL_MASK_TEX_SLOT 3
#define GPENCIL_FILL_TEX_SLOT 4
#define GPENCIL_STROKE_TEX_SLOT 5
/* SSBOs */
#define GPENCIL_OBJECT_SLOT 0
#define GPENCIL_LAYER_SLOT 1
#define GPENCIL_MATERIAL_SLOT 2
#define GPENCIL_LIGHT_SLOT 3
/* UBOs */
#define GPENCIL_SCENE_SLOT 2

View File

@ -396,7 +396,7 @@ static void gpencil_sbuffer_cache_populate(gpIterPopulateData *iter)
* Remember, sbuffer stroke indices start from 0. So we add last index to avoid
* masking issues. */
iter->grp = DRW_shgroup_create_sub(iter->grp);
DRW_shgroup_uniform_block(iter->grp, "materials", iter->ubo_mat);
DRW_shgroup_uniform_block(iter->grp, "gp_materials", iter->ubo_mat);
DRW_shgroup_uniform_float_copy(iter->grp, "gpStrokeIndexOffset", iter->stroke_index_last);
const DRWContextState *ctx = DRW_context_state_get();
@ -444,8 +444,8 @@ static void gpencil_layer_cache_populate(bGPDlayer *gpl,
/* Iterator dependent uniforms. */
DRWShadingGroup *grp = iter->grp = tgp_layer->base_shgrp;
DRW_shgroup_uniform_block(grp, "lights", iter->ubo_lights);
DRW_shgroup_uniform_block(grp, "materials", iter->ubo_mat);
DRW_shgroup_uniform_block(grp, "gp_lights", iter->ubo_lights);
DRW_shgroup_uniform_block(grp, "gp_materials", iter->ubo_mat);
DRW_shgroup_uniform_texture(grp, "gpFillTexture", iter->tex_fill);
DRW_shgroup_uniform_texture(grp, "gpStrokeTexture", iter->tex_stroke);
DRW_shgroup_uniform_int_copy(grp, "gpMaterialOffset", iter->mat_ofs);
@ -492,7 +492,7 @@ static void gpencil_stroke_cache_populate(bGPDlayer *gpl,
iter->grp = DRW_shgroup_create_sub(iter->grp);
if (iter->ubo_mat != ubo_mat) {
DRW_shgroup_uniform_block(iter->grp, "materials", ubo_mat);
DRW_shgroup_uniform_block(iter->grp, "gp_materials", ubo_mat);
iter->ubo_mat = ubo_mat;
}
if (tex_fill) {

View File

@ -0,0 +1,318 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2022 Blender Foundation. */
filedescriptor marked this conversation as resolved Outdated

2023

2023
/** \file
* \ingroup draw
*/
#include "BKE_gpencil.h"
#include "BKE_gpencil_modifier.h"
#include "BLI_listbase_wrapper.hh"
#include "DEG_depsgraph_query.h"
#include "DNA_shader_fx_types.h"
#include "DRW_engine.h"
#include "DRW_render.h"
#include "ED_screen.h"
#include "ED_view3d.h"
#include "GPU_capabilities.h"
#include "IMB_imbuf_types.h"
#include "draw_manager.hh"
#include "draw_pass.hh"
Review

What is this about?

What is this about?
#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::gpencil {
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"};
/** 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, int2(1), (float *)dummy_px_};
Texture dummy_color_tx_ = {"dummy_color", GPU_RGBA16F, 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;
/** \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, const View3D *v3d, const RegionView3D *rv3d)
{
depsgraph_ = depsgraph;
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(Manager & /* manager */)
{
/* TODO(fclem): Remove global draw manager access. */
View main_view("GPencil_MainView", DRW_view_default_get());
objects.begin_sync(depsgraph_, main_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_, reveal_tx_);
}
void object_sync(Manager &manager, ObjectRef &object_ref)
{
switch (object_ref.object->type) {
case OB_GPENCIL:
objects.sync_gpencil(manager, object_ref, main_fb_, main_ps_);
break;
case OB_LAMP:
lights.sync(object_ref);
break;
default:
break;
}
}
void end_sync(Manager & /* manager */)
{
objects.end_sync();
layers.end_sync();
materials.end_sync();
lights.end_sync();
}
void draw_viewport(Manager &manager,
View &view,
GPUTexture *dst_depth_tx,
GPUTexture *dst_color_tx)
{
if (!objects.scene_has_visible_gpencil_object()) {
return;
}
int2 render_size = {GPU_texture_width(dst_depth_tx), GPU_texture_height(dst_depth_tx)};
depth_tx_.acquire(render_size, GPU_DEPTH24_STENCIL8);
color_tx_.acquire(render_size, texture_format_);
reveal_tx_.acquire(render_size, 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_size);
scene_buf_.push_update();
objects.acquire_temporary_buffers(render_size, texture_format_);
manager.submit(main_ps_, view);
objects.release_temporary_buffers();
anti_aliasing.draw(manager, dst_color_tx);
depth_tx_.release();
color_tx_.release();
reveal_tx_.release();
}
};
} // namespace blender::gpencil
/* -------------------------------------------------------------------- */
/** \name Interface with legacy C DRW manager
* \{ */
using namespace blender;
struct GPENCIL_NEXT_Data {
DrawEngineType *engine_type;
DRWViewportEmptyList *fbl;
DRWViewportEmptyList *txl;
DRWViewportEmptyList *psl;
Review

This struct name is interesting... I guess it's temporary though :P

This struct name is interesting... I guess it's temporary though :P
Review

Yes it is :)

Yes it is :)
DRWViewportEmptyList *stl;
gpencil::Instance *instance;
char info[GPU_INFO_SIZE];
};
static void gpencil_engine_init(void *vedata)
{
/* TODO(fclem): Remove once it is minimum required. */
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
GPENCIL_NEXT_Data *ved = reinterpret_cast<GPENCIL_NEXT_Data *>(vedata);
if (ved->instance == nullptr) {
ved->instance = new gpencil::Instance();
}
const DRWContextState *ctx_state = DRW_context_state_get();
ved->instance->init(ctx_state->depsgraph, ctx_state->v3d, ctx_state->rv3d);
}
static void gpencil_draw_scene(void *vedata)
{
GPENCIL_NEXT_Data *ved = reinterpret_cast<GPENCIL_NEXT_Data *>(vedata);
if (!GPU_shader_storage_buffer_objects_support()) {
STRNCPY(ved->info, "Error: No shader storage buffer support");
return;
}
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
const DRWView *default_view = DRW_view_default_get();
draw::Manager *manager = DRW_manager_get();
draw::View view("DefaultView", default_view);
ved->instance->draw_viewport(*manager, view, dtxl->depth, dtxl->color);
}
static void gpencil_cache_init(void *vedata)
{
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
draw::Manager *manager = DRW_manager_get();
reinterpret_cast<GPENCIL_NEXT_Data *>(vedata)->instance->begin_sync(*manager);
}
static void gpencil_cache_populate(void *vedata, Object *object)
{
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
draw::Manager *manager = DRW_manager_get();
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(*manager, ref);
}
static void gpencil_cache_finish(void *vedata)
{
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
draw::Manager *manager = DRW_manager_get();
reinterpret_cast<GPENCIL_NEXT_Data *>(vedata)->instance->end_sync(*manager);
}
static void gpencil_instance_free(void *instance)
{
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
delete reinterpret_cast<gpencil::Instance *>(instance);
}
static void gpencil_engine_free()
{
blender::gpencil::ShaderModule::module_free();
}
static void gpencil_render_to_image(void *vedata,
struct RenderEngine *engine,
struct RenderLayer *layer,
const struct rcti *UNUSED(rect))
{
UNUSED_VARS(vedata, engine, layer);
}
extern "C" {
static const DrawEngineDataSize gpencil_data_size = DRW_VIEWPORT_DATA_SIZE(GPENCIL_NEXT_Data);
DrawEngineType draw_engine_gpencil_next_type = {
Review

Commented argument names instead of this macro here

Commented argument names instead of this macro here
nullptr,
nullptr,
N_("Gpencil"),
&gpencil_data_size,
&gpencil_engine_init,
&gpencil_engine_free,
&gpencil_instance_free,
&gpencil_cache_init,
&gpencil_cache_populate,
&gpencil_cache_finish,
&gpencil_draw_scene,
nullptr,
nullptr,
&gpencil_render_to_image,
nullptr,
};
}
/** \} */

View File

@ -25,6 +25,7 @@ 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;

View File

@ -0,0 +1,64 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2022 Blender Foundation. */

Check everywhere else :)

2023. Check everywhere else :)
/** \file
* \ingroup draw
*/
#pragma once
#include "BKE_gpencil.h"
#include "BKE_image.h"
filedescriptor marked this conversation as resolved
Review

I am not sure all of those are needed here.
I.e. I do not see any BKE_image API used in this header. Might be missing something.

I am not sure all of those are needed here. I.e. I do not see any `BKE_image` API used in this header. Might be missing something.
#include "DRW_gpu_wrapper.hh"
#include "DRW_render.h"
#include "draw_manager.hh"
#include "draw_pass.hh"
namespace blender::gpencil {
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 bGPDlayer *gpl, bool &do_layer_blending)
{
UNUSED_VARS(object, gpl);

In C++ use void sync(const Object * /*object*/, const bke::greasepencil::Layer & /*layer*/, bool &do_layer_blending)

In C++ use `void sync(const Object * /*object*/, const bke::greasepencil::Layer & /*layer*/, bool &do_layer_blending)`
/* TODO(fclem): All of this is placeholder. */
gpLayer layer;
layer.vertex_color_opacity = 0.0f;
layer.opacity = 1.0f;
layer.thickness_offset = 0.0f;
layer.tint = float4(1.0f, 1.0f, 1.0f, 0.0f);
layers_buf_.append(layer);
do_layer_blending = false;
}
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::gpencil

View File

@ -0,0 +1,130 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2022 Blender Foundation. */
/** \file
* \ingroup draw
*/
#pragma once
#include "BKE_gpencil.h"
#include "BKE_image.h"
#include "DRW_gpu_wrapper.hh"
#include "DRW_render.h"
#include "draw_manager.hh"
#include "draw_pass.hh"
namespace blender::gpencil {
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 = float4x4(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);
invert_m4(mat.ptr());
break;
case LA_SUN:
light.forward = math::normalize(float3(ob->object_to_world[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[3]);
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::gpencil

View File

@ -0,0 +1,313 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2022 Blender Foundation. */
/** \file
* \ingroup draw
*/
#pragma once
#include "BKE_gpencil.h"
#include "BKE_image.h"
#include "DRW_gpu_wrapper.hh"
#include "DRW_render.h"
#include "draw_manager.hh"
#include "draw_pass.hh"
namespace blender::gpencil {
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)
{
ImBuf *ibuf;
ImageUser iuser = {nullptr};
GPUTexture *gpu_tex = nullptr;
void *lock;
bool premul = false;
if (image == nullptr) {
texture_pool_.append(nullptr);
return GP_FLAG_NONE;
}
ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock);
if (ibuf != nullptr && ibuf->rect != nullptr) {
gpu_tex = BKE_image_get_gpu_texture(image, &iuser, ibuf);
premul = (image->alpha_mode == IMA_ALPHA_PREMUL) != 0;
}
BKE_image_release_ibuf(image, ibuf, lock);
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);
/* 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::gpencil

View File

@ -0,0 +1,304 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2022 Blender Foundation. */
/** \file
* \ingroup draw
*/
#pragma once
#include "BKE_gpencil.h"
#include "BKE_image.h"
#include "DRW_gpu_wrapper.hh"
#include "DRW_render.h"
#include "draw_manager.hh"
#include "draw_pass.hh"
#include "gpencil_layer.hh"
#include "gpencil_material.hh"
#include "gpencil_shader.hh"
namespace blender::gpencil {
using namespace draw;
class ObjectModule {
private:
/* TODO(fclem): This is a workaround to the current GPencil data structure.
* Rendering is much easier if we have frame containing layers. */
struct LayerData {
/* Layer / Frame tuple representing a layer inside a frame. */
const bGPDlayer *gpl;
const bGPDframe *gpf;
};
struct FrameData {
/* First layer frame to access frame members. */
const bGPDframe *gpf;
Vector<LayerData, 8> layers;
};
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;
/** Forward vector used to sort gpencil objects. */
float3 camera_forward_;
/** Scene current frame. */
float current_frame_ = 0;
/** \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);
if (is_viewport) {
/* TODO(fclem): Avoid access to global DRW. */
const struct 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_ = float3(main_view.viewinv()[2]);
current_frame_ = DEG_get_ctime(depsgraph);
is_object_fb_needed_ = false;
is_layer_fb_needed_ = false;
/* TODO(fclem): Shrink buffer. */
// objects_buf_.shrink();
}
void sync_gpencil(Manager &manager,
ObjectRef &object_ref,
Framebuffer &main_fb,
PassSortable &main_ps)
{
Object *object = object_ref.object;
bGPdata *gpd = static_cast<bGPdata *>(object->data);
ListBaseWrapper<const bGPDlayer> layers(&gpd->layers);
if (BLI_listbase_is_empty(&gpd->layers)) {
return;
}
const bool is_stroke_order_3d = (gpd->draw_mode == GP_DRAWMODE_3D);
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 (auto i : IndexRange(BKE_object_material_count_eval(object))) {
materials_.sync(object, i, do_material_holdout);
}
uint layer_offset = layers_.object_offset_get();
for (const bGPDlayer *layer : layers) {
layers_.sync(object, layer, do_layer_blending);
}
/* Order rendering using camera Z distance. */
float3 position = float3(object->object_to_world[3]);
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));
Vector<FrameData, 5> frames;
displayed_frame_select(frames, layers);
for (const FrameData &frame : frames) {
/* TODO(fclem): Pass per frame object matrix here. */

Typically dead code is discouraged.
Not sure what is it part of, so can't really give strong suggestions.

Typically dead code is discouraged. Not sure what is it part of, so can't really give strong suggestions.
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 = frame_tint_get(gpd, frame.gpf, current_frame_);
ob.layer_offset = layer_offset;
ob.material_offset = material_offset;
GPUVertBuf *position_tx = DRW_cache_gpencil_position_buffer_get(object, frame.gpf->framenum);
GPUVertBuf *color_tx = DRW_cache_gpencil_color_buffer_get(object, frame.gpf->framenum);
GPUBatch *geom = DRW_cache_gpencil_get(object, frame.gpf->framenum);
if (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.draw(geom, handle);
Review

Add a comment what is this about, or remove.

Add a comment what is this about, or remove.
// 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);
}
}
#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()
{

Same as above.

Same as above.
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;
}
private:
static float4 frame_tint_get(const bGPdata *gpd, const bGPDframe *gpf, int /* current_frame */)
{
/* TODO(fclem): Onion color should rely on time and or frame id and not on runtime.onion_id.
* This should be evaluated at draw time as it is just a way of displaying the data. */
const bool use_onion_custom_col = (gpd->onion_flag & GP_ONION_GHOST_PREVCOL) != 0;
const bool use_onion_fade = (gpd->onion_flag & GP_ONION_FADE) != 0;
const bool use_next_col = gpf->runtime.onion_id > 0.0f;
const float *onion_col_custom = (use_onion_custom_col) ?
(use_next_col ? gpd->gcolor_next : gpd->gcolor_prev) :
U.gpencil_new_layer_col;
float4 tint = {UNPACK3(onion_col_custom), 1.0f};
tint[3] = use_onion_fade ? (1.0f / abs(gpf->runtime.onion_id)) : 0.5f;
tint[3] *= gpd->onion_factor;
tint[3] = (gpd->onion_factor > 0.0f) ? clamp_f(tint[3], 0.1f, 1.0f) :
clamp_f(tint[3], 0.01f, 1.0f);
return tint;
}
void displayed_frame_select(Vector<FrameData, 5> &frames,
ListBaseWrapper<const bGPDlayer> layers)
{
/* TODO(fclem): Select onion skin frames. */
/** \note Change data layout to be Frame major instead of Layer major.
* Hopefully the GPencil data layout will be closer to that in the future. */
FrameData frame_data;
frame_data.gpf = layers.get(0)->actframe;
for (const bGPDlayer *layer : layers) {
LayerData layer_data;
layer_data.gpf = layer->actframe;
layer_data.gpl = layer;
frame_data.layers.append(layer_data);
}
frames.append(frame_data);
}
};
} // namespace blender::gpencil

View File

@ -0,0 +1,115 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2022 Blender Foundation. */
/** \file
* \ingroup draw
*/
#include "gpencil_shader.hh"
namespace blender::gpencil {
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;
}
#ifdef DEBUG
/* 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 to put new line and flush the output buffer.

Also, why not to assert the details in the static_shader_create_info_name_get() ? It is a bit weird to print an error about mistake in some other code.

P.S. I think we should implement Glog style of checkers: DCHECK_NE(name, nullptr) << "GPencil: Missing case for eShaderType(" << i << ") in static_shader_create_info_name_get()."

`<< std::endl` to put new line and flush the output buffer. Also, why not to assert the details in the `static_shader_create_info_name_get()` ? It is a bit weird to print an error about mistake in some other code. P.S. I think we should implement Glog style of checkers: `DCHECK_NE(name, nullptr) << "GPencil: Missing case for eShaderType(" << i << ") in static_shader_create_info_name_get()."`
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 "gpencil_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) {
fprintf(stderr, "GPencil: error: Could not compile static shader \"%s\"\n", shader_name);

Pick a side between fprintf and std::cerr ;)

Pick a side between `fprintf` and `std::cerr` ;)
}
BLI_assert(shaders_[shader_type] != nullptr);
}
return shaders_[shader_type];
}
} // namespace blender::gpencil

View File

@ -0,0 +1,65 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2022 Blender Foundation. */
/** \file
* \ingroup draw
*/
#pragma once
#include "DRW_render.h"
namespace blender::gpencil {
enum eShaderType {
/* SMAA antialiasing */
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::gpencil

View File

@ -1,11 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef GPU_SHADER
# pragma once
# 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;
@ -14,6 +19,7 @@ typedef enum gpLightType gpLightType;
#endif
enum gpMaterialFlag {
GP_FLAG_NONE = 0u,
GP_STROKE_ALIGNMENT_STROKE = 1u,
GP_STROKE_ALIGNMENT_OBJECT = 2u,
GP_STROKE_ALIGNMENT_FIXED = 3u,
@ -50,6 +56,12 @@ 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;
@ -116,6 +128,38 @@ struct gpLight {
BLI_STATIC_ASSERT_ALIGN(gpLight, 16)
#endif
struct gpObject {
/** Wether or not to apply lighting to the GPencil object. */
bool1 is_shadeless;
/** Switch between 2d and 3D stroke order. */
bool1 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

@ -0,0 +1,334 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2017 Blender Foundation. */
/** \file
* \ingroup draw
*/
#include "BKE_camera.h"
#include "BLI_listbase_wrapper.hh"
#include "DNA_camera_types.h"
#include "DNA_gpencil_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::gpencil {
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[3]));
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);
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::gpencil

View File

@ -15,19 +15,19 @@ vec3 gpencil_lighting(void)
{
vec3 light_accum = vec3(0.0);
for (int i = 0; i < GPENCIL_LIGHT_BUFFER_LEN; i++) {
if (lights[i]._color.x == -1.0) {
if (gp_lights[i]._color.x == -1.0) {
break;
}
vec3 L = lights[i]._position - gp_interp.pos;
vec3 L = gp_lights[i]._position - gp_interp.pos;
float vis = 1.0;
gpLightType type = floatBitsToUint(lights[i]._type);
gpLightType type = floatBitsToUint(gp_lights[i]._type);
/* Spot Attenuation. */
if (type == GP_LIGHT_TYPE_SPOT) {
mat3 rot_scale = mat3(lights[i]._right, lights[i]._up, lights[i]._forward);
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 - lights[i]._spot_size) / lights[i]._spot_blend);
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);
}
@ -37,7 +37,7 @@ vec3 gpencil_lighting(void)
vis /= L_len_sqr;
}
else {
L = lights[i]._forward;
L = gp_lights[i]._forward;
L_len_sqr = 1.0;
}
/* Lambertian falloff */
@ -45,7 +45,7 @@ vec3 gpencil_lighting(void)
L /= sqrt(L_len_sqr);
vis *= clamp(dot(gpNormal, L), 0.0, 1.0);
}
light_accum += vis * lights[i]._color;
light_accum += vis * gp_lights[i]._color;
}
/* Clamp to avoid NaNs. */
return clamp(light_accum, 0.0, 1e10);
@ -68,7 +68,7 @@ void main()
bool radial = flag_test(gp_interp.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.mat_flag >> GPENCIl_MATID_SHIFT;
col = mix(materials[matid].fill_color, materials[matid].fill_mix_color, fac);
col = mix(gp_materials[matid].fill_color, gp_materials[matid].fill_mix_color, fac);
}
else /* SOLID */ {
col = vec4(1.0);

View File

@ -27,12 +27,14 @@ void gpencil_color_output(vec4 stroke_col, vec4 vert_col, float vert_strength, f
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 = materials[ma1.x + gpMaterialOffset];
gpMaterial gp_mat = gp_materials[ma1.x + gpMaterialOffset];
gpMaterialFlag gp_flag = floatBitsToUint(gp_mat._flag);
gl_Position = gpencil_vertex(vec4(viewportSize, 1.0 / viewportSize),

View File

@ -2,6 +2,8 @@
#include "gpu_shader_create_info.hh"
#include "gpencil_defines.h"
/* -------------------------------------------------------------------- */
/** \name GPencil Object rendering
* \{ */
@ -26,8 +28,8 @@ GPU_SHADER_CREATE_INFO(gpencil_geometry)
.sampler(3, ImageType::FLOAT_2D, "gpStrokeTexture")
.sampler(4, ImageType::DEPTH_2D, "gpSceneDepthTexture")
.sampler(5, ImageType::FLOAT_2D, "gpMaskTexture")
.uniform_buf(4, "gpMaterial", "materials[GPENCIL_MATERIAL_BUFFER_LEN]", Frequency::BATCH)
.uniform_buf(3, "gpLight", "lights[GPENCIL_LIGHT_BUFFER_LEN]", Frequency::BATCH)
.uniform_buf(4, "gpMaterial", "gp_materials[GPENCIL_MATERIAL_BUFFER_LEN]", Frequency::BATCH)
.uniform_buf(3, "gpLight", "gp_lights[GPENCIL_LIGHT_BUFFER_LEN]", Frequency::BATCH)
.push_constant(Type::VEC2, "viewportSize")
/* Per Object */
.push_constant(Type::VEC3, "gpNormal")
@ -43,7 +45,39 @@ GPU_SHADER_CREATE_INFO(gpencil_geometry)
.vertex_out(gpencil_geometry_iface)
.vertex_source("gpencil_vert.glsl")
.fragment_source("gpencil_frag.glsl")
.additional_info("draw_gpencil");
.additional_info("draw_gpencil", "draw_resource_id_varying");
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_source("gpencil_vert.glsl")
.fragment_source("gpencil_frag.glsl")
.additional_info("draw_gpencil_new");
/** \} */

View File

@ -1276,7 +1276,8 @@ static void drw_engines_enable(ViewLayer *UNUSED(view_layer),
drw_engines_enable_from_engine(engine_type, drawtype);
if (gpencil_engine_needed && ((drawtype >= OB_SOLID) || !use_xray)) {
use_drw_engine(&draw_engine_gpencil_type);
use_drw_engine(((U.experimental.enable_gpencil_next) ? &draw_engine_gpencil_next_type :
&draw_engine_gpencil_type));
}
if (is_compositor_enabled()) {
@ -1888,10 +1889,13 @@ static void DRW_render_gpencil_to_image(RenderEngine *engine,
struct RenderLayer *render_layer,
const rcti *rect)
{
if (draw_engine_gpencil_type.render_to_image) {
DrawEngineType *draw_engine = U.experimental.enable_gpencil_next ?
&draw_engine_gpencil_next_type :
&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_gpencil_type);
draw_engine_gpencil_type.render_to_image(gpdata, engine, render_layer, rect);
draw_engine);
draw_engine->render_to_image(gpdata, engine, render_layer, rect);
}
}
@ -2455,7 +2459,8 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph,
else if (!draw_surface) {
/* grease pencil selection */
if (drw_gpencil_engine_needed(depsgraph, v3d)) {
use_drw_engine(&draw_engine_gpencil_type);
use_drw_engine(((U.experimental.enable_gpencil_next) ? &draw_engine_gpencil_next_type :
&draw_engine_gpencil_type));
}
drw_engines_enable_overlays();
@ -2465,7 +2470,8 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph,
drw_engines_enable_basic();
/* grease pencil selection */
if (drw_gpencil_engine_needed(depsgraph, v3d)) {
use_drw_engine(&draw_engine_gpencil_type);
use_drw_engine(((U.experimental.enable_gpencil_next) ? &draw_engine_gpencil_next_type :
&draw_engine_gpencil_type));
}
drw_engines_enable_overlays();
@ -2630,7 +2636,8 @@ void DRW_draw_depth_loop(struct Depsgraph *depsgraph,
drw_manager_init(&DST, viewport, NULL);
if (use_gpencil) {
use_drw_engine(&draw_engine_gpencil_type);
use_drw_engine(((U.experimental.enable_gpencil_next) ? &draw_engine_gpencil_next_type :
&draw_engine_gpencil_type));
}
if (use_basic) {
drw_engines_enable_basic();
@ -2998,6 +3005,7 @@ void DRW_engines_register(void)
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_select_type);

View File

@ -130,6 +130,22 @@ GPU_SHADER_CREATE_INFO(draw_gpencil)
.push_constant(Type::FLOAT, "gpThicknessOffset")
.additional_info("draw_modelmat", "draw_object_infos");
GPU_SHADER_CREATE_INFO(draw_gpencil_new)
.typedef_source("gpencil_shader_shared.h")
.define("DRW_GPENCIL_INFO")
.sampler(0, ImageType::FLOAT_BUFFER, "gp_pos_tx")
.sampler(1, ImageType::FLOAT_BUFFER, "gp_col_tx")
/* Per Object */
.define("gpThicknessScale", "1.0") /* TODO(fclem): Replace with object info. */
.define("gpThicknessWorldScale", "1.0 / 2000.0") /* TODO(fclem): Same as above. */
.define("gpThicknessIsScreenSpace", "(gpThicknessWorldScale < 0.0)")
/* Per Layer */
.define("gpThicknessOffset", "0.0") /* TODO(fclem): Remove. */
.additional_info("draw_modelmat_new",
"draw_resource_id_varying",
"draw_view",
"draw_object_infos_new");
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -29,6 +29,7 @@ set(INC
# For *_info.hh includes.
../compositor/realtime_compositor
../draw/engines/eevee_next
../draw/engines/gpencil
../draw/intern
# For node muting stuff.

View File

@ -653,7 +653,8 @@ typedef struct UserDef_Experimental {
char use_sculpt_texture_paint;
char use_draw_manager_acquire_lock;
char use_realtime_compositor;
char _pad[7];
char enable_gpencil_next;
char _pad[6];
/** `makesdna` does not allow empty structs. */
} UserDef_Experimental;

View File

@ -6389,6 +6389,10 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "enable_eevee_next", 1);
RNA_def_property_ui_text(prop, "EEVEE Next", "Enable the new EEVEE codebase, requires restart");
prop = RNA_def_property(srna, "enable_gpencil_next", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "enable_gpencil_next", 1);
RNA_def_property_ui_text(prop, "GPENCIL Next", "Enable the new GPencil codebase");
prop = RNA_def_property(srna, "use_viewport_debug", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "use_viewport_debug", 1);
RNA_def_property_ui_text(prop,