Add Chromium-only Blender WebEngine parity work
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "BLI_rand.h"
|
||||
#include "BLI_smaa_textures.h"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "gpencil_engine_private.hh"
|
||||
|
||||
namespace blender::draw::gpencil {
|
||||
|
||||
void Instance::antialiasing_init()
|
||||
{
|
||||
const float2 size_f = this->draw_ctx->viewport_size_get();
|
||||
const int2 size(size_f[0], size_f[1]);
|
||||
const float4 metrics = {1.0f / size[0], 1.0f / size[1], float(size[0]), float(size[1])};
|
||||
|
||||
if (this->simplify_antialias) {
|
||||
/* No AA fallback. */
|
||||
PassSimple &pass = this->smaa_resolve_ps;
|
||||
pass.init();
|
||||
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM);
|
||||
pass.shader_set(ShaderCache::get().antialiasing[2].get());
|
||||
pass.bind_texture("blend_tx", &this->color_tx);
|
||||
pass.bind_texture("color_tx", &this->color_tx);
|
||||
pass.bind_texture("reveal_tx", &this->reveal_tx);
|
||||
pass.push_constant("do_anti_aliasing", false);
|
||||
pass.push_constant("only_alpha", this->draw_wireframe);
|
||||
pass.push_constant("viewport_metrics", metrics);
|
||||
pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->smaa_search_tx.is_valid()) {
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ;
|
||||
this->smaa_search_tx.ensure_2d(
|
||||
gpu::TextureFormat::UNORM_8, int2(SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT), usage);
|
||||
GPU_texture_update(this->smaa_search_tx, GPU_DATA_UBYTE, searchTexBytes);
|
||||
|
||||
this->smaa_area_tx.ensure_2d(
|
||||
gpu::TextureFormat::UNORM_8_8, int2(AREATEX_WIDTH, AREATEX_HEIGHT), usage);
|
||||
GPU_texture_update(this->smaa_area_tx, GPU_DATA_UBYTE, areaTexBytes);
|
||||
|
||||
GPU_texture_filter_mode(this->smaa_search_tx, true);
|
||||
GPU_texture_filter_mode(this->smaa_area_tx, true);
|
||||
}
|
||||
|
||||
{
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT;
|
||||
this->smaa_edge_tx.acquire_2d(size, gpu::TextureFormat::UNORM_8_8, usage);
|
||||
this->smaa_weight_tx.acquire_2d(size, gpu::TextureFormat::UNORM_8_8_8_8, usage);
|
||||
|
||||
this->smaa_edge_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(this->smaa_edge_tx));
|
||||
this->smaa_weight_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(this->smaa_weight_tx));
|
||||
}
|
||||
|
||||
SceneGpencil gpencil_settings = this->scene->grease_pencil_settings;
|
||||
const float luma_weight = this->is_viewport ? gpencil_settings.smaa_threshold :
|
||||
gpencil_settings.smaa_threshold_render;
|
||||
|
||||
{
|
||||
/* Stage 1: Edge detection. */
|
||||
PassSimple &pass = this->smaa_edge_ps;
|
||||
pass.init();
|
||||
pass.state_set(DRW_STATE_WRITE_COLOR);
|
||||
pass.shader_set(ShaderCache::get().antialiasing[0].get());
|
||||
pass.bind_texture("color_tx", &this->color_tx);
|
||||
pass.bind_texture("reveal_tx", &this->reveal_tx);
|
||||
pass.push_constant("viewport_metrics", metrics);
|
||||
pass.push_constant("luma_weight", luma_weight);
|
||||
pass.clear_color(float4(0.0f));
|
||||
pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
{
|
||||
/* Stage 2: Blend Weight/Coord. */
|
||||
PassSimple &pass = this->smaa_weight_ps;
|
||||
pass.init();
|
||||
pass.state_set(DRW_STATE_WRITE_COLOR);
|
||||
pass.shader_set(ShaderCache::get().antialiasing[1].get());
|
||||
pass.bind_texture("edges_tx", &this->smaa_edge_tx);
|
||||
pass.bind_texture("area_tx", &this->smaa_area_tx);
|
||||
pass.bind_texture("search_tx", &this->smaa_search_tx);
|
||||
pass.push_constant("viewport_metrics", metrics);
|
||||
pass.clear_color(float4(0.0f));
|
||||
pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
{
|
||||
/* Stage 3: Resolve. */
|
||||
PassSimple &pass = this->smaa_resolve_ps;
|
||||
pass.init();
|
||||
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM);
|
||||
pass.shader_set(ShaderCache::get().antialiasing[2].get());
|
||||
pass.bind_texture("blend_tx", &this->smaa_weight_tx);
|
||||
pass.bind_texture("color_tx", &this->color_tx);
|
||||
pass.bind_texture("reveal_tx", &this->reveal_tx);
|
||||
pass.push_constant("do_anti_aliasing", true);
|
||||
pass.push_constant("only_alpha", this->draw_wireframe);
|
||||
pass.push_constant("viewport_metrics", metrics);
|
||||
pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::antialiasing_draw(Manager &manager)
|
||||
{
|
||||
if (!this->simplify_antialias) {
|
||||
GPU_framebuffer_bind(this->smaa_edge_fb);
|
||||
manager.submit(this->smaa_edge_ps);
|
||||
|
||||
GPU_framebuffer_bind(this->smaa_weight_fb);
|
||||
manager.submit(this->smaa_weight_ps);
|
||||
}
|
||||
|
||||
GPU_framebuffer_bind(this->scene_fb);
|
||||
manager.submit(this->smaa_resolve_ps);
|
||||
|
||||
if (this->need_grease_pencil_pass) {
|
||||
GPU_framebuffer_bind(this->gpencil_pass_fb);
|
||||
GPU_framebuffer_clear(this->gpencil_pass_fb, GPU_COLOR_BIT, {0, 0, 0, 0}, 0, 0);
|
||||
manager.submit(this->smaa_resolve_ps);
|
||||
}
|
||||
|
||||
/* The engine might not support passes, so check if the combined pass actually exists before
|
||||
* rendering grease pencil to it. */
|
||||
const bool combined_pass_exists = DRW_viewport_pass_texture_exists(RE_PASSNAME_COMBINED);
|
||||
if (this->need_combined_pass && combined_pass_exists) {
|
||||
GPU_framebuffer_bind(this->combined_pass_fb);
|
||||
manager.submit(this->smaa_resolve_ps);
|
||||
}
|
||||
}
|
||||
|
||||
static float erfinv_approx(const float x)
|
||||
{
|
||||
/* From: Approximating the `erfinv` function by Mike Giles. */
|
||||
/* To avoid trouble at the limit, clamp input to 1-epsilon. */
|
||||
const float a = math::min(fabsf(x), 0.99999994f);
|
||||
float w = -logf((1.0f - a) * (1.0f + a));
|
||||
float p;
|
||||
if (w < 5.0f) {
|
||||
w = w - 2.5f;
|
||||
p = 2.81022636e-08f;
|
||||
p = p * w + 3.43273939e-07f;
|
||||
p = p * w + -3.5233877e-06f;
|
||||
p = p * w + -4.39150654e-06f;
|
||||
p = p * w + 0.00021858087f;
|
||||
p = p * w + -0.00125372503f;
|
||||
p = p * w + -0.00417768164f;
|
||||
p = p * w + 0.246640727f;
|
||||
p = p * w + 1.50140941f;
|
||||
}
|
||||
else {
|
||||
w = sqrtf(w) - 3.0f;
|
||||
p = -0.000200214257f;
|
||||
p = p * w + 0.000100950558f;
|
||||
p = p * w + 0.00134934322f;
|
||||
p = p * w + -0.00367342844f;
|
||||
p = p * w + 0.00573950773f;
|
||||
p = p * w + -0.0076224613f;
|
||||
p = p * w + 0.00943887047f;
|
||||
p = p * w + 1.00167406f;
|
||||
p = p * w + 2.83297682f;
|
||||
}
|
||||
return p * x;
|
||||
}
|
||||
|
||||
float2 Instance::antialiasing_sample_get(const int sample_index, const int sample_count)
|
||||
{
|
||||
if (sample_count < 2) {
|
||||
return float2(0.0f);
|
||||
}
|
||||
|
||||
double halton[2];
|
||||
{
|
||||
uint primes[2] = {2, 3};
|
||||
double ofs[2] = {0, 0};
|
||||
BLI_halton_2d(primes, ofs, sample_index, halton);
|
||||
}
|
||||
/* Uniform distribution [0..1]. */
|
||||
const float2 rand = float2(halton[0], halton[1]);
|
||||
/* Uniform distribution [-1..1]. */
|
||||
const float2 rand_remap = rand * 2.0f - 1.0f;
|
||||
/* Limit sampling region to avoid outliers. */
|
||||
const float2 rand_adjusted = rand_remap * 0.93f;
|
||||
/* Gaussian distribution [-1..1]. */
|
||||
const float2 offset = float2(erfinv_approx(rand_adjusted.x), erfinv_approx(rand_adjusted.y));
|
||||
/* Gaussian fitted to Blackman-Harris (follows EEVEE). */
|
||||
const float sigma = 0.284f;
|
||||
/* NOTE(fclem): Not sure where this sqrt comes from but is needed to match EEVEE. */
|
||||
return offset * sqrt(sigma);
|
||||
}
|
||||
|
||||
void Instance::antialiasing_accumulate(Manager &manager, const float alpha)
|
||||
{
|
||||
BLI_assert_msg(this->render_color_tx.gpu_texture() != nullptr,
|
||||
"This should only be called during render");
|
||||
const int2 size = this->render_color_tx.size().xy();
|
||||
|
||||
const eGPUTextureUsage usage = GPU_TEXTURE_USAGE_HOST_READ | GPU_TEXTURE_USAGE_SHADER_READ |
|
||||
GPU_TEXTURE_USAGE_SHADER_WRITE | GPU_TEXTURE_USAGE_ATTACHMENT;
|
||||
accumulation_tx.ensure_2d(gpu::TextureFormat::GPENCIL_ACCUM_FORMAT, size, usage);
|
||||
|
||||
{
|
||||
PassSimple &pass = this->accumulate_ps;
|
||||
pass.init();
|
||||
pass.state_set(DRW_STATE_WRITE_DEPTH /* There is no depth, but avoid blank state. */);
|
||||
pass.shader_set(ShaderCache::get().accumulation.get());
|
||||
pass.bind_image("src_img", &this->render_color_tx);
|
||||
pass.bind_image("dst_img", &this->accumulation_tx);
|
||||
pass.push_constant("weight_src", alpha);
|
||||
pass.push_constant("weight_dst", 1.0f - alpha);
|
||||
pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
|
||||
accumulation_fb.ensure(size);
|
||||
GPU_framebuffer_bind(this->accumulation_fb);
|
||||
manager.submit(this->accumulate_ps);
|
||||
}
|
||||
|
||||
} // namespace blender::draw::gpencil
|
||||
@@ -0,0 +1,481 @@
|
||||
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "DRW_engine.hh"
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "ED_view3d.hh"
|
||||
|
||||
#include "DNA_material_types.h"
|
||||
|
||||
#include "BKE_gpencil_legacy.h"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_material.hh"
|
||||
#include "BKE_object.hh"
|
||||
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_hash.h"
|
||||
#include "BLI_link_utils.h"
|
||||
#include "BLI_math_color.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_memblock.h"
|
||||
|
||||
#include "IMB_colormanagement.hh"
|
||||
|
||||
#include "gpencil_engine_private.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
|
||||
#include "UI_resources.hh"
|
||||
|
||||
namespace blender::draw::gpencil {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Object
|
||||
* \{ */
|
||||
|
||||
tObject *gpencil_object_cache_add(Instance *inst,
|
||||
Object *ob,
|
||||
const bool is_stroke_order_3d,
|
||||
const Bounds<float3> bounds)
|
||||
{
|
||||
tObject *tgp_ob = static_cast<tObject *>(BLI_memblock_alloc(inst->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(inst->camera_z_axis, ob->object_to_world().location());
|
||||
tgp_ob->is_drawmode3d = is_stroke_order_3d;
|
||||
|
||||
/* Check if any material with holdout flag enabled. */
|
||||
tgp_ob->do_mat_holdout = false;
|
||||
const int tot_materials = BKE_object_material_used_with_fallback_eval(*ob);
|
||||
for (int i = 0; i < tot_materials; i++) {
|
||||
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, i + 1);
|
||||
if (((gp_style != nullptr) && (gp_style->flag & GP_MATERIAL_IS_STROKE_HOLDOUT)) ||
|
||||
(gp_style->flag & GP_MATERIAL_IS_FILL_HOLDOUT))
|
||||
{
|
||||
tgp_ob->do_mat_holdout = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
float3 size = (bounds.max - bounds.min) * 0.5f;
|
||||
float3 center = math::midpoint(bounds.min, bounds.max);
|
||||
/* Convert bbox to matrix */
|
||||
float mat[4][4];
|
||||
unit_m4(mat);
|
||||
copy_v3_v3(mat[3], center);
|
||||
/* Avoid division by 0.0 later. */
|
||||
add_v3_fl(size, 1e-8f);
|
||||
rescale_m4(mat, size);
|
||||
/* BBox space to World. */
|
||||
mul_m4_m4m4(mat, ob->object_to_world().ptr(), mat);
|
||||
if (View::default_get().is_persp()) {
|
||||
/* BBox center to camera vector. */
|
||||
sub_v3_v3v3(tgp_ob->plane_normal, inst->camera_pos, mat[3]);
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(tgp_ob->plane_normal, inst->camera_z_axis);
|
||||
}
|
||||
/* World to BBox space. */
|
||||
invert_m4(mat);
|
||||
/* Normalize the vector in BBox space. */
|
||||
mul_mat3_m4_v3(mat, tgp_ob->plane_normal);
|
||||
normalize_v3(tgp_ob->plane_normal);
|
||||
|
||||
transpose_m4(mat);
|
||||
/* mat is now a "normal" matrix which will transform
|
||||
* BBox space normal to world space. */
|
||||
mul_mat3_m4_v3(mat, tgp_ob->plane_normal);
|
||||
normalize_v3(tgp_ob->plane_normal);
|
||||
|
||||
/* 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. */
|
||||
unit_m4(tgp_ob->plane_mat);
|
||||
copy_v3_v3(tgp_ob->plane_mat[2], tgp_ob->plane_normal);
|
||||
orthogonalize_m4(tgp_ob->plane_mat, 2);
|
||||
mul_mat3_m4_v3(ob->object_to_world().ptr(), size);
|
||||
float radius = len_v3(size);
|
||||
mul_m4_v3(ob->object_to_world().ptr(), center);
|
||||
rescale_m4(tgp_ob->plane_mat, float3{radius, radius, radius});
|
||||
copy_v3_v3(tgp_ob->plane_mat[3], center);
|
||||
|
||||
/* Add to corresponding list if is in front. */
|
||||
if (ob->dtx & OB_DRAW_IN_FRONT) {
|
||||
BLI_LINKS_APPEND(&inst->tobjects_infront, tgp_ob);
|
||||
}
|
||||
else {
|
||||
BLI_LINKS_APPEND(&inst->tobjects, tgp_ob);
|
||||
}
|
||||
|
||||
return tgp_ob;
|
||||
}
|
||||
|
||||
#define SORT_IMPL_LINKTYPE tObject
|
||||
|
||||
#define SORT_IMPL_FUNC gpencil_tobject_sort_fn_r
|
||||
#include "../../blenlib/intern/list_sort_impl.h"
|
||||
#undef SORT_IMPL_FUNC
|
||||
|
||||
#undef SORT_IMPL_LINKTYPE
|
||||
|
||||
static int gpencil_tobject_dist_sort(const void *a, const void *b)
|
||||
{
|
||||
const tObject *ob_a = static_cast<const tObject *>(a);
|
||||
const tObject *ob_b = static_cast<const tObject *>(b);
|
||||
/* Reminder, camera_z is negative in front of the camera. */
|
||||
if (ob_a->camera_z > ob_b->camera_z) {
|
||||
return 1;
|
||||
}
|
||||
if (ob_a->camera_z < ob_b->camera_z) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gpencil_object_cache_sort(Instance *inst)
|
||||
{
|
||||
if (inst->is_sorted) {
|
||||
return;
|
||||
}
|
||||
/* Sort object by distance to the camera. */
|
||||
if (inst->tobjects.first) {
|
||||
inst->tobjects.first = gpencil_tobject_sort_fn_r(inst->tobjects.first,
|
||||
gpencil_tobject_dist_sort);
|
||||
/* Relink last pointer. */
|
||||
while (inst->tobjects.last->next) {
|
||||
inst->tobjects.last = inst->tobjects.last->next;
|
||||
}
|
||||
}
|
||||
if (inst->tobjects_infront.first) {
|
||||
inst->tobjects_infront.first = gpencil_tobject_sort_fn_r(inst->tobjects_infront.first,
|
||||
gpencil_tobject_dist_sort);
|
||||
/* Relink last pointer. */
|
||||
while (inst->tobjects_infront.last->next) {
|
||||
inst->tobjects_infront.last = inst->tobjects_infront.last->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Join both lists, adding in front. */
|
||||
if (inst->tobjects_infront.first != nullptr) {
|
||||
if (inst->tobjects.last != nullptr) {
|
||||
inst->tobjects.last->next = inst->tobjects_infront.first;
|
||||
inst->tobjects.last = inst->tobjects_infront.last;
|
||||
inst->tobjects_infront.first = inst->tobjects.last = nullptr;
|
||||
}
|
||||
else {
|
||||
/* Only in front objects. */
|
||||
inst->tobjects.first = inst->tobjects_infront.first;
|
||||
inst->tobjects.last = inst->tobjects_infront.last;
|
||||
inst->tobjects_infront.first = inst->tobjects.last = nullptr;
|
||||
}
|
||||
}
|
||||
inst->is_sorted = true;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Layer
|
||||
* \{ */
|
||||
|
||||
static float grease_pencil_layer_final_opacity_get(const Instance *inst,
|
||||
const Object *ob,
|
||||
const GreasePencil &grease_pencil,
|
||||
const bke::greasepencil::Layer &layer)
|
||||
{
|
||||
const bool is_obact = ((inst->obact) && (inst->obact == ob));
|
||||
const bool is_fade = (inst->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 (!inst->is_render) {
|
||||
if (is_obact && is_fade) {
|
||||
return layer.opacity * inst->fade_layer_opacity;
|
||||
}
|
||||
if (!is_obact && (inst->fade_gp_object_opacity > -1.0f)) {
|
||||
return layer.opacity * inst->fade_gp_object_opacity;
|
||||
}
|
||||
}
|
||||
return layer.opacity;
|
||||
}
|
||||
|
||||
static float4 grease_pencil_layer_final_tint_and_alpha_get(const Instance *inst,
|
||||
const GreasePencil &grease_pencil,
|
||||
const int onion_id,
|
||||
float *r_alpha)
|
||||
{
|
||||
const bool use_onion = (onion_id != 0);
|
||||
if (use_onion && inst->do_onion) {
|
||||
const bool use_onion_custom_col = (grease_pencil.onion_skinning_settings.flag &
|
||||
GP_ONION_SKINNING_USE_CUSTOM_COLORS) != 0;
|
||||
const bool use_onion_fade = (grease_pencil.onion_skinning_settings.flag &
|
||||
GP_ONION_SKINNING_USE_FADE) != 0;
|
||||
const bool use_next_col = onion_id > 0;
|
||||
|
||||
const float onion_factor = grease_pencil.onion_skinning_settings.opacity;
|
||||
|
||||
float3 color_next, color_prev;
|
||||
if (use_onion_custom_col) {
|
||||
color_next = float3(grease_pencil.onion_skinning_settings.color_after);
|
||||
color_prev = float3(grease_pencil.onion_skinning_settings.color_before);
|
||||
}
|
||||
else {
|
||||
ui::theme::get_color_3fv(TH_FRAME_AFTER, color_next);
|
||||
ui::theme::get_color_3fv(TH_FRAME_BEFORE, color_prev);
|
||||
}
|
||||
|
||||
const float4 onion_col_custom = use_next_col ? float4(color_next, 1.0f) :
|
||||
float4(color_prev, 1.0f);
|
||||
|
||||
*r_alpha = use_onion_fade ? (1.0f / abs(onion_id)) : 1.0f;
|
||||
*r_alpha *= onion_factor;
|
||||
*r_alpha = (onion_factor > 0.0f) ? clamp_f(*r_alpha, 0.1f, 1.0f) :
|
||||
clamp_f(*r_alpha, 0.01f, 1.0f);
|
||||
*r_alpha *= inst->xray_alpha;
|
||||
|
||||
return onion_col_custom;
|
||||
}
|
||||
|
||||
/* Layer tint is not a property in GPv3 anymore. It's only used for onion skinning. The previous
|
||||
* property is replaced by a tint modifier during conversion. */
|
||||
float4 layer_tint(0.0f);
|
||||
if (GPENCIL_SIMPLIFY_TINT(inst->scene)) {
|
||||
layer_tint[3] = 0.0f;
|
||||
}
|
||||
*r_alpha = 1.0f;
|
||||
*r_alpha *= inst->xray_alpha;
|
||||
|
||||
return layer_tint;
|
||||
}
|
||||
|
||||
/* Random color by layer. */
|
||||
static void grease_pencil_layer_random_color_get(const Object *ob,
|
||||
const 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);
|
||||
IMB_colormanagement_rec709_to_scene_linear(r_color, r_color);
|
||||
}
|
||||
|
||||
tLayer *grease_pencil_layer_cache_get(tObject *tgp_ob, int layer_id, const bool skip_onion)
|
||||
{
|
||||
BLI_assert(layer_id >= 0);
|
||||
for (tLayer *layer = tgp_ob->layers.first; layer != nullptr; layer = layer->next) {
|
||||
if (skip_onion && layer->is_onion) {
|
||||
continue;
|
||||
}
|
||||
if (layer->layer_id == layer_id) {
|
||||
return layer;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
tLayer *grease_pencil_layer_cache_add(Instance *inst,
|
||||
const Object *ob,
|
||||
const bke::greasepencil::Layer &layer,
|
||||
const int onion_id,
|
||||
const bool is_used_as_mask,
|
||||
tObject *tgp_ob)
|
||||
|
||||
{
|
||||
using namespace bke::greasepencil;
|
||||
const GreasePencil &grease_pencil = DRW_object_get_data_for_drawing<GreasePencil>(*ob);
|
||||
|
||||
const bool is_in_front = (ob->dtx & OB_DRAW_IN_FRONT);
|
||||
|
||||
const bool override_vertcol = (inst->v3d_color_type != -1);
|
||||
/* In draw mode and vertex paint mode it's possible to draw vertex colors so we want to make sure
|
||||
* to render them. Otherwise this can lead to unexpected behavior. */
|
||||
const bool is_vert_col_mode = (inst->v3d_color_type == V3D_SHADING_VERTEX_COLOR) ||
|
||||
(ob->mode & OB_MODE_VERTEX_PAINT) != 0 ||
|
||||
(ob->mode & OB_MODE_PAINT_GREASE_PENCIL) != 0 || inst->is_render;
|
||||
const bool is_viewlayer_render = inst->is_render && !layer.view_layer_name().is_empty() &&
|
||||
STREQ(inst->view_layer->name, layer.view_layer_name().c_str());
|
||||
const bool disable_masks_render = is_viewlayer_render &&
|
||||
(layer.base.flag &
|
||||
GP_LAYER_TREE_NODE_DISABLE_MASKS_IN_VIEWLAYER) != 0;
|
||||
bool is_masked = !disable_masks_render && layer.use_masks() && !layer.masks.is_empty();
|
||||
|
||||
const float vert_col_opacity = (override_vertcol) ?
|
||||
(is_vert_col_mode ? inst->vertex_paint_opacity : 0.0f) :
|
||||
(inst->is_render ? 1.0f : inst->vertex_paint_opacity);
|
||||
/* If the layer is used as a mask (but is otherwise not visible in the render), render it with a
|
||||
* opacity of 0 so that it can still mask other layers. */
|
||||
const float layer_opacity = !is_used_as_mask ? grease_pencil_layer_final_opacity_get(
|
||||
inst, ob, grease_pencil, layer) :
|
||||
0.0f;
|
||||
|
||||
float layer_alpha = inst->xray_alpha;
|
||||
const float4 layer_tint = grease_pencil_layer_final_tint_and_alpha_get(
|
||||
inst, grease_pencil, onion_id, &layer_alpha);
|
||||
|
||||
/* Create the new layer descriptor. */
|
||||
int64_t id = inst->gp_layer_pool->append_and_get_index({});
|
||||
tLayer *tgp_layer = &(*inst->gp_layer_pool)[id];
|
||||
BLI_LINKS_APPEND(&tgp_ob->layers, tgp_layer);
|
||||
tgp_layer->layer_id = *grease_pencil.get_layer_index(layer);
|
||||
tgp_layer->is_onion = onion_id != 0;
|
||||
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(inst->gp_maskbit_pool));
|
||||
tgp_layer->mask_invert_bits = static_cast<BLI_bitmap *>(
|
||||
BLI_memblock_alloc(inst->gp_maskbit_pool));
|
||||
BLI_bitmap_set_all(tgp_layer->mask_bits, false, GP_MAX_MASKBITS);
|
||||
|
||||
for (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) {
|
||||
inst->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. */
|
||||
inst->use_signed_fb = true;
|
||||
}
|
||||
|
||||
if (tgp_layer->blend_ps == nullptr) {
|
||||
tgp_layer->blend_ps = std::make_unique<PassSimple>("GPencil Blend Layer");
|
||||
}
|
||||
PassSimple &pass = *tgp_layer->blend_ps;
|
||||
pass.init();
|
||||
pass.state_set(state);
|
||||
pass.shader_set(ShaderCache::get().layer_blend.get());
|
||||
pass.push_constant("blend_mode", int(layer.blend_mode));
|
||||
pass.push_constant("blend_opacity", layer_opacity);
|
||||
pass.bind_texture("color_buf", &inst->color_layer_tx);
|
||||
pass.bind_texture("reveal_buf", &inst->reveal_layer_tx);
|
||||
pass.bind_texture("mask_buf", (is_masked) ? &inst->mask_tx : &inst->dummy_tx);
|
||||
pass.state_stencil(0xFF, 0xFF, 0xFF);
|
||||
pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
|
||||
if (layer.blend_mode == GP_LAYER_BLEND_HARDLIGHT) {
|
||||
/* We cannot do custom blending on Multi-Target frame-buffers.
|
||||
* Workaround by doing 2 passes. */
|
||||
pass.state_set((state & ~DRW_STATE_BLEND_MUL) | DRW_STATE_BLEND_ADD_FULL);
|
||||
pass.push_constant("blend_mode", 999);
|
||||
pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
|
||||
inst->use_layer_fb = true;
|
||||
}
|
||||
|
||||
/* Geometry pass */
|
||||
{
|
||||
if (tgp_layer->geom_ps == nullptr) {
|
||||
tgp_layer->geom_ps = std::make_unique<PassSimple>("GPencil Layer");
|
||||
}
|
||||
|
||||
PassSimple &pass = *tgp_layer->geom_ps;
|
||||
|
||||
gpu::Texture **depth_tex = (is_in_front) ? &inst->dummy_depth : &inst->scene_depth_tx;
|
||||
gpu::Texture **mask_tex = (is_masked) ? &inst->mask_tx : &inst->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;
|
||||
|
||||
pass.state_set(state);
|
||||
pass.shader_set(ShaderCache::get().geometry.get());
|
||||
pass.bind_texture("gp_scene_depth_tx", depth_tex);
|
||||
pass.bind_texture("gp_mask_tx", mask_tex);
|
||||
pass.push_constant("gp_normal", tgp_ob->plane_normal);
|
||||
pass.push_constant("gp_stroke_order3d", tgp_ob->is_drawmode3d);
|
||||
pass.push_constant("gp_vertex_color_opacity", vert_col_opacity);
|
||||
|
||||
pass.bind_texture("gp_fill_tx", inst->dummy_tx);
|
||||
pass.bind_texture("gp_stroke_tx", inst->dummy_tx);
|
||||
|
||||
/* If random color type, need color by layer. */
|
||||
float4 gpl_color;
|
||||
copy_v4_v4(gpl_color, layer_tint);
|
||||
if (inst->v3d_color_type == V3D_SHADING_RANDOM_COLOR) {
|
||||
grease_pencil_layer_random_color_get(ob, layer, gpl_color);
|
||||
gpl_color[3] = 1.0f;
|
||||
}
|
||||
pass.push_constant("gp_layer_tint", gpl_color);
|
||||
|
||||
pass.push_constant("gp_layer_opacity", layer_alpha);
|
||||
pass.state_stencil(0xFF, 0xFF, 0xFF);
|
||||
}
|
||||
|
||||
return tgp_layer;
|
||||
}
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::draw::gpencil
|
||||
@@ -0,0 +1,28 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#define GPENCIL_MATERIAL_BUFFER_LEN 255
|
||||
|
||||
#define GPENCIL_LIGHT_BUFFER_LEN 128
|
||||
|
||||
/* High bits are used to pass material ID to fragment shader. */
|
||||
#define GPENCIL_MATID_SHIFT 19u
|
||||
|
||||
/* 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
|
||||
|
||||
#define GPENCIL_RENDER_FORMAT SFLOAT_16_16_16_16
|
||||
#define GPENCIL_ACCUM_FORMAT SFLOAT_16_16_16_16
|
||||
@@ -0,0 +1,485 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "DNA_light_types.h"
|
||||
#include "DNA_material_types.h"
|
||||
|
||||
#include "BKE_image.hh"
|
||||
#include "BKE_material.hh"
|
||||
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_memblock.h"
|
||||
|
||||
#include "GPU_uniform_buffer.hh"
|
||||
|
||||
#include "IMB_imbuf_types.hh"
|
||||
|
||||
#include "gpencil_engine_private.hh"
|
||||
|
||||
namespace blender::draw::gpencil {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Material
|
||||
* \{ */
|
||||
|
||||
static MaterialPool *gpencil_material_pool_add(Instance *inst)
|
||||
{
|
||||
MaterialPool *matpool = static_cast<MaterialPool *>(BLI_memblock_alloc(inst->gp_material_pool));
|
||||
matpool->next = nullptr;
|
||||
matpool->used_count = 0;
|
||||
if (matpool->ubo == nullptr) {
|
||||
matpool->ubo = GPU_uniformbuf_create(sizeof(matpool->mat_data));
|
||||
}
|
||||
inst->last_material_pool = matpool;
|
||||
return matpool;
|
||||
}
|
||||
|
||||
static gpu::Texture *gpencil_image_texture_get(blender::Image *image, bool *r_alpha_premult)
|
||||
{
|
||||
ImageUser iuser = {nullptr};
|
||||
gpu::Texture *gpu_tex = nullptr;
|
||||
|
||||
gpu_tex = BKE_image_get_gpu_texture(image, &iuser);
|
||||
*r_alpha_premult = (gpu_tex) ? (image->alpha_mode == IMA_ALPHA_PREMUL) : false;
|
||||
|
||||
return gpu_tex;
|
||||
}
|
||||
|
||||
static void gpencil_uv_transform_get(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. */
|
||||
rescale_m4(mat, float3{1.0f / scale[0], 1.0f / scale[1], 0.0});
|
||||
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]);
|
||||
}
|
||||
|
||||
static void gpencil_shade_color(float color[3])
|
||||
{
|
||||
/* This is scene refereed 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);
|
||||
}
|
||||
clamp_v3(color, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
/* Apply all overrides from the solid viewport mode to the GPencil material. */
|
||||
static MaterialGPencilStyle *gpencil_viewport_material_overrides(
|
||||
Instance *inst,
|
||||
Object *ob,
|
||||
int color_type,
|
||||
MaterialGPencilStyle *gp_style,
|
||||
const eV3DShadingLightingMode lighting_mode)
|
||||
{
|
||||
static MaterialGPencilStyle gp_style_tmp;
|
||||
|
||||
switch (color_type) {
|
||||
case V3D_SHADING_MATERIAL_COLOR:
|
||||
case V3D_SHADING_RANDOM_COLOR:
|
||||
/* Random uses a random color by layer and this is done using the tint
|
||||
* layer. A simple color by object, like meshes, is not practical in
|
||||
* grease pencil. */
|
||||
copy_v4_v4(gp_style_tmp.stroke_rgba, gp_style->stroke_rgba);
|
||||
copy_v4_v4(gp_style_tmp.fill_rgba, gp_style->fill_rgba);
|
||||
gp_style = &gp_style_tmp;
|
||||
gp_style->stroke_style = GP_MATERIAL_STROKE_STYLE_SOLID;
|
||||
gp_style->fill_style = GP_MATERIAL_FILL_STYLE_SOLID;
|
||||
break;
|
||||
case V3D_SHADING_TEXTURE_COLOR:
|
||||
gp_style_tmp = dna::shallow_copy(*gp_style);
|
||||
gp_style = &gp_style_tmp;
|
||||
if ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) && (gp_style->sima)) {
|
||||
copy_v4_fl(gp_style->stroke_rgba, 1.0f);
|
||||
gp_style->mix_stroke_factor = 0.0f;
|
||||
}
|
||||
|
||||
if ((gp_style->fill_style == GP_MATERIAL_FILL_STYLE_TEXTURE) && (gp_style->ima)) {
|
||||
copy_v4_fl(gp_style->fill_rgba, 1.0f);
|
||||
gp_style->mix_factor = 0.0f;
|
||||
}
|
||||
else if (gp_style->fill_style == GP_MATERIAL_FILL_STYLE_GRADIENT) {
|
||||
/* gp_style->fill_rgba is needed for correct gradient. */
|
||||
gp_style->mix_factor = 0.0f;
|
||||
}
|
||||
break;
|
||||
case V3D_SHADING_SINGLE_COLOR:
|
||||
gp_style = &gp_style_tmp;
|
||||
gp_style->stroke_style = GP_MATERIAL_STROKE_STYLE_SOLID;
|
||||
gp_style->fill_style = GP_MATERIAL_FILL_STYLE_SOLID;
|
||||
copy_v3_v3(gp_style->fill_rgba, inst->v3d_single_color);
|
||||
gp_style->fill_rgba[3] = 1.0f;
|
||||
copy_v4_v4(gp_style->stroke_rgba, gp_style->fill_rgba);
|
||||
if (lighting_mode != V3D_LIGHTING_FLAT) {
|
||||
gpencil_shade_color(gp_style->fill_rgba);
|
||||
}
|
||||
break;
|
||||
case V3D_SHADING_OBJECT_COLOR:
|
||||
gp_style = &gp_style_tmp;
|
||||
gp_style->stroke_style = GP_MATERIAL_STROKE_STYLE_SOLID;
|
||||
gp_style->fill_style = GP_MATERIAL_FILL_STYLE_SOLID;
|
||||
copy_v4_v4(gp_style->fill_rgba, ob->color);
|
||||
copy_v4_v4(gp_style->stroke_rgba, ob->color);
|
||||
if (lighting_mode != V3D_LIGHTING_FLAT) {
|
||||
gpencil_shade_color(gp_style->fill_rgba);
|
||||
}
|
||||
break;
|
||||
case V3D_SHADING_VERTEX_COLOR:
|
||||
gp_style = &gp_style_tmp;
|
||||
gp_style->stroke_style = GP_MATERIAL_STROKE_STYLE_SOLID;
|
||||
gp_style->fill_style = GP_MATERIAL_FILL_STYLE_SOLID;
|
||||
copy_v4_fl(gp_style->fill_rgba, 1.0f);
|
||||
copy_v4_fl(gp_style->stroke_rgba, 1.0f);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return gp_style;
|
||||
}
|
||||
|
||||
MaterialPool *gpencil_material_pool_create(Instance *inst,
|
||||
Object *ob,
|
||||
int *ofs,
|
||||
const bool is_vertex_mode)
|
||||
{
|
||||
MaterialPool *matpool = inst->last_material_pool;
|
||||
|
||||
int mat_len = BKE_object_material_used_with_fallback_eval(*ob);
|
||||
|
||||
bool reuse_matpool = matpool && ((matpool->used_count + mat_len) <= GPENCIL_MATERIAL_BUFFER_LEN);
|
||||
|
||||
if (reuse_matpool) {
|
||||
/* Share the matpool with other objects. Return offset to first material. */
|
||||
*ofs = matpool->used_count;
|
||||
}
|
||||
else {
|
||||
matpool = gpencil_material_pool_add(inst);
|
||||
*ofs = 0;
|
||||
}
|
||||
|
||||
/* Force vertex color in solid mode with vertex paint mode. Same behavior as meshes. */
|
||||
int color_type = (inst->v3d_color_type != -1 && is_vertex_mode) ? V3D_SHADING_VERTEX_COLOR :
|
||||
inst->v3d_color_type;
|
||||
const eV3DShadingLightingMode lighting_mode = eV3DShadingLightingMode(
|
||||
(inst->v3d != nullptr) ? eV3DShadingLightingMode(inst->v3d->shading.light) :
|
||||
V3D_LIGHTING_STUDIO);
|
||||
|
||||
MaterialPool *pool = matpool;
|
||||
for (int i = 0; i < mat_len; i++) {
|
||||
if ((i > 0) && (pool->used_count == GPENCIL_MATERIAL_BUFFER_LEN)) {
|
||||
pool->next = gpencil_material_pool_add(inst);
|
||||
pool = pool->next;
|
||||
}
|
||||
int mat_id = pool->used_count++;
|
||||
|
||||
gpMaterial *mat_data = &pool->mat_data[mat_id];
|
||||
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, i + 1);
|
||||
|
||||
if (gp_style->mode == GP_MATERIAL_MODE_LINE) {
|
||||
mat_data->flag = 0;
|
||||
}
|
||||
else {
|
||||
switch (gp_style->alignment_mode) {
|
||||
case GP_MATERIAL_FOLLOW_PATH:
|
||||
mat_data->flag = GP_STROKE_ALIGNMENT_STROKE;
|
||||
break;
|
||||
case GP_MATERIAL_FOLLOW_OBJ:
|
||||
mat_data->flag = GP_STROKE_ALIGNMENT_OBJECT;
|
||||
break;
|
||||
case GP_MATERIAL_FOLLOW_FIXED:
|
||||
default:
|
||||
mat_data->flag = GP_STROKE_ALIGNMENT_FIXED;
|
||||
break;
|
||||
}
|
||||
|
||||
if (gp_style->mode == GP_MATERIAL_MODE_DOT) {
|
||||
mat_data->flag |= GP_STROKE_DOTS;
|
||||
}
|
||||
|
||||
switch (gp_style->placement_mode) {
|
||||
case GP_MATERIAL_PLACEMENT_RADIUS:
|
||||
mat_data->flag |= GP_DOTS_PLACEMENT_MODE_RADIUS;
|
||||
break;
|
||||
case GP_MATERIAL_PLACEMENT_DENSITY:
|
||||
mat_data->flag |= GP_DOTS_PLACEMENT_MODE_DENSITY;
|
||||
break;
|
||||
default:
|
||||
case GP_MATERIAL_PLACEMENT_COUNT:
|
||||
mat_data->flag |= GP_DOTS_PLACEMENT_MODE_COUNT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((gp_style->mode != GP_MATERIAL_MODE_LINE) ||
|
||||
(gp_style->flag & GP_MATERIAL_DISABLE_STENCIL))
|
||||
{
|
||||
mat_data->flag |= GP_STROKE_OVERLAP;
|
||||
}
|
||||
|
||||
/* Material with holdout. */
|
||||
if (gp_style->flag & GP_MATERIAL_IS_STROKE_HOLDOUT) {
|
||||
mat_data->flag |= GP_STROKE_HOLDOUT;
|
||||
}
|
||||
if (gp_style->flag & GP_MATERIAL_IS_FILL_HOLDOUT) {
|
||||
mat_data->flag |= GP_FILL_HOLDOUT;
|
||||
}
|
||||
|
||||
/* Dots or Squares rotation. */
|
||||
mat_data->alignment_rot[0] = cosf(gp_style->alignment_rotation);
|
||||
mat_data->alignment_rot[1] = sinf(gp_style->alignment_rotation);
|
||||
if (gp_style->mode == GP_MATERIAL_MODE_LINE) {
|
||||
/* Convert pixel size to stroke u, the factor of `500` is from legacy Grease Pencil. */
|
||||
mat_data->stroke_u_scale = 500.0f / gp_style->texture_pixsize;
|
||||
}
|
||||
else {
|
||||
switch (gp_style->placement_mode) {
|
||||
case GP_MATERIAL_PLACEMENT_RADIUS:
|
||||
/* The radius spacing is a percentage and inverse, so it as a factor of `100` */
|
||||
mat_data->stroke_u_scale = 100.0f / gp_style->placement_radius_spacing;
|
||||
/* Divide by two, to convert diameter to radius. */
|
||||
mat_data->stroke_u_scale *= 0.5f;
|
||||
break;
|
||||
case GP_MATERIAL_PLACEMENT_DENSITY:
|
||||
mat_data->stroke_u_scale = gp_style->placement_density;
|
||||
break;
|
||||
default:
|
||||
case GP_MATERIAL_PLACEMENT_COUNT:
|
||||
mat_data->stroke_u_scale = gp_style->placement_count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (gp_style->flag & GP_MATERIAL_USE_DOTS_RANDOMIZATION) {
|
||||
mat_data->flag |= GP_DOTS_USE_RANDOMIZATION;
|
||||
|
||||
mat_data->random_packed.x = (unit_float_to_ushort_clamp(gp_style->random_size_factor));
|
||||
mat_data->random_packed.x |= (unit_float_to_ushort_clamp(gp_style->random_strength_factor))
|
||||
<< 16;
|
||||
|
||||
mat_data->random_packed.y = (unit_float_to_ushort_clamp(gp_style->random_rotation_factor));
|
||||
mat_data->random_packed.y |= (unit_float_to_ushort_clamp(gp_style->random_hue_factor)) << 16;
|
||||
|
||||
mat_data->random_packed.z = (unit_float_to_ushort_clamp(gp_style->random_saturation_factor));
|
||||
mat_data->random_packed.z |= (unit_float_to_ushort_clamp(gp_style->random_value_factor))
|
||||
<< 16;
|
||||
|
||||
mat_data->random_packed.w = float_as_uint(gp_style->random_noise_scale);
|
||||
}
|
||||
else {
|
||||
mat_data->random_packed = uint4(0);
|
||||
}
|
||||
|
||||
gp_style = gpencil_viewport_material_overrides(inst, ob, color_type, gp_style, lighting_mode);
|
||||
|
||||
/* Stroke Style */
|
||||
if ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) && (gp_style->sima)) {
|
||||
bool premul;
|
||||
pool->tex_stroke[mat_id] = gpencil_image_texture_get(gp_style->sima, &premul);
|
||||
mat_data->flag |= pool->tex_stroke[mat_id] ? GP_STROKE_TEXTURE_USE : GP_FLAG_NONE;
|
||||
mat_data->flag |= premul ? GP_STROKE_TEXTURE_PREMUL : GP_FLAG_NONE;
|
||||
copy_v4_v4(mat_data->stroke_color, gp_style->stroke_rgba);
|
||||
mat_data->stroke_texture_mix = 1.0f - gp_style->mix_stroke_factor;
|
||||
}
|
||||
else /* if (gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_SOLID) */ {
|
||||
pool->tex_stroke[mat_id] = nullptr;
|
||||
mat_data->flag &= ~GP_STROKE_TEXTURE_USE;
|
||||
copy_v4_v4(mat_data->stroke_color, gp_style->stroke_rgba);
|
||||
mat_data->stroke_texture_mix = 0.0f;
|
||||
}
|
||||
|
||||
/* Fill Style */
|
||||
if ((gp_style->fill_style == GP_MATERIAL_FILL_STYLE_TEXTURE) && (gp_style->ima)) {
|
||||
bool use_clip = (gp_style->flag & GP_MATERIAL_TEX_CLAMP) != 0;
|
||||
bool premul;
|
||||
pool->tex_fill[mat_id] = gpencil_image_texture_get(gp_style->ima, &premul);
|
||||
mat_data->flag |= pool->tex_fill[mat_id] ? GP_FILL_TEXTURE_USE : GP_FLAG_NONE;
|
||||
mat_data->flag |= premul ? GP_FILL_TEXTURE_PREMUL : GP_FLAG_NONE;
|
||||
mat_data->flag |= use_clip ? GP_FILL_TEXTURE_CLIP : GP_FLAG_NONE;
|
||||
gpencil_uv_transform_get(gp_style->texture_offset,
|
||||
gp_style->texture_scale,
|
||||
gp_style->texture_angle,
|
||||
reinterpret_cast<float (*)[2]>(&mat_data->fill_uv_rot_scale),
|
||||
mat_data->fill_uv_offset);
|
||||
copy_v4_v4(mat_data->fill_color, gp_style->fill_rgba);
|
||||
mat_data->fill_texture_mix = 1.0f - gp_style->mix_factor;
|
||||
}
|
||||
else if (gp_style->fill_style == GP_MATERIAL_FILL_STYLE_GRADIENT) {
|
||||
bool use_radial = (gp_style->gradient_type == GP_MATERIAL_GRADIENT_RADIAL);
|
||||
pool->tex_fill[mat_id] = nullptr;
|
||||
mat_data->flag |= GP_FILL_GRADIENT_USE;
|
||||
mat_data->flag |= use_radial ? GP_FILL_GRADIENT_RADIAL : GP_FLAG_NONE;
|
||||
gpencil_uv_transform_get(gp_style->texture_offset,
|
||||
gp_style->texture_scale,
|
||||
gp_style->texture_angle,
|
||||
reinterpret_cast<float (*)[2]>(&mat_data->fill_uv_rot_scale),
|
||||
mat_data->fill_uv_offset);
|
||||
copy_v4_v4(mat_data->fill_color, gp_style->fill_rgba);
|
||||
copy_v4_v4(mat_data->fill_mix_color, gp_style->mix_rgba);
|
||||
mat_data->fill_texture_mix = 1.0f - gp_style->mix_factor;
|
||||
if (gp_style->flag & GP_MATERIAL_FLIP_FILL) {
|
||||
swap_v4_v4(mat_data->fill_color, mat_data->fill_mix_color);
|
||||
}
|
||||
}
|
||||
else /* if (gp_style->fill_style == GP_MATERIAL_FILL_STYLE_SOLID) */ {
|
||||
pool->tex_fill[mat_id] = nullptr;
|
||||
copy_v4_v4(mat_data->fill_color, gp_style->fill_rgba);
|
||||
mat_data->fill_texture_mix = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return matpool;
|
||||
}
|
||||
|
||||
void gpencil_material_resources_get(MaterialPool *first_pool,
|
||||
int mat_id,
|
||||
gpu::Texture **r_tex_stroke,
|
||||
gpu::Texture **r_tex_fill,
|
||||
gpu::UniformBuf **r_ubo_mat)
|
||||
{
|
||||
MaterialPool *matpool = first_pool;
|
||||
BLI_assert(mat_id >= 0);
|
||||
int pool_id = mat_id / GPENCIL_MATERIAL_BUFFER_LEN;
|
||||
for (int i = 0; i < pool_id; i++) {
|
||||
matpool = matpool->next;
|
||||
}
|
||||
mat_id = mat_id % GPENCIL_MATERIAL_BUFFER_LEN;
|
||||
*r_ubo_mat = matpool->ubo;
|
||||
if (r_tex_fill) {
|
||||
*r_tex_fill = matpool->tex_fill[mat_id];
|
||||
}
|
||||
if (r_tex_stroke) {
|
||||
*r_tex_stroke = matpool->tex_stroke[mat_id];
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Lights
|
||||
* \{ */
|
||||
|
||||
LightPool *gpencil_light_pool_add(Instance *inst)
|
||||
{
|
||||
LightPool *lightpool = static_cast<LightPool *>(BLI_memblock_alloc(inst->gp_light_pool));
|
||||
lightpool->light_used = 0;
|
||||
/* Tag light list end. */
|
||||
lightpool->light_data[0].light_color[0] = -1.0;
|
||||
if (lightpool->ubo == nullptr) {
|
||||
lightpool->ubo = GPU_uniformbuf_create(sizeof(lightpool->light_data));
|
||||
}
|
||||
inst->last_light_pool = lightpool;
|
||||
return lightpool;
|
||||
}
|
||||
|
||||
void gpencil_light_ambient_add(LightPool *lightpool, const float color[3])
|
||||
{
|
||||
if (lightpool->light_used >= GPENCIL_LIGHT_BUFFER_LEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpLight *gp_light = &lightpool->light_data[lightpool->light_used];
|
||||
gp_light->type = GP_LIGHT_TYPE_AMBIENT;
|
||||
copy_v3_v3(gp_light->light_color, color);
|
||||
lightpool->light_used++;
|
||||
|
||||
if (lightpool->light_used < GPENCIL_LIGHT_BUFFER_LEN) {
|
||||
/* Tag light list end. */
|
||||
gp_light[1].light_color[0] = -1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
static float light_power_get(const Light *la)
|
||||
{
|
||||
if (la->type == LA_AREA) {
|
||||
return 1.0f / (4.0f * M_PI);
|
||||
}
|
||||
if (ELEM(la->type, LA_SPOT, LA_LOCAL)) {
|
||||
return 1.0f / (4.0f * M_PI * M_PI);
|
||||
}
|
||||
|
||||
return 1.0f / M_PI;
|
||||
}
|
||||
|
||||
void gpencil_light_pool_populate(LightPool *lightpool, Object *ob)
|
||||
{
|
||||
Light &light = DRW_object_get_data_for_drawing<Light>(*ob);
|
||||
|
||||
if (lightpool->light_used >= GPENCIL_LIGHT_BUFFER_LEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpLight *gp_light = &lightpool->light_data[lightpool->light_used];
|
||||
float (*mat)[4] = reinterpret_cast<float (*)[4]>(&gp_light->right);
|
||||
|
||||
if (light.type == LA_SPOT) {
|
||||
copy_m4_m4(mat, ob->world_to_object().ptr());
|
||||
gp_light->type = GP_LIGHT_TYPE_SPOT;
|
||||
gp_light->spot_size = cosf(light.spotsize * 0.5f);
|
||||
gp_light->spot_blend = (1.0f - gp_light->spot_size) * light.spotblend;
|
||||
}
|
||||
else if (light.type == LA_AREA) {
|
||||
/* Simulate area lights using a spot light. */
|
||||
normalize_m4_m4(mat, ob->object_to_world().ptr());
|
||||
invert_m4(mat);
|
||||
gp_light->type = GP_LIGHT_TYPE_SPOT;
|
||||
gp_light->spot_size = cosf(M_PI_2);
|
||||
gp_light->spot_blend = (1.0f - gp_light->spot_size) * 1.0f;
|
||||
}
|
||||
else if (light.type == LA_SUN) {
|
||||
normalize_v3_v3(gp_light->forward, ob->object_to_world().ptr()[2]);
|
||||
gp_light->type = GP_LIGHT_TYPE_SUN;
|
||||
}
|
||||
else {
|
||||
gp_light->type = GP_LIGHT_TYPE_POINT;
|
||||
}
|
||||
copy_v4_v4(gp_light->position, ob->object_to_world().location());
|
||||
copy_v3_v3(gp_light->light_color, &light.r);
|
||||
mul_v3_fl(gp_light->light_color, light.energy * light_power_get(&light));
|
||||
|
||||
lightpool->light_used++;
|
||||
|
||||
if (lightpool->light_used < GPENCIL_LIGHT_BUFFER_LEN) {
|
||||
/* Tag light list end. */
|
||||
gp_light[1].light_color[0] = -1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
LightPool *gpencil_light_pool_create(Instance *inst, Object * /*ob*/)
|
||||
{
|
||||
LightPool *lightpool = inst->last_light_pool;
|
||||
|
||||
if (lightpool == nullptr) {
|
||||
lightpool = gpencil_light_pool_add(inst);
|
||||
}
|
||||
/* TODO(fclem): Light linking. */
|
||||
// gpencil_light_pool_populate(lightpool, ob);
|
||||
|
||||
return lightpool;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::draw::gpencil
|
||||
@@ -0,0 +1,22 @@
|
||||
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DRW_render.hh"
|
||||
|
||||
namespace blender::draw::gpencil {
|
||||
|
||||
struct Engine : public DrawEngine::Pointer {
|
||||
DrawEngine *create_instance() final;
|
||||
|
||||
static void render_to_image(RenderEngine *engine, RenderLayer *render_layer, const rcti rect);
|
||||
static void free_static();
|
||||
};
|
||||
|
||||
} // namespace blender::draw::gpencil
|
||||
@@ -0,0 +1,943 @@
|
||||
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
#include "DRW_engine.hh"
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "BKE_compositor.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_gpencil_legacy.h"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_material.hh"
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_paint.hh"
|
||||
#include "BKE_shader_fx.hh"
|
||||
|
||||
#include "BKE_camera.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_memblock.h"
|
||||
#include "BLI_virtual_array.hh"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_material_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
#include "DNA_world_types.h"
|
||||
|
||||
#include "GPU_texture.hh"
|
||||
#include "GPU_uniform_buffer.hh"
|
||||
|
||||
#include "draw_cache.hh"
|
||||
#include "draw_manager.hh"
|
||||
#include "draw_view.hh"
|
||||
|
||||
#include "gpencil_engine.hh"
|
||||
#include "gpencil_engine_private.hh"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "ED_grease_pencil.hh"
|
||||
#include "ED_screen.hh"
|
||||
#include "ED_view3d.hh"
|
||||
|
||||
#include "GPU_debug.hh"
|
||||
|
||||
namespace blender::draw::gpencil {
|
||||
|
||||
void Instance::init()
|
||||
{
|
||||
this->draw_ctx = DRW_context_get();
|
||||
|
||||
const View3D *v3d = draw_ctx->v3d;
|
||||
|
||||
if (!dummy_texture.is_valid()) {
|
||||
const float pixels[1][4] = {{1.0f, 0.0f, 1.0f, 1.0f}};
|
||||
dummy_texture.ensure_2d(
|
||||
gpu::TextureFormat::UNORM_8_8_8_8, int2(1), GPU_TEXTURE_USAGE_SHADER_READ, &pixels[0][0]);
|
||||
}
|
||||
if (!dummy_depth.is_valid()) {
|
||||
const float pixels[1] = {1.0f};
|
||||
dummy_depth.ensure_2d(
|
||||
gpu::TextureFormat::SFLOAT_32_DEPTH, int2(1), GPU_TEXTURE_USAGE_SHADER_READ, &pixels[0]);
|
||||
}
|
||||
|
||||
/* Resize and reset memory-blocks. */
|
||||
BLI_memblock_clear(this->gp_light_pool, light_pool_free);
|
||||
BLI_memblock_clear(this->gp_material_pool, material_pool_free);
|
||||
BLI_memblock_clear(this->gp_object_pool, nullptr);
|
||||
this->gp_layer_pool->clear();
|
||||
this->gp_vfx_pool->clear();
|
||||
BLI_memblock_clear(this->gp_maskbit_pool, nullptr);
|
||||
|
||||
this->view_layer = draw_ctx->view_layer;
|
||||
this->scene = draw_ctx->scene;
|
||||
this->v3d = draw_ctx->v3d;
|
||||
this->last_light_pool = nullptr;
|
||||
this->last_material_pool = nullptr;
|
||||
this->tobjects.first = nullptr;
|
||||
this->tobjects.last = nullptr;
|
||||
this->tobjects_infront.first = nullptr;
|
||||
this->tobjects_infront.last = nullptr;
|
||||
this->sbuffer_tobjects.first = nullptr;
|
||||
this->sbuffer_tobjects.last = nullptr;
|
||||
this->dummy_tx = this->dummy_texture;
|
||||
this->draw_wireframe = (v3d && v3d->shading.type == OB_WIRE);
|
||||
this->scene_depth_tx = nullptr;
|
||||
this->scene_fb = nullptr;
|
||||
this->is_render = this->render_depth_tx.is_valid() || (v3d && v3d->shading.type == OB_RENDER);
|
||||
this->is_viewport = (v3d != nullptr);
|
||||
this->global_light_pool = gpencil_light_pool_add(this);
|
||||
this->shadeless_light_pool = gpencil_light_pool_add(this);
|
||||
/* Small HACK: we don't want the global pool to be reused,
|
||||
* so we set the last light pool to nullptr. */
|
||||
this->last_light_pool = nullptr;
|
||||
this->is_sorted = false;
|
||||
|
||||
bool use_scene_lights = false;
|
||||
bool use_scene_world = false;
|
||||
|
||||
if (v3d) {
|
||||
use_scene_lights = V3D_USES_SCENE_LIGHTS(v3d);
|
||||
|
||||
use_scene_world = V3D_USES_SCENE_WORLD(v3d);
|
||||
|
||||
this->v3d_color_type = (v3d->shading.type == OB_SOLID) ? v3d->shading.color_type : -1;
|
||||
/* Special case: If we're in Vertex Paint mode, enforce #V3D_SHADING_VERTEX_COLOR setting. */
|
||||
if (v3d->shading.type == OB_SOLID && draw_ctx->obact &&
|
||||
(draw_ctx->obact->mode & OB_MODE_VERTEX_GREASE_PENCIL) != 0)
|
||||
{
|
||||
this->v3d_color_type = V3D_SHADING_VERTEX_COLOR;
|
||||
}
|
||||
|
||||
copy_v3_v3(this->v3d_single_color, v3d->shading.single_color);
|
||||
|
||||
/* For non active frame, use only lines in multiedit mode. */
|
||||
const bool overlays_on = (v3d->flag2 & V3D_HIDE_OVERLAYS) == 0;
|
||||
this->use_multiedit_lines_only = overlays_on &&
|
||||
(v3d->gp_flag & V3D_GP_SHOW_MULTIEDIT_LINES) != 0;
|
||||
|
||||
const bool shmode_xray_support = v3d->shading.type <= OB_SOLID;
|
||||
this->xray_alpha = (shmode_xray_support && XRAY_ENABLED(v3d)) ? XRAY_ALPHA(v3d) : 1.0f;
|
||||
this->force_stroke_order_3d = v3d->gp_flag & V3D_GP_FORCE_STROKE_ORDER_3D;
|
||||
}
|
||||
else if (this->is_render) {
|
||||
use_scene_lights = true;
|
||||
use_scene_world = true;
|
||||
this->use_multiedit_lines_only = false;
|
||||
this->xray_alpha = 1.0f;
|
||||
this->v3d_color_type = -1;
|
||||
this->force_stroke_order_3d = false;
|
||||
}
|
||||
|
||||
this->use_lighting = (v3d && v3d->shading.type > OB_SOLID) || this->is_render;
|
||||
this->use_lights = use_scene_lights;
|
||||
|
||||
gpencil_light_ambient_add(this->shadeless_light_pool, float3{1.0f, 1.0f, 1.0f});
|
||||
|
||||
World *world = draw_ctx->scene->world;
|
||||
if (world != nullptr && use_scene_world) {
|
||||
gpencil_light_ambient_add(this->global_light_pool, &world->horr);
|
||||
}
|
||||
else if (v3d) {
|
||||
float world_light[3];
|
||||
copy_v3_fl(world_light, v3d->shading.studiolight_intensity);
|
||||
gpencil_light_ambient_add(this->global_light_pool, world_light);
|
||||
}
|
||||
|
||||
float4x4 viewmatinv = View::default_get().viewinv();
|
||||
copy_v3_v3(this->camera_z_axis, viewmatinv[2]);
|
||||
copy_v3_v3(this->camera_pos, viewmatinv[3]);
|
||||
this->camera_z_offset = dot_v3v3(viewmatinv[3], viewmatinv[2]);
|
||||
|
||||
if (draw_ctx && draw_ctx->rv3d && v3d) {
|
||||
this->camera = (draw_ctx->rv3d->persp == RV3D_CAMOB) ? v3d->camera : nullptr;
|
||||
}
|
||||
else {
|
||||
this->camera = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::begin_sync()
|
||||
{
|
||||
this->cfra = int(DEG_get_ctime(draw_ctx->depsgraph));
|
||||
this->simplify_antialias = GPENCIL_SIMPLIFY_AA(draw_ctx->scene);
|
||||
this->use_layer_fb = false;
|
||||
this->use_object_fb = false;
|
||||
this->use_mask_fb = false;
|
||||
|
||||
const bool use_viewport_compositor = draw_ctx->is_viewport_compositor_enabled();
|
||||
const Set<std::string> needed_passes = bke::compositor::get_used_passes(*scene, view_layer);
|
||||
this->need_combined_pass = use_viewport_compositor &&
|
||||
needed_passes.contains(RE_PASSNAME_COMBINED);
|
||||
this->need_grease_pencil_pass = use_viewport_compositor &&
|
||||
needed_passes.contains(RE_PASSNAME_GREASE_PENCIL);
|
||||
this->use_signed_fb = !this->is_viewport;
|
||||
|
||||
if (draw_ctx->v3d) {
|
||||
const bool hide_overlay = ((draw_ctx->v3d->flag2 & V3D_HIDE_OVERLAYS) != 0);
|
||||
const bool show_onion = ((draw_ctx->v3d->gp_flag & V3D_GP_SHOW_ONION_SKIN) != 0);
|
||||
const bool playing = (draw_ctx->evil_C != nullptr) ?
|
||||
ED_screen_animation_playing(CTX_wm_manager(draw_ctx->evil_C)) !=
|
||||
nullptr :
|
||||
false;
|
||||
this->do_onion = show_onion && !hide_overlay && !playing;
|
||||
this->do_onion_only_active_object = ((draw_ctx->v3d->gp_flag &
|
||||
V3D_GP_ONION_SKIN_ACTIVE_OBJECT) != 0);
|
||||
this->playing = playing;
|
||||
/* Save simplify flags (can change while drawing, so it's better to save). */
|
||||
Scene *scene = draw_ctx->scene;
|
||||
this->simplify_fill = GPENCIL_SIMPLIFY_FILL(scene, playing);
|
||||
this->simplify_fx = GPENCIL_SIMPLIFY_FX(scene, playing) ||
|
||||
(draw_ctx->v3d->shading.type < OB_RENDER);
|
||||
|
||||
/* Fade Layer. */
|
||||
const bool is_fade_layer = ((!hide_overlay) && (!this->is_render) &&
|
||||
(draw_ctx->v3d->gp_flag & V3D_GP_FADE_NOACTIVE_LAYERS));
|
||||
this->fade_layer_opacity = (is_fade_layer) ? draw_ctx->v3d->overlay.gpencil_fade_layer : -1.0f;
|
||||
this->vertex_paint_opacity = draw_ctx->v3d->overlay.gpencil_vertex_paint_opacity;
|
||||
/* Fade GPencil Objects. */
|
||||
const bool is_fade_object = ((!hide_overlay) && (!this->is_render) &&
|
||||
(draw_ctx->v3d->gp_flag & V3D_GP_FADE_OBJECTS) &&
|
||||
(draw_ctx->v3d->gp_flag & V3D_GP_FADE_NOACTIVE_GPENCIL));
|
||||
this->fade_gp_object_opacity = (is_fade_object) ?
|
||||
draw_ctx->v3d->overlay.gpencil_paper_opacity :
|
||||
-1.0f;
|
||||
this->fade_3d_object_opacity = ((!hide_overlay) && (!this->is_render) &&
|
||||
(draw_ctx->v3d->gp_flag & V3D_GP_FADE_OBJECTS)) ?
|
||||
draw_ctx->v3d->overlay.gpencil_paper_opacity :
|
||||
-1.0f;
|
||||
}
|
||||
else {
|
||||
this->do_onion = true;
|
||||
Scene *scene = draw_ctx->scene;
|
||||
this->simplify_fill = GPENCIL_SIMPLIFY_FILL(scene, false);
|
||||
this->simplify_fx = GPENCIL_SIMPLIFY_FX(scene, false);
|
||||
this->fade_layer_opacity = -1.0f;
|
||||
this->playing = false;
|
||||
}
|
||||
|
||||
{
|
||||
this->stroke_batch = nullptr;
|
||||
this->fill_batch = nullptr;
|
||||
|
||||
this->obact = draw_ctx->obact;
|
||||
}
|
||||
|
||||
/* Free unneeded buffers. */
|
||||
this->snapshot_depth_tx.free();
|
||||
this->snapshot_color_tx.free();
|
||||
this->snapshot_reveal_tx.free();
|
||||
|
||||
{
|
||||
PassSimple &pass = this->merge_depth_ps;
|
||||
pass.init();
|
||||
pass.state_set(DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS);
|
||||
pass.shader_set(ShaderCache::get().depth_merge.get());
|
||||
pass.bind_texture("depth_buf", &this->depth_tx);
|
||||
pass.push_constant("stroke_order3d", &this->is_stroke_order_3d);
|
||||
pass.push_constant("gp_model_matrix", &this->object_bound_mat);
|
||||
pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
{
|
||||
/* Merges the object's depth to the viewport compositor depth pass. */
|
||||
PassSimple &pass = this->merge_depth_pass_ps;
|
||||
pass.init();
|
||||
pass.state_set(DRW_STATE_WRITE_COLOR);
|
||||
pass.shader_set(ShaderCache::get().depth_pass_merge.get());
|
||||
pass.bind_texture("depth_buf", &this->depth_tx);
|
||||
pass.bind_image("depth_pass_img", &this->depth_pass_img);
|
||||
pass.push_constant("stroke_order3d", &this->is_stroke_order_3d);
|
||||
pass.push_constant("gp_model_matrix", &this->object_bound_mat);
|
||||
pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
{
|
||||
PassSimple &pass = this->mask_invert_ps;
|
||||
pass.init();
|
||||
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_LOGIC_INVERT);
|
||||
pass.shader_set(ShaderCache::get().mask_invert.get());
|
||||
pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
|
||||
Camera *cam = id_cast<Camera *>(
|
||||
(this->camera != nullptr && this->camera->type == OB_CAMERA) ? this->camera->data : nullptr);
|
||||
|
||||
/* Pseudo DOF setup. */
|
||||
if (cam && (cam->dof.flag & CAM_DOF_ENABLED)) {
|
||||
const float2 vp_size = draw_ctx->viewport_size_get();
|
||||
float fstop = cam->dof.aperture_fstop;
|
||||
float sensor = BKE_camera_sensor_size(cam->sensor_fit, cam->sensor_x, cam->sensor_y);
|
||||
float focus_dist = BKE_camera_object_dof_distance(this->camera);
|
||||
float focal_len = cam->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 (draw_ctx->rv3d != nullptr) {
|
||||
sensor_scaled *= draw_ctx->rv3d->viewcamtexcofac[0];
|
||||
}
|
||||
|
||||
this->dof_params[1] = aperture * fabsf(focal_len_scaled / (focus_dist - focal_len_scaled));
|
||||
this->dof_params[1] *= vp_size[0] / sensor_scaled;
|
||||
this->dof_params[0] = -focus_dist * this->dof_params[1];
|
||||
}
|
||||
else {
|
||||
/* Disable DoF blur scaling. */
|
||||
this->camera = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
#define DISABLE_BATCHING 0
|
||||
|
||||
bool Instance::is_used_as_layer_mask_in_viewlayer(const GreasePencil &grease_pencil,
|
||||
const bke::greasepencil::Layer &mask_layer,
|
||||
const ViewLayer &view_layer)
|
||||
{
|
||||
using namespace bke::greasepencil;
|
||||
for (const Layer *layer : grease_pencil.layers()) {
|
||||
if (layer->view_layer_name().is_empty() ||
|
||||
!STREQ(view_layer.name, layer->view_layer_name().c_str()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((layer->base.flag & GP_LAYER_TREE_NODE_DISABLE_MASKS_IN_VIEWLAYER) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (GreasePencilLayerMask &mask : layer->masks) {
|
||||
if (STREQ(mask.layer_name, mask_layer.name().c_str())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Instance::use_layer_in_render(const GreasePencil &grease_pencil,
|
||||
const bke::greasepencil::Layer &layer,
|
||||
const ViewLayer &view_layer,
|
||||
bool &r_is_used_as_mask)
|
||||
{
|
||||
if (!layer.view_layer_name().is_empty() &&
|
||||
!STREQ(view_layer.name, layer.view_layer_name().c_str()))
|
||||
{
|
||||
/* Do not skip layers that are masks when rendering the viewlayer so that it can still be used
|
||||
* to clip/mask other layers. */
|
||||
if (is_used_as_layer_mask_in_viewlayer(grease_pencil, layer, view_layer)) {
|
||||
r_is_used_as_mask = true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
tObject *Instance::object_sync_do(Object *ob, ResourceHandleRange res_handle)
|
||||
{
|
||||
using namespace ed::greasepencil;
|
||||
using namespace bke::greasepencil;
|
||||
GreasePencil &grease_pencil = DRW_object_get_data_for_drawing<GreasePencil>(*ob);
|
||||
const bool is_vertex_mode = (ob->mode & OB_MODE_VERTEX_PAINT) != 0;
|
||||
const Bounds<float3> bounds = grease_pencil.bounds_min_max_eval().value_or(Bounds(float3(0)));
|
||||
|
||||
const bool do_onion = !this->is_render && this->do_onion &&
|
||||
(this->do_onion_only_active_object ? this->obact == ob : true);
|
||||
const bool do_multi_frame = (((this->scene->toolsettings->gpencil_flags &
|
||||
GP_USE_MULTI_FRAME_EDITING) != 0) &&
|
||||
(ob->mode != OB_MODE_OBJECT));
|
||||
const bool use_stroke_order_3d = this->force_stroke_order_3d ||
|
||||
((grease_pencil.flag & GREASE_PENCIL_STROKE_ORDER_3D) != 0);
|
||||
tObject *tgp_ob = gpencil_object_cache_add(this, ob, use_stroke_order_3d, bounds);
|
||||
|
||||
int mat_ofs = 0;
|
||||
MaterialPool *matpool = gpencil_material_pool_create(this, ob, &mat_ofs, is_vertex_mode);
|
||||
|
||||
gpu::Texture *tex_fill = this->dummy_tx;
|
||||
gpu::Texture *tex_stroke = this->dummy_tx;
|
||||
|
||||
gpu::Batch *iter_geom = nullptr;
|
||||
PassSimple *last_pass = nullptr;
|
||||
int vfirst = 0;
|
||||
int vcount = 0;
|
||||
|
||||
const auto drawcall_flush = [&](PassSimple &pass) {
|
||||
#if !DISABLE_BATCHING
|
||||
if (iter_geom != nullptr) {
|
||||
pass.draw(iter_geom, 1, vcount, vfirst, res_handle);
|
||||
}
|
||||
#endif
|
||||
iter_geom = nullptr;
|
||||
vfirst = -1;
|
||||
vcount = 0;
|
||||
};
|
||||
|
||||
const auto drawcall_add =
|
||||
[&](PassSimple &pass, gpu::Batch *draw_geom, const int v_first, const int v_count) {
|
||||
#if DISABLE_BATCHING
|
||||
pass.draw(iter_geom, 1, vcount, vfirst, res_handle);
|
||||
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(pass);
|
||||
}
|
||||
iter_geom = draw_geom;
|
||||
if (vfirst == -1) {
|
||||
vfirst = v_first;
|
||||
}
|
||||
vcount = v_first + v_count - vfirst;
|
||||
};
|
||||
|
||||
int t_offset = 0;
|
||||
/* Note that we loop over all the drawings (including the onion skinned ones) to make sure we
|
||||
* match the offsets of the batch cache. */
|
||||
const Vector<DrawingInfo> drawings = retrieve_visible_drawings(
|
||||
*this->scene, grease_pencil, true);
|
||||
const Span<const Layer *> layers = grease_pencil.layers();
|
||||
for (const DrawingInfo info : drawings) {
|
||||
const Layer &layer = *layers[info.layer_index];
|
||||
|
||||
const std::optional<GroupedSpan<int3>> triangles = info.drawing.triangles();
|
||||
const bke::CurvesGeometry &curves = info.drawing.strokes();
|
||||
const OffsetIndices<int> points_by_curve = curves.evaluated_points_by_curve();
|
||||
const bke::AttributeAccessor attributes = curves.attributes();
|
||||
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);
|
||||
const IndexMask visible_fills = ed::greasepencil::retrieve_visible_fills(
|
||||
*ob, info.drawing, memory);
|
||||
const std::optional<GroupedSpan<int>> fills = info.drawing.fills();
|
||||
const int num_fills = fills.has_value() ? fills->size() : 0;
|
||||
|
||||
/* Precompute all the triangle and vertex counts.
|
||||
* In case the drawing should not be rendered, we need to compute the offset where the next
|
||||
* drawing begins. */
|
||||
Array<int> num_triangles_per_fill(num_fills);
|
||||
Array<int> num_vertices_per_curve(curves.curves_num());
|
||||
int total_num_triangles = 0;
|
||||
int total_num_vertices = 0;
|
||||
if (triangles) {
|
||||
visible_fills.foreach_index([&](const int fill_index) {
|
||||
const int num_stroke_triangles = (*triangles)[fill_index].size();
|
||||
num_triangles_per_fill[fill_index] = num_stroke_triangles;
|
||||
total_num_triangles += num_stroke_triangles;
|
||||
});
|
||||
}
|
||||
|
||||
visible_strokes.foreach_index([&](const int curve_i) {
|
||||
const IndexRange points = points_by_curve[curve_i];
|
||||
const int num_stroke_vertices = (points.size() +
|
||||
int(cyclic[curve_i] && (points.size() >= 3)));
|
||||
num_vertices_per_curve[curve_i] = num_stroke_vertices;
|
||||
total_num_vertices += num_stroke_vertices;
|
||||
});
|
||||
|
||||
bool is_layer_used_as_mask = false;
|
||||
const bool show_drawing_in_render = use_layer_in_render(
|
||||
grease_pencil, layer, *this->view_layer, is_layer_used_as_mask);
|
||||
if (!show_drawing_in_render) {
|
||||
/* Skip over the entire drawing. */
|
||||
t_offset += total_num_triangles;
|
||||
t_offset += total_num_vertices * 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (last_pass) {
|
||||
drawcall_flush(*last_pass);
|
||||
}
|
||||
|
||||
tLayer *tgp_layer = grease_pencil_layer_cache_add(
|
||||
this, ob, layer, info.onion_id, is_layer_used_as_mask, tgp_ob);
|
||||
PassSimple &pass = *tgp_layer->geom_ps;
|
||||
last_pass = &pass;
|
||||
|
||||
const bool use_lights = this->use_lighting &&
|
||||
((layer.base.flag & GP_LAYER_TREE_NODE_USE_LIGHTS) != 0) &&
|
||||
(ob->dtx & OB_USE_GPENCIL_LIGHTS);
|
||||
|
||||
gpu::UniformBuf *lights_ubo = (use_lights) ? this->global_light_pool->ubo :
|
||||
this->shadeless_light_pool->ubo;
|
||||
|
||||
gpu::UniformBuf *ubo_mat;
|
||||
gpencil_material_resources_get(matpool, 0, nullptr, nullptr, &ubo_mat);
|
||||
|
||||
pass.bind_ubo("gp_lights", lights_ubo);
|
||||
pass.bind_ubo("gp_materials", ubo_mat);
|
||||
pass.bind_texture("gp_fill_tx", tex_fill);
|
||||
pass.bind_texture("gp_stroke_tx", tex_stroke);
|
||||
pass.push_constant("gp_material_offset", mat_ofs);
|
||||
/* Since we don't use the sbuffer in GPv3, this is always 0. */
|
||||
pass.push_constant("gp_stroke_index_offset", 0.0f);
|
||||
pass.push_constant("viewport_size", float2(draw_ctx->viewport_size_get()));
|
||||
|
||||
const VArray<int> stroke_materials = *attributes.lookup_or_default<int>(
|
||||
"material_index", bke::AttrDomain::Curve, 0);
|
||||
const VArray<bool> is_fill_guide = *attributes.lookup_or_default<bool>(
|
||||
".is_fill_guide", bke::AttrDomain::Curve, false);
|
||||
|
||||
const VArray<bool> hide_stroke = *attributes.lookup_or_default<bool>(
|
||||
"hide_stroke", bke::AttrDomain::Curve, false);
|
||||
const VArray<int> fill_ids = *attributes.lookup_or_default<int>(
|
||||
"fill_id", bke::AttrDomain::Curve, 0);
|
||||
|
||||
const bool only_lines = !ELEM(ob->mode,
|
||||
OB_MODE_PAINT_GREASE_PENCIL,
|
||||
OB_MODE_WEIGHT_GREASE_PENCIL,
|
||||
OB_MODE_VERTEX_GREASE_PENCIL) &&
|
||||
info.frame_number != this->cfra && this->use_multiedit_lines_only &&
|
||||
do_multi_frame;
|
||||
const bool is_onion = info.onion_id != 0;
|
||||
|
||||
int fill_index = 0;
|
||||
|
||||
Array<int> fill_index_by_curves(curves.curves_num(), -1);
|
||||
Array<int> first_curves(curves.curves_num());
|
||||
array_utils::fill_index_range<int>(first_curves);
|
||||
|
||||
for (const int curve_i : curves.curves_range()) {
|
||||
const bool is_filled = fill_ids[curve_i] != 0;
|
||||
const bool active_filled = is_filled && (fill_index_by_curves[curve_i] == -1);
|
||||
|
||||
/* Keep track of already rendered fills. */
|
||||
if (active_filled) {
|
||||
const Span<int> fill = (*fills)[fill_index];
|
||||
const int first_curve = fill.first();
|
||||
for (const int pos : fill.index_range()) {
|
||||
const int curve_i = fill[pos];
|
||||
fill_index_by_curves[curve_i] = fill_index;
|
||||
first_curves[curve_i] = first_curve;
|
||||
}
|
||||
|
||||
fill_index++;
|
||||
}
|
||||
}
|
||||
|
||||
visible_strokes.foreach_index([&](const int curve_i) {
|
||||
/* Will be `-1` if not a fill. */
|
||||
const int fill_index = fill_index_by_curves[curve_i];
|
||||
|
||||
const bool is_filled = fill_index != -1;
|
||||
const bool active_filled = is_filled && (first_curves[curve_i] == curve_i);
|
||||
|
||||
/* The material index is allowed to be negative as it's stored as a generic attribute. We
|
||||
* clamp it here to avoid crashing in the rendering code. Any stroke with a material < 0 will
|
||||
* use the first material in the first material slot. */
|
||||
const int material_index = std::max(stroke_materials[curve_i], 0);
|
||||
const MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, material_index + 1);
|
||||
|
||||
const bool is_fill_guide_stroke = is_fill_guide[curve_i];
|
||||
|
||||
const bool has_triangles = active_filled && triangles && !triangles->is_empty() &&
|
||||
!(*triangles)[fill_index].is_empty();
|
||||
|
||||
const bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0;
|
||||
const bool show_stroke = !hide_stroke[curve_i] || is_fill_guide_stroke;
|
||||
const bool show_fill = (has_triangles) && active_filled && (!this->simplify_fill) &&
|
||||
!is_fill_guide_stroke;
|
||||
const bool hide_onion = is_onion && ((gp_style->flag & GP_MATERIAL_HIDE_ONIONSKIN) != 0 ||
|
||||
(!do_onion && !do_multi_frame));
|
||||
const bool skip_stroke = hide_material || (!show_stroke && !show_fill) ||
|
||||
(only_lines && !do_onion && is_onion) || hide_onion;
|
||||
|
||||
if (skip_stroke) {
|
||||
if (active_filled) {
|
||||
t_offset += num_triangles_per_fill[fill_index];
|
||||
}
|
||||
t_offset += num_vertices_per_curve[curve_i] * 2;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
gpu::UniformBuf *new_ubo_mat;
|
||||
gpu::Texture *new_tex_fill = nullptr;
|
||||
gpu::Texture *new_tex_stroke = nullptr;
|
||||
gpencil_material_resources_get(
|
||||
matpool, mat_ofs + material_index, &new_tex_stroke, &new_tex_fill, &new_ubo_mat);
|
||||
|
||||
const 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(pass);
|
||||
|
||||
if (new_ubo_mat != ubo_mat) {
|
||||
pass.bind_ubo("gp_materials", new_ubo_mat);
|
||||
ubo_mat = new_ubo_mat;
|
||||
}
|
||||
if (new_tex_fill) {
|
||||
pass.bind_texture("gp_fill_tx", new_tex_fill);
|
||||
tex_fill = new_tex_fill;
|
||||
}
|
||||
if (new_tex_stroke) {
|
||||
pass.bind_texture("gp_stroke_tx", new_tex_stroke);
|
||||
tex_stroke = new_tex_stroke;
|
||||
}
|
||||
}
|
||||
|
||||
gpu::Batch *geom = DRW_cache_grease_pencil_get(this->scene, ob);
|
||||
if (iter_geom != geom) {
|
||||
drawcall_flush(pass);
|
||||
|
||||
gpu::VertBuf *position_tx = DRW_cache_grease_pencil_position_buffer_get(this->scene, ob);
|
||||
gpu::VertBuf *color_tx = DRW_cache_grease_pencil_color_buffer_get(this->scene, ob);
|
||||
pass.bind_texture("gp_pos_tx", position_tx);
|
||||
pass.bind_texture("gp_col_tx", color_tx);
|
||||
}
|
||||
|
||||
if (show_fill) {
|
||||
const int v_first = t_offset * 3;
|
||||
const int v_count = num_triangles_per_fill[fill_index] * 3;
|
||||
drawcall_add(pass, geom, v_first, v_count);
|
||||
}
|
||||
|
||||
if (active_filled) {
|
||||
t_offset += num_triangles_per_fill[fill_index];
|
||||
}
|
||||
|
||||
if (show_stroke) {
|
||||
const int v_first = t_offset * 3;
|
||||
const int v_count = num_vertices_per_curve[curve_i] * 2 * 3;
|
||||
drawcall_add(pass, geom, v_first, v_count);
|
||||
}
|
||||
|
||||
t_offset += num_vertices_per_curve[curve_i] * 2;
|
||||
});
|
||||
}
|
||||
|
||||
if (last_pass) {
|
||||
drawcall_flush(*last_pass);
|
||||
}
|
||||
|
||||
return tgp_ob;
|
||||
}
|
||||
|
||||
void Instance::object_sync(ObjectRef &ob_ref, Manager &manager)
|
||||
{
|
||||
Object *ob = ob_ref.object;
|
||||
|
||||
/* object must be visible */
|
||||
if (!(DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ob->data && (ob->type == OB_GREASE_PENCIL) && (ob->dt >= OB_SOLID)) {
|
||||
ResourceHandleRange res_handle = manager.unique_handle(ob_ref);
|
||||
|
||||
tObject *tgp_ob = object_sync_do(ob, res_handle);
|
||||
vfx_sync(ob, tgp_ob);
|
||||
}
|
||||
|
||||
if (ob->type == OB_LAMP && this->use_lights) {
|
||||
gpencil_light_pool_populate(this->global_light_pool, ob);
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::end_sync()
|
||||
{
|
||||
/* Upload UBO data. */
|
||||
BLI_memblock_iter iter;
|
||||
BLI_memblock_iternew(this->gp_material_pool, &iter);
|
||||
MaterialPool *pool;
|
||||
while ((pool = static_cast<MaterialPool *>(BLI_memblock_iterstep(&iter)))) {
|
||||
GPU_uniformbuf_update(pool->ubo, pool->mat_data);
|
||||
}
|
||||
|
||||
BLI_memblock_iternew(this->gp_light_pool, &iter);
|
||||
LightPool *lpool;
|
||||
while ((lpool = static_cast<LightPool *>(BLI_memblock_iterstep(&iter)))) {
|
||||
GPU_uniformbuf_update(lpool->ubo, lpool->light_data);
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::acquire_resources()
|
||||
{
|
||||
/* Create frame-buffers only if needed. */
|
||||
if (this->tobjects.first == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int2 size = int2(draw_ctx->viewport_size_get());
|
||||
|
||||
const gpu::TextureFormat format_color = gpu::TextureFormat::SFLOAT_16_16_16_16;
|
||||
const gpu::TextureFormat format_reveal = this->use_signed_fb ?
|
||||
gpu::TextureFormat::SFLOAT_16_16_16_16 :
|
||||
gpu::TextureFormat::UNORM_10_10_10_2;
|
||||
|
||||
this->depth_tx.acquire_2d(size, gpu::TextureFormat::SFLOAT_32_DEPTH_UINT_8);
|
||||
this->color_tx.acquire_2d(size, format_color);
|
||||
this->reveal_tx.acquire_2d(size, format_reveal);
|
||||
|
||||
this->gpencil_fb.ensure(GPU_ATTACHMENT_TEXTURE(this->depth_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(this->color_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(this->reveal_tx));
|
||||
|
||||
if (this->use_layer_fb) {
|
||||
this->color_layer_tx.acquire_2d(size, format_color);
|
||||
this->reveal_layer_tx.acquire_2d(size, format_reveal);
|
||||
|
||||
this->layer_fb.ensure(GPU_ATTACHMENT_TEXTURE(this->depth_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(this->color_layer_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(this->reveal_layer_tx));
|
||||
}
|
||||
|
||||
if (this->use_object_fb) {
|
||||
this->color_object_tx.acquire_2d(size, format_color);
|
||||
this->reveal_object_tx.acquire_2d(size, format_reveal);
|
||||
|
||||
this->object_fb.ensure(GPU_ATTACHMENT_TEXTURE(this->depth_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(this->color_object_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(this->reveal_object_tx));
|
||||
}
|
||||
|
||||
if (this->use_mask_fb) {
|
||||
/* Use high quality format for render. */
|
||||
const gpu::TextureFormat mask_format = this->is_render ? gpu::TextureFormat::UNORM_16 :
|
||||
gpu::TextureFormat::UNORM_8;
|
||||
/* We need an extra depth to not disturb the normal drawing. */
|
||||
this->mask_depth_tx.acquire_2d(size, gpu::TextureFormat::SFLOAT_32_DEPTH_UINT_8);
|
||||
/* The mask_color_tx is needed for frame-buffer completeness. */
|
||||
this->mask_color_tx.acquire_2d(size, gpu::TextureFormat::UNORM_8);
|
||||
this->mask_tx.acquire_2d(size, mask_format);
|
||||
|
||||
this->mask_fb.ensure(GPU_ATTACHMENT_TEXTURE(this->mask_depth_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(this->mask_color_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(this->mask_tx));
|
||||
}
|
||||
|
||||
/* The engine might not support passes, so check if the combined pass actually exists before
|
||||
* rendering grease pencil to it. */
|
||||
const bool combined_pass_exists = DRW_viewport_pass_texture_exists(RE_PASSNAME_COMBINED);
|
||||
if (this->need_combined_pass && combined_pass_exists) {
|
||||
draw::TextureFromPool &combined_pass = DRW_viewport_pass_texture_get(RE_PASSNAME_COMBINED);
|
||||
this->combined_pass_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(combined_pass));
|
||||
}
|
||||
|
||||
if (this->need_grease_pencil_pass) {
|
||||
const int2 size = int2(draw_ctx->viewport_size_get());
|
||||
draw::TextureFromPool &grease_pencil_pass = DRW_viewport_pass_texture_get(
|
||||
RE_PASSNAME_GREASE_PENCIL);
|
||||
grease_pencil_pass.acquire_2d(size, gpu::TextureFormat::SFLOAT_16_16_16_16);
|
||||
this->gpencil_pass_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(grease_pencil_pass));
|
||||
}
|
||||
|
||||
if (DRW_viewport_pass_texture_exists(RE_PASSNAME_DEPTH)) {
|
||||
draw::TextureFromPool &depth_pass = DRW_viewport_pass_texture_get(RE_PASSNAME_DEPTH);
|
||||
this->depth_pass_img = depth_pass.gpu_texture();
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::release_resources()
|
||||
{
|
||||
this->depth_tx.release();
|
||||
this->color_tx.release();
|
||||
this->reveal_tx.release();
|
||||
this->color_layer_tx.release();
|
||||
this->reveal_layer_tx.release();
|
||||
this->color_object_tx.release();
|
||||
this->reveal_object_tx.release();
|
||||
this->mask_depth_tx.release();
|
||||
this->mask_color_tx.release();
|
||||
this->mask_tx.release();
|
||||
this->smaa_edge_tx.release();
|
||||
this->smaa_weight_tx.release();
|
||||
}
|
||||
|
||||
void Instance::draw_mask(View &view, tObject *ob, tLayer *layer)
|
||||
{
|
||||
Manager *manager = DRW_manager_get();
|
||||
|
||||
bool inverted = false;
|
||||
/* OPTI(@fclem): we could optimize by only clearing if the new mask_bits does not contain all
|
||||
* the masks already rendered in the buffer, and drawing only the layers not already drawn. */
|
||||
bool cleared = false;
|
||||
|
||||
GPU_debug_group_begin("GPencil Mask");
|
||||
|
||||
GPU_framebuffer_bind(this->mask_fb);
|
||||
|
||||
for (int i = 0; i < GP_MAX_MASKBITS; i++) {
|
||||
if (!BLI_BITMAP_TEST(layer->mask_bits, i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (BLI_BITMAP_TEST_BOOL(layer->mask_invert_bits, i) != inverted) {
|
||||
if (cleared) {
|
||||
manager->submit(this->mask_invert_ps);
|
||||
}
|
||||
inverted = !inverted;
|
||||
}
|
||||
|
||||
if (!cleared) {
|
||||
cleared = true;
|
||||
GPU_framebuffer_clear_color_depth(
|
||||
this->mask_fb, {1.0, 1.0, 1.0, 1.0}, ob->is_drawmode3d ? 1.0f : 0.0f);
|
||||
}
|
||||
|
||||
tLayer *mask_layer = grease_pencil_layer_cache_get(ob, i, true);
|
||||
/* When filtering by view-layer, the mask could be null and must be ignored. */
|
||||
if (mask_layer == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
manager->submit(*mask_layer->geom_ps, view);
|
||||
}
|
||||
|
||||
if (!inverted) {
|
||||
/* Blend shader expect an opacity mask not a revealage buffer. */
|
||||
manager->submit(this->mask_invert_ps);
|
||||
}
|
||||
|
||||
GPU_debug_group_end();
|
||||
}
|
||||
|
||||
void Instance::draw_object(View &view, tObject *ob)
|
||||
{
|
||||
Manager *manager = DRW_manager_get();
|
||||
|
||||
const std::array<double4, 2> clear_cols = {double4{0, 0, 0, 0}, double4{1, 1, 1, 1}};
|
||||
|
||||
GPU_debug_group_begin("GPencil Object");
|
||||
|
||||
gpu::FrameBuffer *fb_object = (ob->vfx.first) ? this->object_fb : this->gpencil_fb;
|
||||
|
||||
GPU_framebuffer_bind(fb_object);
|
||||
GPU_framebuffer_clear_depth_stencil(fb_object, ob->is_drawmode3d ? 1.0f : 0.0f, 0x00);
|
||||
|
||||
if (ob->vfx.first) {
|
||||
GPU_framebuffer_multi_clear(fb_object, clear_cols);
|
||||
}
|
||||
|
||||
for (tLayer *layer = ob->layers.first; layer; layer = layer->next) {
|
||||
if (layer->mask_bits) {
|
||||
draw_mask(view, ob, layer);
|
||||
}
|
||||
|
||||
if (layer->blend_ps) {
|
||||
GPU_framebuffer_bind(this->layer_fb);
|
||||
GPU_framebuffer_multi_clear(this->layer_fb, clear_cols);
|
||||
}
|
||||
else {
|
||||
GPU_framebuffer_bind(fb_object);
|
||||
}
|
||||
|
||||
manager->submit(*layer->geom_ps, view);
|
||||
|
||||
if (layer->blend_ps) {
|
||||
GPU_framebuffer_bind(fb_object);
|
||||
manager->submit(*layer->blend_ps);
|
||||
}
|
||||
}
|
||||
|
||||
for (tVfx *vfx = ob->vfx.first; vfx; vfx = vfx->next) {
|
||||
GPU_framebuffer_bind(*(vfx->target_fb));
|
||||
manager->submit(*vfx->vfx_ps);
|
||||
}
|
||||
|
||||
this->object_bound_mat = float4x4(ob->plane_mat);
|
||||
this->is_stroke_order_3d = ob->is_drawmode3d;
|
||||
|
||||
if (this->scene_fb) {
|
||||
GPU_framebuffer_bind(this->scene_fb);
|
||||
manager->submit(this->merge_depth_ps, view);
|
||||
}
|
||||
|
||||
if (DRW_viewport_pass_texture_exists(RE_PASSNAME_DEPTH)) {
|
||||
manager->submit(this->merge_depth_pass_ps, view);
|
||||
}
|
||||
|
||||
GPU_debug_group_end();
|
||||
}
|
||||
|
||||
void Instance::draw(Manager &manager)
|
||||
{
|
||||
DefaultTextureList *dtxl = draw_ctx->viewport_texture_list_get();
|
||||
DefaultFramebufferList *dfbl = draw_ctx->viewport_framebuffer_list_get();
|
||||
|
||||
if (this->render_depth_tx.is_valid()) {
|
||||
this->scene_depth_tx = this->render_depth_tx;
|
||||
this->scene_fb = this->render_fb;
|
||||
}
|
||||
else {
|
||||
this->scene_fb = dfbl->default_fb;
|
||||
this->scene_depth_tx = dtxl->depth;
|
||||
}
|
||||
BLI_assert(this->scene_depth_tx);
|
||||
|
||||
std::array<double4, 2> clear_cols = {double4{0.0f, 0.0f, 0.0f, 0.0f},
|
||||
double4{1.0f, 1.0f, 1.0f, 1.0f}};
|
||||
|
||||
/* Fade 3D objects. */
|
||||
if ((!this->is_render) && (this->fade_3d_object_opacity > -1.0f) && (this->obact != nullptr) &&
|
||||
ELEM(this->obact->type, OB_GREASE_PENCIL))
|
||||
{
|
||||
float3 background_color;
|
||||
ED_view3d_background_color_get(this->scene, this->v3d, background_color);
|
||||
/* Blend color. */
|
||||
background_color = math::interpolate(
|
||||
background_color, float3(clear_cols[0]), this->fade_3d_object_opacity);
|
||||
|
||||
clear_cols[0] = double4(background_color, 0.0f);
|
||||
clear_cols[1] = double4(float4(clear_cols[1]) * this->fade_3d_object_opacity);
|
||||
}
|
||||
|
||||
/* Sort object by decreasing Z to avoid most of alpha ordering issues. */
|
||||
gpencil_object_cache_sort(this);
|
||||
|
||||
if (this->tobjects.first == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
DRW_submission_start();
|
||||
|
||||
antialiasing_init();
|
||||
|
||||
this->acquire_resources();
|
||||
|
||||
if (this->tobjects.first) {
|
||||
GPU_framebuffer_bind(this->gpencil_fb);
|
||||
GPU_framebuffer_multi_clear(this->gpencil_fb, clear_cols);
|
||||
}
|
||||
|
||||
View &view = View::default_get();
|
||||
|
||||
for (tObject *ob = this->tobjects.first; ob; ob = ob->next) {
|
||||
draw_object(view, ob);
|
||||
}
|
||||
|
||||
if (this->scene_fb) {
|
||||
antialiasing_draw(manager);
|
||||
}
|
||||
|
||||
this->release_resources();
|
||||
|
||||
DRW_submission_end();
|
||||
}
|
||||
|
||||
DrawEngine *Engine::create_instance()
|
||||
{
|
||||
return new Instance();
|
||||
}
|
||||
|
||||
void Engine::free_static()
|
||||
{
|
||||
ShaderCache::release();
|
||||
}
|
||||
|
||||
} // namespace blender::draw::gpencil
|
||||
@@ -0,0 +1,450 @@
|
||||
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_bitmap.h"
|
||||
#include "BLI_memblock.h"
|
||||
|
||||
#include "DNA_shader_fx_types.h"
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "BKE_grease_pencil.hh"
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
|
||||
#include "draw_pass.hh"
|
||||
#include "draw_view_data.hh"
|
||||
|
||||
#define GP_LIGHT
|
||||
|
||||
#include "gpencil_defines.hh"
|
||||
#include "gpencil_shader.hh"
|
||||
#include "gpencil_shader_shared.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct GpencilBatchCache;
|
||||
struct Object;
|
||||
struct RenderEngine;
|
||||
struct RenderLayer;
|
||||
struct View3D;
|
||||
|
||||
/* used to convert pixel scale. */
|
||||
#define GPENCIL_PIXEL_FACTOR 2000.0f
|
||||
|
||||
/* used to expand VBOs. Size has a big impact in the speed */
|
||||
#define GPENCIL_VBO_BLOCK_SIZE 128
|
||||
|
||||
#define GP_MAX_MASKBITS 256
|
||||
|
||||
namespace draw::gpencil {
|
||||
|
||||
struct MaterialPool {
|
||||
/* Single linked-list. */
|
||||
MaterialPool *next;
|
||||
/* GPU representation of materials. */
|
||||
gpMaterial mat_data[GPENCIL_MATERIAL_BUFFER_LEN];
|
||||
/* Matching ubo. */
|
||||
gpu::UniformBuf *ubo;
|
||||
/* Texture per material. NULL means none. */
|
||||
gpu::Texture *tex_fill[GPENCIL_MATERIAL_BUFFER_LEN];
|
||||
gpu::Texture *tex_stroke[GPENCIL_MATERIAL_BUFFER_LEN];
|
||||
/* Number of material used in this pool. */
|
||||
int used_count;
|
||||
};
|
||||
|
||||
struct LightPool {
|
||||
/* GPU representation of materials. */
|
||||
gpLight light_data[GPENCIL_LIGHT_BUFFER_LEN];
|
||||
/* Matching ubo. */
|
||||
gpu::UniformBuf *ubo;
|
||||
/* Number of light in the pool. */
|
||||
int light_used;
|
||||
};
|
||||
|
||||
/* Temporary gpencil FX reflection used by the gpencil::Instance. */
|
||||
struct tVfx {
|
||||
/** Single linked-list. */
|
||||
struct tVfx *next = nullptr;
|
||||
::std::unique_ptr<PassSimple> vfx_ps = ::std::make_unique<PassSimple>("vfx");
|
||||
/* Frame-buffer reference since it may not be allocated yet. */
|
||||
gpu::FrameBuffer **target_fb = nullptr;
|
||||
};
|
||||
|
||||
/* Temporary gpencil layer reflection used by the gpencil::Instance. */
|
||||
struct tLayer {
|
||||
/** Single linked-list. */
|
||||
struct tLayer *next;
|
||||
/** Geometry pass (draw all strokes). */
|
||||
::std::unique_ptr<PassSimple> geom_ps;
|
||||
/** Blend pass to composite onto the target buffer (blends modes). NULL if not needed. */
|
||||
::std::unique_ptr<PassSimple> blend_ps;
|
||||
/** Layer id of the mask. */
|
||||
BLI_bitmap *mask_bits;
|
||||
BLI_bitmap *mask_invert_bits;
|
||||
/** Index in the layer list. Used as id for masking. */
|
||||
int layer_id;
|
||||
/** True if this pass is part of the onion skinning. */
|
||||
bool is_onion;
|
||||
};
|
||||
|
||||
/* Temporary object reflection used by the gpencil::Instance. */
|
||||
struct tObject {
|
||||
/** Single linked-list. */
|
||||
struct tObject *next;
|
||||
|
||||
struct {
|
||||
tLayer *first, *last;
|
||||
} layers;
|
||||
|
||||
struct {
|
||||
tVfx *first, *last;
|
||||
} vfx;
|
||||
|
||||
/* Distance to camera. Used for sorting. */
|
||||
float camera_z;
|
||||
/* Normal used for shading. Based on view angle. */
|
||||
float3 plane_normal;
|
||||
/* Used for drawing depth merge pass. */
|
||||
float plane_mat[4][4];
|
||||
|
||||
bool is_drawmode3d;
|
||||
|
||||
/* Use Material Holdout. */
|
||||
bool do_mat_holdout;
|
||||
};
|
||||
|
||||
/* *********** LISTS *********** */
|
||||
|
||||
struct Instance final : public DrawEngine {
|
||||
PassSimple smaa_edge_ps = {"smaa_edge"};
|
||||
PassSimple smaa_weight_ps = {"smaa_weight"};
|
||||
PassSimple smaa_resolve_ps = {"smaa_resolve"};
|
||||
PassSimple accumulate_ps = {"aa_accumulate"};
|
||||
/* Composite the object depth to the default depth buffer to occlude overlays. */
|
||||
PassSimple merge_depth_ps = {"merge_depth_ps"};
|
||||
/* Composite the object depth to the depth pass. */
|
||||
PassSimple merge_depth_pass_ps = {"merge_depth_pass_ps"};
|
||||
/* Invert mask buffer content. */
|
||||
PassSimple mask_invert_ps = {"mask_invert_ps"};
|
||||
|
||||
float4x4 object_bound_mat;
|
||||
|
||||
/* Dummy texture to avoid errors cause by empty sampler. */
|
||||
Texture dummy_texture = {"dummy_texture"};
|
||||
Texture dummy_depth = {"dummy_depth"};
|
||||
/* Textures used during render. Containing underlying rendered scene. */
|
||||
Texture render_depth_tx = {"render_depth_tx"};
|
||||
Texture render_color_tx = {"render_color_tx"};
|
||||
/* Snapshot for smoother drawing. */
|
||||
Texture snapshot_depth_tx = {"snapshot_depth_tx"};
|
||||
Texture snapshot_color_tx = {"snapshot_color_tx"};
|
||||
Texture snapshot_reveal_tx = {"snapshot_reveal_tx"};
|
||||
/* Textures used by Anti-aliasing. */
|
||||
Texture smaa_area_tx = {"smaa_area_tx"};
|
||||
Texture smaa_search_tx = {"smaa_search_tx"};
|
||||
|
||||
/* Stores the viewport compositor depth pass if needed. */
|
||||
gpu::Texture *depth_pass_img = nullptr;
|
||||
|
||||
/* Temp Textures (shared with other engines). */
|
||||
TextureFromPool depth_tx = {"depth_tx"};
|
||||
TextureFromPool color_tx = {"color_tx"};
|
||||
TextureFromPool color_layer_tx = {"color_layer_tx"};
|
||||
TextureFromPool color_object_tx = {"color_object_tx"};
|
||||
/* Revealage is 1 - alpha */
|
||||
TextureFromPool reveal_tx = {"reveal_tx"};
|
||||
TextureFromPool reveal_layer_tx = {"reveal_layer_tx"};
|
||||
TextureFromPool reveal_object_tx = {"reveal_object_tx"};
|
||||
/* Mask texture */
|
||||
TextureFromPool mask_depth_tx = {"mask_depth_tx"};
|
||||
TextureFromPool mask_color_tx = {"mask_color_tx"};
|
||||
TextureFromPool mask_tx = {"mask_tx"};
|
||||
/* Anti-Aliasing. */
|
||||
TextureFromPool smaa_edge_tx = {"smaa_edge_tx"};
|
||||
TextureFromPool smaa_weight_tx = {"smaa_weight_tx"};
|
||||
|
||||
Framebuffer render_fb = {"render_fb"};
|
||||
Framebuffer gpencil_fb = {"gpencil_fb"};
|
||||
Framebuffer combined_pass_fb = {"combined_pass_fb"};
|
||||
Framebuffer gpencil_pass_fb = {"gpencil_pass_fb"};
|
||||
Framebuffer snapshot_fb = {"snapshot_fb"};
|
||||
Framebuffer layer_fb = {"layer_fb"};
|
||||
Framebuffer object_fb = {"object_fb"};
|
||||
Framebuffer mask_fb = {"mask_fb"};
|
||||
Framebuffer smaa_edge_fb = {"smaa_edge_fb"};
|
||||
Framebuffer smaa_weight_fb = {"smaa_weight_fb"};
|
||||
|
||||
/* NOTE: These do not preserve the PassSimple memory across frames.
|
||||
* If that becomes a bottleneck, these containers can be improved. */
|
||||
using tVfx_Pool = draw::detail::SubPassVector<tVfx>;
|
||||
using tLayer_Pool = draw::detail::SubPassVector<tLayer>;
|
||||
|
||||
/* tObject */
|
||||
struct BLI_memblock *gp_object_pool = BLI_memblock_create(sizeof(tObject));
|
||||
/* tLayer */
|
||||
tLayer_Pool *gp_layer_pool = new tLayer_Pool();
|
||||
/* tVfx */
|
||||
tVfx_Pool *gp_vfx_pool = new tVfx_Pool();
|
||||
/* MaterialPool */
|
||||
struct BLI_memblock *gp_material_pool = BLI_memblock_create(sizeof(MaterialPool));
|
||||
/* LightPool */
|
||||
struct BLI_memblock *gp_light_pool = BLI_memblock_create(sizeof(LightPool));
|
||||
/* BLI_bitmap */
|
||||
struct BLI_memblock *gp_maskbit_pool = BLI_memblock_create(BLI_BITMAP_SIZE(GP_MAX_MASKBITS));
|
||||
|
||||
const DRWContext *draw_ctx = nullptr;
|
||||
|
||||
/* Last used material pool. */
|
||||
MaterialPool *last_material_pool;
|
||||
/* Last used light pool. */
|
||||
LightPool *last_light_pool;
|
||||
/* Common lightpool containing all lights in the scene. */
|
||||
LightPool *global_light_pool;
|
||||
/* Common lightpool containing one ambient white light. */
|
||||
LightPool *shadeless_light_pool;
|
||||
/* Linked list of tObjects. */
|
||||
struct {
|
||||
tObject *first, *last;
|
||||
} tobjects, tobjects_infront;
|
||||
/* Used to record whether the `tobjects` list is sorted. Do not sort drawings again in separate
|
||||
* pass rendering to avoid generating infinite lists. */
|
||||
bool is_sorted;
|
||||
/* Pointer to dtxl->depth */
|
||||
gpu::Texture *scene_depth_tx;
|
||||
gpu::FrameBuffer *scene_fb;
|
||||
/* Used for render accumulation antialiasing. */
|
||||
Texture accumulation_tx = {"gp_accumulation_tx"};
|
||||
Framebuffer accumulation_fb = {"gp_accumulation_fb"};
|
||||
/* Copy of txl->dummy_tx */
|
||||
gpu::Texture *dummy_tx;
|
||||
/* Copy of v3d->shading.single_color. */
|
||||
float v3d_single_color[3];
|
||||
/* Copy of v3d->shading.color_type or -1 to ignore. */
|
||||
int v3d_color_type;
|
||||
/* Current frame */
|
||||
int cfra;
|
||||
/* If we are rendering for final render (F12).
|
||||
* NOTE: set to false for viewport and opengl rendering (including sequencer scene rendering),
|
||||
* but set to true when rendering in #OB_RENDER shading mode (viewport or opengl rendering). */
|
||||
bool is_render;
|
||||
/* If we are in viewport display (used for VFX). */
|
||||
bool is_viewport;
|
||||
/* Is shading set to wire-frame. */
|
||||
bool draw_wireframe;
|
||||
/* Used by the depth merge step. */
|
||||
int is_stroke_order_3d;
|
||||
/* Used for computing object distance to camera. */
|
||||
float camera_z_axis[3], camera_z_offset;
|
||||
float camera_pos[3];
|
||||
/* Pseudo depth of field parameter. Used to scale blur radius. */
|
||||
float dof_params[2];
|
||||
/* Used for DoF Setup. */
|
||||
Object *camera;
|
||||
/* Copy of draw_ctx->view_layer for convenience. */
|
||||
struct ViewLayer *view_layer;
|
||||
/* Copy of draw_ctx->scene for convenience. */
|
||||
struct Scene *scene;
|
||||
/* Copy of draw_ctx->vie3d for convenience. */
|
||||
struct View3D *v3d;
|
||||
|
||||
/* Active object. */
|
||||
Object *obact;
|
||||
/* List of temp objects containing the stroke. */
|
||||
struct {
|
||||
tObject *first, *last;
|
||||
} sbuffer_tobjects;
|
||||
/* Batches containing the temp stroke. */
|
||||
gpu::Batch *stroke_batch;
|
||||
gpu::Batch *fill_batch;
|
||||
bool snapshot_buffer_dirty;
|
||||
|
||||
/* Display onion skinning */
|
||||
bool do_onion;
|
||||
/* Show only the onion skins of the active object. */
|
||||
bool do_onion_only_active_object;
|
||||
/* Playing animation */
|
||||
bool playing;
|
||||
/* simplify settings */
|
||||
bool simplify_fill;
|
||||
bool simplify_fx;
|
||||
bool simplify_antialias;
|
||||
/* Use scene lighting or flat shading (global setting). */
|
||||
bool use_lighting;
|
||||
/* Use physical lights or just ambient lighting. */
|
||||
bool use_lights;
|
||||
/* Do we need additional frame-buffers? */
|
||||
bool use_layer_fb;
|
||||
bool use_object_fb;
|
||||
bool use_mask_fb;
|
||||
/* The viewport compositor needs the combined pass, so we need to render to it. */
|
||||
bool need_combined_pass;
|
||||
/* The viewport compositor needs the grease pencil pass, so we need to render to it. */
|
||||
bool need_grease_pencil_pass;
|
||||
/* Some blend mode needs to add negative values.
|
||||
* This is only supported if target texture is signed. Only switch for the `reveal_tex`. */
|
||||
bool use_signed_fb;
|
||||
/* Use only lines for multiedit and not active frame. */
|
||||
bool use_multiedit_lines_only;
|
||||
/* Layer opacity for fading. */
|
||||
float fade_layer_opacity;
|
||||
/* Opacity for fading gpencil objects. */
|
||||
float fade_gp_object_opacity;
|
||||
/* Opacity for fading 3D objects. */
|
||||
float fade_3d_object_opacity;
|
||||
/* Mask opacity uniform. */
|
||||
float mask_opacity;
|
||||
/* X-ray transparency in solid mode. */
|
||||
float xray_alpha;
|
||||
/* Mask invert uniform. */
|
||||
int mask_invert;
|
||||
/* Vertex Paint opacity. */
|
||||
float vertex_paint_opacity;
|
||||
/* Force 3D depth rendering. */
|
||||
bool force_stroke_order_3d;
|
||||
|
||||
~Instance() final
|
||||
{
|
||||
BLI_memblock_destroy(gp_light_pool, light_pool_free);
|
||||
BLI_memblock_destroy(gp_material_pool, material_pool_free);
|
||||
BLI_memblock_destroy(gp_maskbit_pool, nullptr);
|
||||
BLI_memblock_destroy(gp_object_pool, nullptr);
|
||||
delete gp_layer_pool;
|
||||
delete gp_vfx_pool;
|
||||
}
|
||||
|
||||
void acquire_resources();
|
||||
void release_resources();
|
||||
|
||||
StringRefNull name_get() final
|
||||
{
|
||||
return "Grease Pencil";
|
||||
}
|
||||
|
||||
void init() final;
|
||||
|
||||
void begin_sync() final;
|
||||
void object_sync(ObjectRef &ob_ref, Manager &manager) final;
|
||||
void end_sync() final;
|
||||
|
||||
void draw(Manager &manager) final;
|
||||
|
||||
void antialiasing_accumulate(Manager &manager, float alpha);
|
||||
|
||||
static float2 antialiasing_sample_get(int sample_index, int sample_count);
|
||||
|
||||
private:
|
||||
tObject *object_sync_do(Object *ob, ResourceHandleRange res_handle);
|
||||
|
||||
/* Check if the passed in layer is used by any other layer as a mask (in the viewlayer). */
|
||||
bool is_used_as_layer_mask_in_viewlayer(const GreasePencil &grease_pencil,
|
||||
const bke::greasepencil::Layer &mask_layer,
|
||||
const ViewLayer &view_layer);
|
||||
|
||||
/* Returns true if this layer should be rendered (as part of the viewlayer). */
|
||||
bool use_layer_in_render(const GreasePencil &grease_pencil,
|
||||
const bke::greasepencil::Layer &layer,
|
||||
const ViewLayer &view_layer,
|
||||
bool &r_is_used_as_mask);
|
||||
|
||||
void draw_mask(View &view, tObject *ob, tLayer *layer);
|
||||
void draw_object(View &view, tObject *ob);
|
||||
|
||||
void antialiasing_init();
|
||||
void antialiasing_draw(Manager &manager);
|
||||
|
||||
struct VfxFramebufferRef {
|
||||
/* These may not be allocated yet, use address of future pointer. */
|
||||
gpu::FrameBuffer **fb;
|
||||
gpu::Texture **color_tx;
|
||||
gpu::Texture **reveal_tx;
|
||||
};
|
||||
|
||||
SwapChain<VfxFramebufferRef, 2> vfx_swapchain_;
|
||||
|
||||
PassSimple &vfx_pass_create(const char *name,
|
||||
DRWState state,
|
||||
gpu::Shader *sh,
|
||||
tObject *tgp_ob,
|
||||
GPUSamplerState sampler = GPUSamplerState::internal_sampler());
|
||||
|
||||
void vfx_blur_sync(BlurShaderFxData *fx, Object *ob, tObject *tgp_ob);
|
||||
void vfx_colorize_sync(ColorizeShaderFxData *fx, Object *ob, tObject *tgp_ob);
|
||||
void vfx_flip_sync(FlipShaderFxData *fx, Object *ob, tObject *tgp_ob);
|
||||
void vfx_rim_sync(RimShaderFxData *fx, Object *ob, tObject *tgp_ob);
|
||||
void vfx_pixelize_sync(PixelShaderFxData *fx, Object *ob, tObject *tgp_ob);
|
||||
void vfx_shadow_sync(ShadowShaderFxData *fx, Object *ob, tObject *tgp_ob);
|
||||
void vfx_glow_sync(GlowShaderFxData *fx, Object *ob, tObject *tgp_ob);
|
||||
void vfx_wave_sync(WaveShaderFxData *fx, Object *ob, tObject *tgp_ob);
|
||||
void vfx_swirl_sync(SwirlShaderFxData *fx, Object *ob, tObject *tgp_ob);
|
||||
|
||||
void vfx_sync(Object *ob, tObject *tgp_ob);
|
||||
|
||||
static void material_pool_free(void *storage)
|
||||
{
|
||||
MaterialPool *matpool = static_cast<MaterialPool *>(storage);
|
||||
GPU_UBO_FREE_SAFE(matpool->ubo);
|
||||
}
|
||||
|
||||
static void light_pool_free(void *storage)
|
||||
{
|
||||
LightPool *lightpool = static_cast<LightPool *>(storage);
|
||||
GPU_UBO_FREE_SAFE(lightpool->ubo);
|
||||
}
|
||||
};
|
||||
|
||||
struct GPENCIL_Data {
|
||||
void *engine_type; /* Required */
|
||||
struct Instance *instance;
|
||||
|
||||
char info[GPU_INFO_SIZE];
|
||||
};
|
||||
|
||||
/* geometry batch cache functions */
|
||||
struct GpencilBatchCache *gpencil_batch_cache_get(struct Object *ob, int cfra);
|
||||
|
||||
tObject *gpencil_object_cache_add(Instance *inst,
|
||||
Object *ob,
|
||||
bool is_stroke_order_3d,
|
||||
Bounds<float3> bounds);
|
||||
void gpencil_object_cache_sort(Instance *inst);
|
||||
|
||||
tLayer *grease_pencil_layer_cache_get(tObject *tgp_ob, int layer_id, bool skip_onion);
|
||||
|
||||
tLayer *grease_pencil_layer_cache_add(Instance *inst,
|
||||
const Object *ob,
|
||||
const bke::greasepencil::Layer &layer,
|
||||
int onion_id,
|
||||
bool is_used_as_mask,
|
||||
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.
|
||||
*/
|
||||
MaterialPool *gpencil_material_pool_create(Instance *inst,
|
||||
Object *ob,
|
||||
int *ofs,
|
||||
bool is_vertex_mode);
|
||||
void gpencil_material_resources_get(MaterialPool *first_pool,
|
||||
int mat_id,
|
||||
gpu::Texture **r_tex_stroke,
|
||||
gpu::Texture **r_tex_fill,
|
||||
gpu::UniformBuf **r_ubo_mat);
|
||||
|
||||
void gpencil_light_ambient_add(LightPool *lightpool, const float color[3]);
|
||||
void gpencil_light_pool_populate(LightPool *lightpool, Object *ob);
|
||||
LightPool *gpencil_light_pool_add(Instance *inst);
|
||||
/**
|
||||
* Creates a single pool containing all lights assigned (light linked) for a given object.
|
||||
*/
|
||||
LightPool *gpencil_light_pool_create(Instance *inst, Object *ob);
|
||||
|
||||
} // namespace draw::gpencil
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,447 @@
|
||||
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "BLI_math_geom.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "BKE_colortools.hh"
|
||||
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "BKE_object.hh"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "RE_engine.h"
|
||||
#include "RE_pipeline.h"
|
||||
#include "render_types.h"
|
||||
|
||||
#include "IMB_imbuf_types.hh"
|
||||
|
||||
#include "gpencil_engine_private.hh"
|
||||
|
||||
namespace blender::draw::gpencil {
|
||||
|
||||
/* Remap depth from views-pace to [0..1] to be able to use it with as GPU depth buffer. */
|
||||
static void remap_depth(const View &view, MutableSpan<float> pix_z)
|
||||
{
|
||||
if (view.is_persp()) {
|
||||
const float4x4 &winmat = view.winmat();
|
||||
for (auto &pix : pix_z) {
|
||||
pix = (-winmat[3][2] / -pix) - winmat[2][2];
|
||||
pix = clamp_f(pix * 0.5f + 0.5f, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Keep in mind, near and far distance are negatives. */
|
||||
const float near = view.near_clip();
|
||||
const float far = view.far_clip();
|
||||
const float range_inv = 1.0f / fabsf(far - near);
|
||||
for (auto &pix : pix_z) {
|
||||
pix = (pix + near) * range_inv;
|
||||
pix = clamp_f(pix, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void render_set_view(RenderEngine *engine,
|
||||
const Depsgraph *depsgraph,
|
||||
const float2 aa_offset = float2{0.0f})
|
||||
{
|
||||
Object *camera = DEG_get_evaluated(depsgraph, RE_GetCamera(engine->re));
|
||||
|
||||
float4x4 winmat, viewinv;
|
||||
RE_GetCameraWindow(engine->re, camera, winmat.ptr());
|
||||
RE_GetCameraModelMatrix(engine->re, camera, viewinv.ptr());
|
||||
|
||||
window_translate_m4(winmat.ptr(), winmat.ptr(), UNPACK2(aa_offset));
|
||||
|
||||
View::default_set(math::invert(viewinv), winmat);
|
||||
}
|
||||
|
||||
static void render_init_buffers(const DRWContext *draw_ctx,
|
||||
Instance &inst,
|
||||
RenderEngine *engine,
|
||||
RenderLayer *render_layer,
|
||||
const rcti *rect,
|
||||
const bool use_separated_pass)
|
||||
{
|
||||
const int2 size = int2(draw_ctx->viewport_size_get());
|
||||
View &view = View::default_get();
|
||||
|
||||
/* 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_DEPTH, 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_data_for_write() : nullptr;
|
||||
float *pix_col = (rpass_col_src) ? rpass_col_src->ibuf->float_data_for_write() : nullptr;
|
||||
|
||||
if (!pix_z || !pix_col) {
|
||||
RE_engine_set_error_message(engine,
|
||||
"Warning: To correctly render occluded Grease Pencil objects, "
|
||||
"enable Combined and Depth passes.");
|
||||
}
|
||||
|
||||
if (pix_z) {
|
||||
/* Depth need to be remapped to [0..1] range. */
|
||||
pix_z = MEM_dupalloc(pix_z);
|
||||
remap_depth(view, {pix_z, rpass_z_src->rectx * rpass_z_src->recty});
|
||||
}
|
||||
|
||||
const bool has_full_rect = (rect->xmin == 0 && rect->ymin == 0 && rect->xmax == size.x &&
|
||||
rect->ymax == size.y);
|
||||
const bool do_region = !use_separated_pass && !has_full_rect;
|
||||
const bool do_clear_z = !pix_z || do_region;
|
||||
const bool do_clear_col = use_separated_pass || (!pix_col) || do_region;
|
||||
|
||||
/* FIXME(fclem): we have a precision loss in the depth buffer because of this re-upload.
|
||||
* Find where it comes from! */
|
||||
/* In multi view render the textures can be reused. */
|
||||
if (inst.render_depth_tx.is_valid() && !do_clear_z && has_full_rect) {
|
||||
GPU_texture_update(inst.render_depth_tx, GPU_DATA_FLOAT, pix_z);
|
||||
}
|
||||
else {
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT |
|
||||
GPU_TEXTURE_USAGE_HOST_READ;
|
||||
inst.render_depth_tx.ensure_2d(
|
||||
gpu::TextureFormat::SFLOAT_32_DEPTH, int2(size), usage, do_region ? nullptr : pix_z);
|
||||
}
|
||||
if (inst.render_color_tx.is_valid() && !do_clear_col && has_full_rect) {
|
||||
GPU_texture_update(inst.render_color_tx, GPU_DATA_FLOAT, pix_col);
|
||||
}
|
||||
else {
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT |
|
||||
GPU_TEXTURE_USAGE_HOST_READ;
|
||||
inst.render_color_tx.ensure_2d(
|
||||
gpu::TextureFormat::SFLOAT_16_16_16_16, int2(size), usage, do_region ? nullptr : pix_col);
|
||||
}
|
||||
|
||||
inst.render_fb.ensure(GPU_ATTACHMENT_TEXTURE(inst.render_depth_tx),
|
||||
GPU_ATTACHMENT_TEXTURE(inst.render_color_tx));
|
||||
|
||||
if (do_clear_z || do_clear_col) {
|
||||
/* To avoid unpredictable result, clear buffers that have not be initialized. */
|
||||
GPU_framebuffer_bind(inst.render_fb);
|
||||
if (do_clear_col) {
|
||||
GPU_framebuffer_clear_color(inst.render_fb, {0.0, 0.0, 0.0, 0.0});
|
||||
}
|
||||
if (do_clear_z) {
|
||||
GPU_framebuffer_clear_depth(inst.render_fb, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (do_region) {
|
||||
int x = rect->xmin;
|
||||
int y = rect->ymin;
|
||||
int w = BLI_rcti_size_x(rect);
|
||||
int h = BLI_rcti_size_y(rect);
|
||||
if (pix_col) {
|
||||
GPU_texture_update_sub(inst.render_color_tx, GPU_DATA_FLOAT, pix_col, x, y, 0, w, h, 0);
|
||||
}
|
||||
if (pix_z) {
|
||||
GPU_texture_update_sub(inst.render_depth_tx, GPU_DATA_FLOAT, pix_z, x, y, 0, w, h, 0);
|
||||
}
|
||||
}
|
||||
|
||||
MEM_SAFE_DELETE(pix_z);
|
||||
}
|
||||
|
||||
static void render_result_z(const DRWContext *draw_ctx,
|
||||
RenderLayer *rl,
|
||||
const char *viewname,
|
||||
Instance &instance,
|
||||
const rcti *rect)
|
||||
{
|
||||
ViewLayer *view_layer = draw_ctx->view_layer;
|
||||
if ((view_layer->passflag & SCE_PASS_DEPTH) == 0) {
|
||||
return;
|
||||
}
|
||||
RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_DEPTH, viewname);
|
||||
if (rp == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
float *ro_buffer_data = rp->ibuf->float_data_for_write();
|
||||
|
||||
GPU_framebuffer_read_depth(instance.render_fb,
|
||||
rect->xmin,
|
||||
rect->ymin,
|
||||
BLI_rcti_size_x(rect),
|
||||
BLI_rcti_size_y(rect),
|
||||
GPU_DATA_FLOAT,
|
||||
ro_buffer_data);
|
||||
|
||||
float4x4 winmat = View::default_get().winmat();
|
||||
|
||||
int pix_num = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect);
|
||||
|
||||
/* Convert GPU depth [0..1] to view Z [near..far] */
|
||||
if (View::default_get().is_persp()) {
|
||||
for (int i = 0; i < pix_num; i++) {
|
||||
if (ro_buffer_data[i] == 1.0f) {
|
||||
ro_buffer_data[i] = 1e10f; /* Background */
|
||||
}
|
||||
else {
|
||||
ro_buffer_data[i] = ro_buffer_data[i] * 2.0f - 1.0f;
|
||||
ro_buffer_data[i] = winmat[3][2] / (ro_buffer_data[i] + winmat[2][2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Keep in mind, near and far distance are negatives. */
|
||||
float near = View::default_get().near_clip();
|
||||
float far = View::default_get().far_clip();
|
||||
float range = fabsf(far - near);
|
||||
|
||||
for (int i = 0; i < pix_num; i++) {
|
||||
if (ro_buffer_data[i] == 1.0f) {
|
||||
ro_buffer_data[i] = 1e10f; /* Background */
|
||||
}
|
||||
else {
|
||||
ro_buffer_data[i] = ro_buffer_data[i] * range - near;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void render_result_combined(RenderLayer *rl,
|
||||
const char *viewname,
|
||||
Instance &instance,
|
||||
const rcti *rect)
|
||||
{
|
||||
RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_COMBINED, viewname);
|
||||
|
||||
Framebuffer read_fb;
|
||||
read_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(instance.accumulation_tx));
|
||||
GPU_framebuffer_bind(read_fb);
|
||||
GPU_framebuffer_read_color(read_fb,
|
||||
rect->xmin,
|
||||
rect->ymin,
|
||||
BLI_rcti_size_x(rect),
|
||||
BLI_rcti_size_y(rect),
|
||||
4,
|
||||
0,
|
||||
GPU_DATA_FLOAT,
|
||||
rp->ibuf->float_data_for_write());
|
||||
}
|
||||
|
||||
static void render_result_separated_pass(float *data, Instance &instance, const rcti *rect)
|
||||
{
|
||||
Framebuffer read_fb;
|
||||
read_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(instance.accumulation_tx));
|
||||
GPU_framebuffer_bind(read_fb);
|
||||
GPU_framebuffer_read_color(read_fb,
|
||||
rect->xmin,
|
||||
rect->ymin,
|
||||
BLI_rcti_size_x(rect),
|
||||
BLI_rcti_size_y(rect),
|
||||
4,
|
||||
0,
|
||||
GPU_DATA_FLOAT,
|
||||
data);
|
||||
}
|
||||
|
||||
/* This is taken from eevee::Sampling::cdf_from_curvemapping. */
|
||||
static void cdf_from_curvemapping(const CurveMapping &curve, Array<float> &cdf)
|
||||
{
|
||||
BLI_assert(cdf.size() > 1);
|
||||
cdf[0] = 0.0f;
|
||||
/* Actual CDF evaluation. */
|
||||
for (const int u : IndexRange(cdf.size() - 1)) {
|
||||
const float x = float(u + 1) / float(cdf.size() - 1);
|
||||
cdf[u + 1] = cdf[u] + BKE_curvemapping_evaluateF(&curve, 0, x);
|
||||
}
|
||||
/* Normalize the CDF. */
|
||||
for (const int u : cdf.index_range()) {
|
||||
cdf[u] /= cdf.last();
|
||||
}
|
||||
/* Just to make sure. */
|
||||
cdf.last() = 1.0f;
|
||||
}
|
||||
|
||||
/* This is taken from eevee::Sampling::cdf_invert. */
|
||||
static void cdf_invert(Array<float> &cdf, Array<float> &inverted_cdf)
|
||||
{
|
||||
BLI_assert(cdf.first() == 0.0f && cdf.last() == 1.0f);
|
||||
for (const int u : inverted_cdf.index_range()) {
|
||||
const float x = clamp_f(u / float(inverted_cdf.size() - 1), 1e-5f, 1.0f - 1e-5f);
|
||||
for (const int i : cdf.index_range().drop_front(1)) {
|
||||
if (cdf[i] >= x) {
|
||||
const float t = (x - cdf[i]) / (cdf[i] - cdf[i - 1]);
|
||||
inverted_cdf[u] = (float(i) + t) / float(cdf.size() - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This is taken from eevee::MotionBlurModule::shutter_time_to_scene_time. */
|
||||
static float shutter_time_to_scene_time(const int shutter_position,
|
||||
const float shutter_time,
|
||||
const float frame_time,
|
||||
float time)
|
||||
{
|
||||
switch (shutter_position) {
|
||||
case SCE_MB_START:
|
||||
/* No offset. */
|
||||
break;
|
||||
case SCE_MB_CENTER:
|
||||
time -= 0.5f;
|
||||
break;
|
||||
case SCE_MB_END:
|
||||
time -= 1.0;
|
||||
break;
|
||||
default:
|
||||
BLI_assert_msg(false, "Invalid motion blur position enum!");
|
||||
break;
|
||||
}
|
||||
time *= shutter_time;
|
||||
time += frame_time;
|
||||
return time;
|
||||
}
|
||||
|
||||
static void render_frame(RenderEngine *engine,
|
||||
Depsgraph *depsgraph,
|
||||
const DRWContext *draw_ctx,
|
||||
RenderLayer *render_layer,
|
||||
const rcti rect,
|
||||
gpencil::Instance &inst,
|
||||
Manager &manager,
|
||||
const bool separated_pass)
|
||||
{
|
||||
Scene *scene = draw_ctx->scene;
|
||||
|
||||
const float aa_radius = clamp_f(scene->r.gauss, 0.0f, 100.0f);
|
||||
|
||||
const bool motion_blur_enabled = (scene->r.mode & R_MBLUR) != 0 &&
|
||||
(draw_ctx->view_layer->layflag & SCE_LAY_MOTION_BLUR) != 0 &&
|
||||
scene->grease_pencil_settings.motion_blur_steps > 0;
|
||||
|
||||
const int motion_steps_count =
|
||||
motion_blur_enabled ? max_ii(1, scene->grease_pencil_settings.motion_blur_steps) * 2 + 1 : 1;
|
||||
const int total_step_count = ceil_to_multiple_u(scene->grease_pencil_settings.aa_samples,
|
||||
motion_steps_count);
|
||||
const int aa_per_step = total_step_count / motion_steps_count;
|
||||
|
||||
const int shutter_position = scene->r.motion_blur_position;
|
||||
const float shutter_time = scene->r.motion_blur_shutter;
|
||||
|
||||
const int initial_frame = scene->r.cfra;
|
||||
const float initial_subframe = scene->r.subframe;
|
||||
const float frame_time = initial_frame + initial_subframe;
|
||||
|
||||
Array<float> time_steps(motion_steps_count);
|
||||
if (motion_blur_enabled) {
|
||||
BKE_curvemapping_changed(&scene->r.mblur_shutter_curve, false);
|
||||
|
||||
Array<float> cdf(CM_TABLE);
|
||||
cdf_from_curvemapping(scene->r.mblur_shutter_curve, cdf);
|
||||
cdf_invert(cdf, time_steps);
|
||||
|
||||
for (float &scene_time : time_steps) {
|
||||
scene_time = shutter_time_to_scene_time(
|
||||
shutter_position, shutter_time, frame_time, scene_time);
|
||||
}
|
||||
}
|
||||
else {
|
||||
BLI_assert(time_steps.size() == 1);
|
||||
time_steps.first() = frame_time;
|
||||
}
|
||||
|
||||
int sample_i = 0;
|
||||
for (const float time : time_steps) {
|
||||
inst.init();
|
||||
|
||||
if (motion_blur_enabled) {
|
||||
DRW_render_set_time(engine, depsgraph, floorf(time), fractf(time));
|
||||
}
|
||||
|
||||
inst.camera = DEG_get_evaluated(depsgraph, RE_GetCamera(engine->re));
|
||||
|
||||
manager.begin_sync();
|
||||
|
||||
/* Loop over all objects and create draw structure. */
|
||||
inst.begin_sync();
|
||||
DRW_render_object_iter(engine, depsgraph, [&](ObjectRef &ob_ref, RenderEngine *, Depsgraph *) {
|
||||
if (!ELEM(ob_ref.object->type, OB_GREASE_PENCIL, OB_LAMP)) {
|
||||
return;
|
||||
}
|
||||
if (!(DRW_object_visibility_in_active_context(ob_ref.object) & OB_VISIBLE_SELF)) {
|
||||
return;
|
||||
}
|
||||
inst.object_sync(ob_ref, manager);
|
||||
});
|
||||
inst.end_sync();
|
||||
|
||||
manager.end_sync();
|
||||
|
||||
for ([[maybe_unused]] const int i : IndexRange(aa_per_step)) {
|
||||
const float2 aa_sample = Instance::antialiasing_sample_get(sample_i, total_step_count) *
|
||||
aa_radius;
|
||||
const float2 aa_offset = 2.0f * aa_sample / float2(inst.render_color_tx.size());
|
||||
render_set_view(engine, depsgraph, aa_offset);
|
||||
render_init_buffers(draw_ctx, inst, engine, render_layer, &rect, separated_pass);
|
||||
|
||||
/* Render the gpencil object and merge the result to the underlying render. */
|
||||
inst.draw(manager);
|
||||
|
||||
/* Weight of this render SSAA sample. The sum of previous samples is weighted by
|
||||
* `1 - weight`. This diminishes after each new sample as we want all samples to be equally
|
||||
* weighted inside the final result (inside the combined buffer). This weighting scheme
|
||||
* allows to always store the resolved result making it ready for in-progress display or
|
||||
* read-back. */
|
||||
const float weight = 1.0f / (1.0f + sample_i);
|
||||
inst.antialiasing_accumulate(manager, weight);
|
||||
|
||||
sample_i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (motion_blur_enabled) {
|
||||
/* Restore original frame number. This is because the render pipeline expects it. */
|
||||
RE_engine_frame_set(engine, initial_frame, initial_subframe);
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::render_to_image(RenderEngine *engine, RenderLayer *render_layer, const rcti rect)
|
||||
{
|
||||
const char *viewname = RE_GetActiveRenderView(engine->re);
|
||||
|
||||
const DRWContext *draw_ctx = DRW_context_get();
|
||||
Depsgraph *depsgraph = draw_ctx->depsgraph;
|
||||
|
||||
if (draw_ctx->view_layer->grease_pencil_flags & GREASE_PENCIL_AS_SEPARATE_PASS) {
|
||||
Render *re = engine->re;
|
||||
RE_create_render_pass(
|
||||
re->result, RE_PASSNAME_GREASE_PENCIL, 4, "RGBA", render_layer->name, viewname, true);
|
||||
}
|
||||
|
||||
gpencil::Instance inst;
|
||||
|
||||
Manager &manager = *DRW_manager_get();
|
||||
|
||||
render_set_view(engine, depsgraph);
|
||||
render_init_buffers(draw_ctx, inst, engine, render_layer, &rect, false);
|
||||
|
||||
render_frame(engine, depsgraph, draw_ctx, render_layer, rect, inst, manager, false);
|
||||
render_result_combined(render_layer, viewname, inst, &rect);
|
||||
|
||||
float *pass_data = RE_RenderLayerGetPass(render_layer, RE_PASSNAME_GREASE_PENCIL, viewname);
|
||||
if (pass_data) {
|
||||
render_frame(engine, depsgraph, draw_ctx, render_layer, rect, inst, manager, true);
|
||||
render_result_separated_pass(pass_data, inst, &rect);
|
||||
}
|
||||
|
||||
/* Transfer depth in the last step, because if we need to render separate pass, we need original
|
||||
* untouched depth buffer. */
|
||||
render_result_z(draw_ctx, render_layer, viewname, inst, &rect);
|
||||
}
|
||||
|
||||
} // namespace blender::draw::gpencil
|
||||
@@ -0,0 +1,62 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPU_shader.hh"
|
||||
|
||||
namespace blender::draw::gpencil {
|
||||
|
||||
using StaticShader = gpu::StaticShader;
|
||||
|
||||
class ShaderCache {
|
||||
private:
|
||||
static gpu::StaticShaderCache<ShaderCache> &get_static_cache()
|
||||
{
|
||||
static gpu::StaticShaderCache<ShaderCache> static_cache;
|
||||
return static_cache;
|
||||
}
|
||||
|
||||
public:
|
||||
static ShaderCache &get()
|
||||
{
|
||||
return get_static_cache().get();
|
||||
}
|
||||
static void release()
|
||||
{
|
||||
get_static_cache().release();
|
||||
}
|
||||
|
||||
/* SMAA antialiasing */
|
||||
StaticShader antialiasing[3] = {{"gpencil_antialiasing_stage_0"},
|
||||
{"gpencil_antialiasing_stage_1"},
|
||||
{"gpencil_antialiasing_stage_2"}};
|
||||
/* Accumulation antialiasing */
|
||||
StaticShader accumulation = {"gpencil_antialiasing_accumulation"};
|
||||
/* GPencil Object rendering */
|
||||
StaticShader geometry = {"gpencil_geometry"};
|
||||
/* All layer blend types in one shader! */
|
||||
StaticShader layer_blend = {"gpencil_layer_blend"};
|
||||
/* Merge the final object depth to the depth buffer. */
|
||||
StaticShader depth_merge = {"gpencil_depth_merge"};
|
||||
/* Merge the final object depth to the depth pass. */
|
||||
StaticShader depth_pass_merge = {"gpencil_depth_pass_merge"};
|
||||
/* Invert the content of the mask buffer. */
|
||||
StaticShader mask_invert = {"gpencil_mask_invert"};
|
||||
/* Effects. */
|
||||
StaticShader fx_composite = {"gpencil_fx_composite"};
|
||||
StaticShader fx_colorize = {"gpencil_fx_colorize"};
|
||||
StaticShader fx_blur = {"gpencil_fx_blur"};
|
||||
StaticShader fx_glow = {"gpencil_fx_glow"};
|
||||
StaticShader fx_pixelize = {"gpencil_fx_pixelize"};
|
||||
StaticShader fx_rim = {"gpencil_fx_rim"};
|
||||
StaticShader fx_shadow = {"gpencil_fx_shadow"};
|
||||
StaticShader fx_transform = {"gpencil_fx_transform"};
|
||||
};
|
||||
|
||||
} // namespace blender::draw::gpencil
|
||||
@@ -0,0 +1,641 @@
|
||||
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_gpencil_legacy_types.h"
|
||||
#include "DNA_shader_fx_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "BLI_link_utils.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_vector.h"
|
||||
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "BKE_camera.h"
|
||||
|
||||
#include "gpencil_engine_private.hh"
|
||||
|
||||
namespace blender::draw::gpencil {
|
||||
|
||||
/* verify if this fx is active */
|
||||
static bool effect_is_active(ShaderFxData *fx, bool is_edit, bool is_viewport)
|
||||
{
|
||||
if (fx == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
PassSimple &Instance::vfx_pass_create(
|
||||
const char *name, DRWState state, gpu::Shader *sh, tObject *tgp_ob, GPUSamplerState sampler)
|
||||
{
|
||||
UNUSED_VARS(name);
|
||||
|
||||
int64_t id = gp_vfx_pool->append_and_get_index({});
|
||||
tVfx &tgp_vfx = (*gp_vfx_pool)[id];
|
||||
tgp_vfx.target_fb = vfx_swapchain_.next().fb;
|
||||
|
||||
PassSimple &pass = *tgp_vfx.vfx_ps;
|
||||
pass.init();
|
||||
pass.state_set(state);
|
||||
pass.shader_set(sh);
|
||||
pass.bind_texture("color_buf", vfx_swapchain_.current().color_tx, sampler);
|
||||
pass.bind_texture("reveal_buf", vfx_swapchain_.current().reveal_tx, sampler);
|
||||
|
||||
vfx_swapchain_.swap();
|
||||
|
||||
BLI_LINKS_APPEND(&tgp_ob->vfx, &tgp_vfx);
|
||||
|
||||
return pass;
|
||||
}
|
||||
|
||||
void Instance::vfx_blur_sync(BlurShaderFxData *fx, Object *ob, tObject *tgp_ob)
|
||||
{
|
||||
if ((fx->samples == 0.0f) || (fx->radius[0] == 0.0f && fx->radius[1] == 0.0f)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((fx->flag & FX_BLUR_DOF_MODE) && this->camera == nullptr) {
|
||||
/* No blur outside camera view (or when DOF is disabled on the camera). */
|
||||
return;
|
||||
}
|
||||
|
||||
const float s = sin(fx->rotation);
|
||||
const float c = cos(fx->rotation);
|
||||
|
||||
float4x4 winmat, persmat;
|
||||
float blur_size[2] = {fx->radius[0], fx->radius[1]};
|
||||
persmat = View::default_get().persmat();
|
||||
const float w = fabsf(mul_project_m4_v3_zfac(persmat.ptr(), ob->object_to_world().location()));
|
||||
|
||||
if (fx->flag & FX_BLUR_DOF_MODE) {
|
||||
/* Compute circle of confusion size. */
|
||||
float coc = (this->dof_params[0] / -w) - this->dof_params[1];
|
||||
copy_v2_fl(blur_size, fabsf(coc));
|
||||
}
|
||||
else {
|
||||
/* Modify by distance to camera and object scale. */
|
||||
winmat = View::default_get().winmat();
|
||||
const float2 vp_size = this->draw_ctx->viewport_size_get();
|
||||
float world_pixel_scale = 1.0f / GPENCIL_PIXEL_FACTOR;
|
||||
float scale = mat4_to_scale(ob->object_to_world().ptr());
|
||||
float distance_factor = world_pixel_scale * scale * winmat[1][1] * vp_size[1] / w;
|
||||
mul_v2_fl(blur_size, distance_factor);
|
||||
}
|
||||
|
||||
gpu::Shader *sh = ShaderCache::get().fx_blur.get();
|
||||
|
||||
DRWState state = DRW_STATE_WRITE_COLOR;
|
||||
if (blur_size[0] > 0.0f) {
|
||||
auto &grp = vfx_pass_create("Fx Blur H", state, sh, tgp_ob);
|
||||
grp.push_constant("offset", float2(blur_size[0] * c, blur_size[0] * s));
|
||||
grp.push_constant("samp_count", max_ii(1, min_ii(fx->samples, blur_size[0])));
|
||||
grp.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
if (blur_size[1] > 0.0f) {
|
||||
auto &grp = vfx_pass_create("Fx Blur V", state, sh, tgp_ob);
|
||||
grp.push_constant("offset", float2(-blur_size[1] * s, blur_size[1] * c));
|
||||
grp.push_constant("samp_count", max_ii(1, min_ii(fx->samples, blur_size[1])));
|
||||
grp.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::vfx_colorize_sync(ColorizeShaderFxData *fx, Object * /*ob*/, tObject *tgp_ob)
|
||||
{
|
||||
gpu::Shader *sh = ShaderCache::get().fx_colorize.get();
|
||||
|
||||
DRWState state = DRW_STATE_WRITE_COLOR;
|
||||
auto &grp = vfx_pass_create("Fx Colorize", state, sh, tgp_ob);
|
||||
grp.push_constant("low_color", float3(fx->low_color));
|
||||
grp.push_constant("high_color", float3(fx->high_color));
|
||||
grp.push_constant("factor", fx->factor);
|
||||
grp.push_constant("mode", fx->mode);
|
||||
grp.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
|
||||
void Instance::vfx_flip_sync(FlipShaderFxData *fx, Object * /*ob*/, tObject *tgp_ob)
|
||||
{
|
||||
float axis_flip[2];
|
||||
axis_flip[0] = (fx->flag & FX_FLIP_HORIZONTAL) ? -1.0f : 1.0f;
|
||||
axis_flip[1] = (fx->flag & FX_FLIP_VERTICAL) ? -1.0f : 1.0f;
|
||||
|
||||
gpu::Shader *sh = ShaderCache::get().fx_transform.get();
|
||||
|
||||
DRWState state = DRW_STATE_WRITE_COLOR;
|
||||
auto &grp = vfx_pass_create("Fx Flip", state, sh, tgp_ob);
|
||||
grp.push_constant("axis_flip", float2(axis_flip));
|
||||
grp.push_constant("wave_offset", float2(0.0f, 0.0f));
|
||||
grp.push_constant("swirl_radius", 0.0f);
|
||||
grp.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
|
||||
void Instance::vfx_rim_sync(RimShaderFxData *fx, Object *ob, tObject *tgp_ob)
|
||||
{
|
||||
float4x4 winmat, persmat;
|
||||
float offset[2] = {float(fx->offset[0]), float(fx->offset[1])};
|
||||
float blur_size[2] = {float(fx->blur[0]), float(fx->blur[1])};
|
||||
winmat = View::default_get().winmat();
|
||||
persmat = View::default_get().persmat();
|
||||
const float2 vp_size = this->draw_ctx->viewport_size_get();
|
||||
const float2 vp_size_inv = 1.0f / vp_size;
|
||||
|
||||
const float w = fabsf(mul_project_m4_v3_zfac(persmat.ptr(), ob->object_to_world().location()));
|
||||
|
||||
/* Modify by distance to camera and object scale. */
|
||||
float world_pixel_scale = 1.0f / GPENCIL_PIXEL_FACTOR;
|
||||
float scale = mat4_to_scale(ob->object_to_world().ptr());
|
||||
float distance_factor = (world_pixel_scale * scale * winmat[1][1] * vp_size[1]) / w;
|
||||
mul_v2_fl(offset, distance_factor);
|
||||
mul_v2_v2(offset, vp_size_inv);
|
||||
mul_v2_fl(blur_size, distance_factor);
|
||||
|
||||
gpu::Shader *sh = ShaderCache::get().fx_rim.get();
|
||||
|
||||
{
|
||||
DRWState state = DRW_STATE_WRITE_COLOR;
|
||||
auto &grp = vfx_pass_create("Fx Rim H", state, sh, tgp_ob);
|
||||
grp.push_constant("blur_dir", float2(blur_size[0] * vp_size_inv[0], 0.0f));
|
||||
grp.push_constant("uv_offset", float2(offset));
|
||||
grp.push_constant("samp_count", max_ii(1, min_ii(fx->samples, blur_size[0])));
|
||||
grp.push_constant("mask_color", float3(fx->mask_rgb));
|
||||
grp.push_constant("is_first_pass", true);
|
||||
grp.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
|
||||
{
|
||||
DRWState state = DRW_STATE_WRITE_COLOR;
|
||||
switch (fx->mode) {
|
||||
case eShaderFxRimMode_Normal:
|
||||
state |= DRW_STATE_BLEND_ALPHA_PREMUL;
|
||||
break;
|
||||
case eShaderFxRimMode_Add:
|
||||
state |= DRW_STATE_BLEND_ADD_FULL;
|
||||
break;
|
||||
case eShaderFxRimMode_Subtract:
|
||||
state |= DRW_STATE_BLEND_SUB;
|
||||
break;
|
||||
case eShaderFxRimMode_Multiply:
|
||||
case eShaderFxRimMode_Divide:
|
||||
case eShaderFxRimMode_Overlay:
|
||||
state |= DRW_STATE_BLEND_MUL;
|
||||
break;
|
||||
}
|
||||
|
||||
zero_v2(offset);
|
||||
|
||||
auto &grp = vfx_pass_create("Fx Rim V", state, sh, tgp_ob);
|
||||
grp.push_constant("blur_dir", float2(0.0f, blur_size[1] * vp_size_inv[1]));
|
||||
grp.push_constant("uv_offset", float2(offset));
|
||||
grp.push_constant("rim_color", float3(fx->rim_rgb));
|
||||
grp.push_constant("samp_count", max_ii(1, min_ii(fx->samples, blur_size[1])));
|
||||
grp.push_constant("blend_mode", fx->mode);
|
||||
grp.push_constant("is_first_pass", false);
|
||||
grp.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
|
||||
if (fx->mode == eShaderFxRimMode_Overlay) {
|
||||
/* We cannot do custom blending on multi-target frame-buffers.
|
||||
* Workaround by doing 2 passes. */
|
||||
grp.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL);
|
||||
grp.push_constant("blend_mode", 999);
|
||||
grp.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::vfx_pixelize_sync(PixelShaderFxData *fx, Object *ob, tObject *tgp_ob)
|
||||
{
|
||||
float4x4 persmat, winmat;
|
||||
float ob_center[3], pixsize_uniform[2];
|
||||
winmat = View::default_get().winmat();
|
||||
persmat = View::default_get().persmat();
|
||||
const float2 vp_size = this->draw_ctx->viewport_size_get();
|
||||
const float2 vp_size_inv = 1.0f / vp_size;
|
||||
float pixel_size[2] = {float(fx->size[0]), float(fx->size[1])};
|
||||
mul_v2_v2(pixel_size, vp_size_inv);
|
||||
|
||||
/* Fixed pixelisation center from object center. */
|
||||
const float w = fabsf(mul_project_m4_v3_zfac(persmat.ptr(), ob->object_to_world().location()));
|
||||
mul_v3_m4v3(ob_center, persmat.ptr(), ob->object_to_world().location());
|
||||
mul_v3_fl(ob_center, 1.0f / w);
|
||||
|
||||
const bool use_antialiasing = ((fx->flag & FX_PIXEL_FILTER_NEAREST) == 0);
|
||||
|
||||
/* Convert to uvs. */
|
||||
mul_v2_fl(ob_center, 0.5f);
|
||||
add_v2_fl(ob_center, 0.5f);
|
||||
|
||||
/* Modify by distance to camera and object scale. */
|
||||
float world_pixel_scale = 1.0f / GPENCIL_PIXEL_FACTOR;
|
||||
float scale = mat4_to_scale(ob->object_to_world().ptr());
|
||||
mul_v2_fl(pixel_size, (world_pixel_scale * scale * winmat[1][1] * vp_size[1]) / w);
|
||||
|
||||
/* Center to texel */
|
||||
madd_v2_v2fl(ob_center, pixel_size, -0.5f);
|
||||
|
||||
gpu::Shader *sh = ShaderCache::get().fx_pixelize.get();
|
||||
|
||||
DRWState state = DRW_STATE_WRITE_COLOR;
|
||||
|
||||
/* Only if pixelated effect is bigger than 1px. */
|
||||
if (pixel_size[0] > vp_size_inv[0]) {
|
||||
copy_v2_fl2(pixsize_uniform, pixel_size[0], vp_size_inv[1]);
|
||||
GPUSamplerState sampler = (use_antialiasing) ? GPUSamplerState::internal_sampler() :
|
||||
GPUSamplerState::default_sampler();
|
||||
|
||||
auto &grp = vfx_pass_create("Fx Pixelize X", state, sh, tgp_ob, sampler);
|
||||
grp.push_constant("target_pixel_size", float2(pixsize_uniform));
|
||||
grp.push_constant("target_pixel_offset", float2(ob_center));
|
||||
grp.push_constant("accum_offset", float2(pixel_size[0], 0.0f));
|
||||
int samp_count = (pixel_size[0] / vp_size_inv[0] > 3.0) ? 2 : 1;
|
||||
grp.push_constant("samp_count", (use_antialiasing ? samp_count : 0));
|
||||
grp.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
|
||||
if (pixel_size[1] > vp_size_inv[1]) {
|
||||
GPUSamplerState sampler = (use_antialiasing) ? GPUSamplerState::internal_sampler() :
|
||||
GPUSamplerState::default_sampler();
|
||||
copy_v2_fl2(pixsize_uniform, vp_size_inv[0], pixel_size[1]);
|
||||
auto &grp = vfx_pass_create("Fx Pixelize Y", state, sh, tgp_ob, sampler);
|
||||
grp.push_constant("target_pixel_size", float2(pixsize_uniform));
|
||||
grp.push_constant("accum_offset", float2(0.0f, pixel_size[1]));
|
||||
int samp_count = (pixel_size[1] / vp_size_inv[1] > 3.0) ? 2 : 1;
|
||||
grp.push_constant("samp_count", (use_antialiasing ? samp_count : 0));
|
||||
grp.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::vfx_shadow_sync(ShadowShaderFxData *fx, Object *ob, tObject *tgp_ob)
|
||||
{
|
||||
const bool use_obj_pivot = (fx->flag & FX_SHADOW_USE_OBJECT) != 0;
|
||||
const bool use_wave = (fx->flag & FX_SHADOW_USE_WAVE) != 0;
|
||||
|
||||
float4x4 uv_mat, winmat, persmat;
|
||||
float rot_center[3];
|
||||
float wave_ofs[3], wave_dir[3], wave_phase, blur_dir[2], tmp[2];
|
||||
float offset[2] = {float(fx->offset[0]), float(fx->offset[1])};
|
||||
float blur_size[2] = {float(fx->blur[0]), float(fx->blur[1])};
|
||||
winmat = View::default_get().winmat();
|
||||
persmat = View::default_get().persmat();
|
||||
const float2 vp_size = this->draw_ctx->viewport_size_get();
|
||||
const float2 vp_size_inv = 1.0f / vp_size;
|
||||
const float ratio = vp_size_inv[1] / vp_size_inv[0];
|
||||
|
||||
copy_v3_v3(rot_center,
|
||||
(use_obj_pivot && fx->object) ? fx->object->object_to_world().location() :
|
||||
ob->object_to_world().location());
|
||||
|
||||
const float w = fabsf(mul_project_m4_v3_zfac(persmat.ptr(), rot_center));
|
||||
mul_v3_m4v3(rot_center, persmat.ptr(), rot_center);
|
||||
mul_v3_fl(rot_center, 1.0f / w);
|
||||
|
||||
/* Modify by distance to camera and object scale. */
|
||||
float world_pixel_scale = 1.0f / GPENCIL_PIXEL_FACTOR;
|
||||
float scale = mat4_to_scale(ob->object_to_world().ptr());
|
||||
float distance_factor = (world_pixel_scale * scale * winmat[1][1] * vp_size[1]) / w;
|
||||
mul_v2_fl(offset, distance_factor);
|
||||
mul_v2_v2(offset, vp_size_inv);
|
||||
mul_v2_fl(blur_size, distance_factor);
|
||||
|
||||
rot_center[0] = rot_center[0] * 0.5f + 0.5f;
|
||||
rot_center[1] = rot_center[1] * 0.5f + 0.5f;
|
||||
|
||||
/* UV transform matrix. (loc, rot, scale) Sent to shader as 2x3 matrix. */
|
||||
unit_m4(uv_mat.ptr());
|
||||
translate_m4(uv_mat.ptr(), rot_center[0], rot_center[1], 0.0f);
|
||||
rescale_m4(uv_mat.ptr(), float3{1.0f / fx->scale[0], 1.0f / fx->scale[1], 1.0f});
|
||||
translate_m4(uv_mat.ptr(), -offset[0], -offset[1], 0.0f);
|
||||
rescale_m4(uv_mat.ptr(), float3{1.0f / ratio, 1.0f, 1.0f});
|
||||
rotate_m4(uv_mat.ptr(), 'Z', fx->rotation);
|
||||
rescale_m4(uv_mat.ptr(), float3{ratio, 1.0f, 1.0f});
|
||||
translate_m4(uv_mat.ptr(), -rot_center[0], -rot_center[1], 0.0f);
|
||||
|
||||
if (use_wave) {
|
||||
float dir[2];
|
||||
if (fx->orientation == 0) {
|
||||
/* Horizontal */
|
||||
copy_v2_fl2(dir, 1.0f, 0.0f);
|
||||
}
|
||||
else {
|
||||
/* Vertical */
|
||||
copy_v2_fl2(dir, 0.0f, 1.0f);
|
||||
}
|
||||
/* This is applied after rotation. Counter the rotation to keep aligned with global axis. */
|
||||
rotate_v2_v2fl(wave_dir, dir, fx->rotation);
|
||||
/* Rotate 90 degrees. */
|
||||
copy_v2_v2(wave_ofs, wave_dir);
|
||||
std::swap(wave_ofs[0], wave_ofs[1]);
|
||||
wave_ofs[1] *= -1.0f;
|
||||
/* Keep world space scaling and aspect ratio. */
|
||||
mul_v2_fl(wave_dir, 1.0f / (max_ff(1e-8f, fx->period) * distance_factor));
|
||||
mul_v2_v2(wave_dir, vp_size);
|
||||
mul_v2_fl(wave_ofs, fx->amplitude * distance_factor);
|
||||
mul_v2_v2(wave_ofs, vp_size_inv);
|
||||
/* Phase start at shadow center. */
|
||||
wave_phase = fx->phase - dot_v2v2(rot_center, wave_dir);
|
||||
}
|
||||
else {
|
||||
zero_v2(wave_dir);
|
||||
zero_v2(wave_ofs);
|
||||
wave_phase = 0.0f;
|
||||
}
|
||||
|
||||
gpu::Shader *sh = ShaderCache::get().fx_shadow.get();
|
||||
|
||||
copy_v2_fl2(blur_dir, blur_size[0] * vp_size_inv[0], 0.0f);
|
||||
|
||||
{
|
||||
DRWState state = DRW_STATE_WRITE_COLOR;
|
||||
auto &grp = vfx_pass_create("Fx Shadow H", state, sh, tgp_ob);
|
||||
grp.push_constant("blur_dir", float2(blur_dir));
|
||||
grp.push_constant("wave_dir", float2(wave_dir));
|
||||
grp.push_constant("wave_offset", float2(wave_ofs));
|
||||
grp.push_constant("wave_phase", wave_phase);
|
||||
grp.push_constant("uv_rot_x", float2(uv_mat[0]));
|
||||
grp.push_constant("uv_rot_y", float2(uv_mat[1]));
|
||||
grp.push_constant("uv_offset", float2(uv_mat[3]));
|
||||
grp.push_constant("samp_count", max_ii(1, min_ii(fx->samples, blur_size[0])));
|
||||
grp.push_constant("is_first_pass", true);
|
||||
grp.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
|
||||
unit_m4(uv_mat.ptr());
|
||||
zero_v2(wave_ofs);
|
||||
|
||||
/* Reset the `uv_mat` to account for rotation in the Y-axis (Shadow-V parameter). */
|
||||
copy_v2_fl2(tmp, 0.0f, blur_size[1]);
|
||||
rotate_v2_v2fl(blur_dir, tmp, -fx->rotation);
|
||||
mul_v2_v2(blur_dir, vp_size_inv);
|
||||
|
||||
{
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA_PREMUL;
|
||||
auto &grp = vfx_pass_create("Fx Shadow V", state, sh, tgp_ob);
|
||||
grp.push_constant("shadow_color", float4(fx->shadow_rgba));
|
||||
grp.push_constant("blur_dir", float2(blur_dir));
|
||||
grp.push_constant("wave_offset", float2(wave_ofs));
|
||||
grp.push_constant("uv_rot_x", float2(uv_mat[0]));
|
||||
grp.push_constant("uv_rot_y", float2(uv_mat[1]));
|
||||
grp.push_constant("uv_offset", float2(uv_mat[3]));
|
||||
grp.push_constant("samp_count", max_ii(1, min_ii(fx->samples, blur_size[1])));
|
||||
grp.push_constant("is_first_pass", false);
|
||||
grp.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::vfx_glow_sync(GlowShaderFxData *fx, Object * /*ob*/, tObject *tgp_ob)
|
||||
{
|
||||
const bool use_glow_under = (fx->flag & FX_GLOW_USE_ALPHA) != 0;
|
||||
const float s = sin(fx->rotation);
|
||||
const float c = cos(fx->rotation);
|
||||
|
||||
gpu::Shader *sh = ShaderCache::get().fx_glow.get();
|
||||
|
||||
float ref_col[4];
|
||||
|
||||
if (fx->mode == eShaderFxGlowMode_Luminance) {
|
||||
/* Only pass in the first value for luminance. */
|
||||
ref_col[0] = fx->threshold;
|
||||
ref_col[1] = -1.0f;
|
||||
ref_col[2] = -1.0f;
|
||||
ref_col[3] = -1.0f;
|
||||
}
|
||||
else {
|
||||
/* First three values are the RGB for the selected color, last value the threshold. */
|
||||
copy_v3_v3(ref_col, fx->select_color);
|
||||
ref_col[3] = fx->threshold;
|
||||
}
|
||||
|
||||
DRWState state = DRW_STATE_WRITE_COLOR;
|
||||
auto &grp = vfx_pass_create("Fx Glow H", state, sh, tgp_ob);
|
||||
grp.push_constant("offset", float2(fx->blur[0] * c, fx->blur[0] * s));
|
||||
grp.push_constant("samp_count", max_ii(1, min_ii(fx->samples, fx->blur[0])));
|
||||
grp.push_constant("threshold", float4(ref_col));
|
||||
grp.push_constant("glow_color", float4(fx->glow_color));
|
||||
grp.push_constant("glow_under", use_glow_under);
|
||||
grp.push_constant("first_pass", true);
|
||||
grp.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
|
||||
state = DRW_STATE_WRITE_COLOR;
|
||||
/* Blending: Force blending. */
|
||||
switch (fx->blend_mode) {
|
||||
case eGplBlendMode_Regular:
|
||||
state |= DRW_STATE_BLEND_ALPHA_PREMUL;
|
||||
break;
|
||||
case eGplBlendMode_Add:
|
||||
state |= DRW_STATE_BLEND_ADD_FULL;
|
||||
break;
|
||||
case eGplBlendMode_Subtract:
|
||||
state |= DRW_STATE_BLEND_SUB;
|
||||
break;
|
||||
case eGplBlendMode_Multiply:
|
||||
case eGplBlendMode_Divide:
|
||||
state |= DRW_STATE_BLEND_MUL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Small Hack: We ask for RGBA16F buffer if using use_glow_under to store original
|
||||
* revealage in alpha channel. */
|
||||
if (fx->blend_mode == eGplBlendMode_Subtract || use_glow_under) {
|
||||
/* For this effect to propagate, we need a signed floating point buffer. */
|
||||
this->use_signed_fb = true;
|
||||
}
|
||||
|
||||
{
|
||||
auto &grp = vfx_pass_create("Fx Glow V", state, sh, tgp_ob);
|
||||
grp.push_constant("offset", float2(-fx->blur[1] * s, fx->blur[1] * c));
|
||||
grp.push_constant("samp_count", max_ii(1, min_ii(fx->samples, fx->blur[0])));
|
||||
grp.push_constant("threshold", float4{-1.0f, -1.0f, -1.0f, -1.0});
|
||||
grp.push_constant("glow_color", float4{1.0f, 1.0f, 1.0f, fx->glow_color[3]});
|
||||
grp.push_constant("first_pass", false);
|
||||
grp.push_constant("blend_mode", fx->blend_mode);
|
||||
grp.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::vfx_wave_sync(WaveShaderFxData *fx, Object *ob, tObject *tgp_ob)
|
||||
{
|
||||
float4x4 winmat, persmat;
|
||||
float wave_center[3];
|
||||
float wave_ofs[3], wave_dir[3], wave_phase;
|
||||
winmat = View::default_get().winmat();
|
||||
persmat = View::default_get().persmat();
|
||||
const float2 vp_size = this->draw_ctx->viewport_size_get();
|
||||
const float2 vp_size_inv = 1.0f / vp_size;
|
||||
|
||||
const float w = fabsf(mul_project_m4_v3_zfac(persmat.ptr(), ob->object_to_world().location()));
|
||||
mul_v3_m4v3(wave_center, persmat.ptr(), ob->object_to_world().location());
|
||||
mul_v3_fl(wave_center, 1.0f / w);
|
||||
|
||||
/* Modify by distance to camera and object scale. */
|
||||
float world_pixel_scale = 1.0f / GPENCIL_PIXEL_FACTOR;
|
||||
float scale = mat4_to_scale(ob->object_to_world().ptr());
|
||||
float distance_factor = (world_pixel_scale * scale * winmat[1][1] * vp_size[1]) / w;
|
||||
|
||||
wave_center[0] = wave_center[0] * 0.5f + 0.5f;
|
||||
wave_center[1] = wave_center[1] * 0.5f + 0.5f;
|
||||
|
||||
if (fx->orientation == 0) {
|
||||
/* Horizontal */
|
||||
copy_v2_fl2(wave_dir, 1.0f, 0.0f);
|
||||
}
|
||||
else {
|
||||
/* Vertical */
|
||||
copy_v2_fl2(wave_dir, 0.0f, 1.0f);
|
||||
}
|
||||
/* Rotate 90 degrees. */
|
||||
copy_v2_v2(wave_ofs, wave_dir);
|
||||
std::swap(wave_ofs[0], wave_ofs[1]);
|
||||
wave_ofs[1] *= -1.0f;
|
||||
/* Keep world space scaling and aspect ratio. */
|
||||
mul_v2_fl(wave_dir, 1.0f / (max_ff(1e-8f, fx->period) * distance_factor));
|
||||
mul_v2_v2(wave_dir, vp_size);
|
||||
mul_v2_fl(wave_ofs, fx->amplitude * distance_factor);
|
||||
mul_v2_v2(wave_ofs, vp_size_inv);
|
||||
/* Phase start at shadow center. */
|
||||
wave_phase = fx->phase - dot_v2v2(wave_center, wave_dir);
|
||||
|
||||
gpu::Shader *sh = ShaderCache::get().fx_transform.get();
|
||||
|
||||
DRWState state = DRW_STATE_WRITE_COLOR;
|
||||
auto &grp = vfx_pass_create("Fx Wave", state, sh, tgp_ob);
|
||||
grp.push_constant("axis_flip", float2(1.0f, 1.0f));
|
||||
grp.push_constant("wave_dir", float2(wave_dir));
|
||||
grp.push_constant("wave_offset", float2(wave_ofs));
|
||||
grp.push_constant("wave_phase", wave_phase);
|
||||
grp.push_constant("swirl_radius", 0.0f);
|
||||
grp.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
|
||||
void Instance::vfx_swirl_sync(SwirlShaderFxData *fx, Object * /*ob*/, tObject *tgp_ob)
|
||||
{
|
||||
if (fx->object == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
float4x4 winmat, persmat;
|
||||
float swirl_center[3];
|
||||
winmat = View::default_get().winmat();
|
||||
persmat = View::default_get().persmat();
|
||||
const float2 vp_size = this->draw_ctx->viewport_size_get();
|
||||
|
||||
copy_v3_v3(swirl_center, fx->object->object_to_world().location());
|
||||
|
||||
const float w = fabsf(mul_project_m4_v3_zfac(persmat.ptr(), swirl_center));
|
||||
mul_v3_m4v3(swirl_center, persmat.ptr(), swirl_center);
|
||||
mul_v3_fl(swirl_center, 1.0f / w);
|
||||
|
||||
/* Modify by distance to camera and object scale. */
|
||||
float world_pixel_scale = 1.0f / GPENCIL_PIXEL_FACTOR;
|
||||
float scale = mat4_to_scale(fx->object->object_to_world().ptr());
|
||||
float distance_factor = (world_pixel_scale * scale * winmat[1][1] * vp_size[1]) / w;
|
||||
|
||||
mul_v2_fl(swirl_center, 0.5f);
|
||||
add_v2_fl(swirl_center, 0.5f);
|
||||
mul_v2_v2(swirl_center, vp_size);
|
||||
|
||||
float radius = fx->radius * distance_factor;
|
||||
if (radius < 1.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpu::Shader *sh = ShaderCache::get().fx_transform.get();
|
||||
|
||||
DRWState state = DRW_STATE_WRITE_COLOR;
|
||||
auto &grp = vfx_pass_create("Fx Flip", state, sh, tgp_ob);
|
||||
grp.push_constant("axis_flip", float2(1.0f, 1.0f));
|
||||
grp.push_constant("wave_offset", float2(0.0f, 0.0f));
|
||||
grp.push_constant("swirl_center", float2(swirl_center));
|
||||
grp.push_constant("swirl_angle", fx->angle);
|
||||
grp.push_constant("swirl_radius", radius);
|
||||
grp.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
}
|
||||
|
||||
void Instance::vfx_sync(Object *ob, tObject *tgp_ob)
|
||||
{
|
||||
const bool is_edit_mode = ELEM(
|
||||
ob->mode, OB_MODE_EDIT, OB_MODE_SCULPT_GREASE_PENCIL, OB_MODE_WEIGHT_GREASE_PENCIL);
|
||||
|
||||
vfx_swapchain_.next().fb = &layer_fb;
|
||||
vfx_swapchain_.next().color_tx = &color_layer_tx;
|
||||
vfx_swapchain_.next().reveal_tx = &reveal_layer_tx;
|
||||
vfx_swapchain_.current().fb = &object_fb;
|
||||
vfx_swapchain_.current().color_tx = &color_object_tx;
|
||||
vfx_swapchain_.current().reveal_tx = &reveal_object_tx;
|
||||
|
||||
/* If simplify enabled, nothing more to do. */
|
||||
if (!this->simplify_fx) {
|
||||
for (ShaderFxData &fx : ob->shader_fx) {
|
||||
if (effect_is_active(&fx, is_edit_mode, this->is_viewport)) {
|
||||
switch (fx.type) {
|
||||
case eShaderFxType_Blur:
|
||||
vfx_blur_sync(reinterpret_cast<BlurShaderFxData *>(&fx), ob, tgp_ob);
|
||||
break;
|
||||
case eShaderFxType_Colorize:
|
||||
vfx_colorize_sync(reinterpret_cast<ColorizeShaderFxData *>(&fx), ob, tgp_ob);
|
||||
break;
|
||||
case eShaderFxType_Flip:
|
||||
vfx_flip_sync(reinterpret_cast<FlipShaderFxData *>(&fx), ob, tgp_ob);
|
||||
break;
|
||||
case eShaderFxType_Pixel:
|
||||
vfx_pixelize_sync(reinterpret_cast<PixelShaderFxData *>(&fx), ob, tgp_ob);
|
||||
break;
|
||||
case eShaderFxType_Rim:
|
||||
vfx_rim_sync(reinterpret_cast<RimShaderFxData *>(&fx), ob, tgp_ob);
|
||||
break;
|
||||
case eShaderFxType_Shadow:
|
||||
vfx_shadow_sync(reinterpret_cast<ShadowShaderFxData *>(&fx), ob, tgp_ob);
|
||||
break;
|
||||
case eShaderFxType_Glow:
|
||||
vfx_glow_sync(reinterpret_cast<GlowShaderFxData *>(&fx), ob, tgp_ob);
|
||||
break;
|
||||
case eShaderFxType_Swirl:
|
||||
vfx_swirl_sync(reinterpret_cast<SwirlShaderFxData *>(&fx), ob, tgp_ob);
|
||||
break;
|
||||
case eShaderFxType_Wave:
|
||||
vfx_wave_sync(reinterpret_cast<WaveShaderFxData *>(&fx), ob, tgp_ob);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((!this->simplify_fx && tgp_ob->vfx.first != nullptr) || tgp_ob->do_mat_holdout) {
|
||||
/* We need an extra pass to combine result to main buffer. */
|
||||
vfx_swapchain_.next().fb = &this->gpencil_fb;
|
||||
|
||||
gpu::Shader *sh = ShaderCache::get().fx_composite.get();
|
||||
|
||||
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_MUL;
|
||||
auto &grp = vfx_pass_create("GPencil Object Compose", state, sh, tgp_ob);
|
||||
grp.push_constant("is_first_pass", true);
|
||||
grp.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
|
||||
/* We cannot do custom blending on multi-target frame-buffers.
|
||||
* Workaround by doing 2 passes. */
|
||||
grp.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL);
|
||||
grp.push_constant("is_first_pass", false);
|
||||
grp.draw_procedural(GPU_PRIM_TRIS, 1, 3);
|
||||
|
||||
this->use_object_fb = true;
|
||||
this->use_layer_fb = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::draw::gpencil
|
||||
@@ -0,0 +1,100 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPU_shader_shared_utils.hh"
|
||||
|
||||
enum [[host_shared]] gpMaterialFlag : uint32_t {
|
||||
GP_FLAG_NONE = 0u,
|
||||
GP_STROKE_ALIGNMENT_STROKE = 1u,
|
||||
GP_STROKE_ALIGNMENT_OBJECT = 2u,
|
||||
GP_STROKE_ALIGNMENT_FIXED = 3u,
|
||||
GP_STROKE_ALIGNMENT = 0x3u,
|
||||
GP_STROKE_OVERLAP = (1u << 2u),
|
||||
GP_STROKE_TEXTURE_USE = (1u << 3u),
|
||||
GP_STROKE_TEXTURE_STENCIL = (1u << 4u),
|
||||
GP_STROKE_TEXTURE_PREMUL = (1u << 5u),
|
||||
GP_STROKE_DOTS = (1u << 6u),
|
||||
GP_STROKE_HOLDOUT = (1u << 7u),
|
||||
GP_FILL_HOLDOUT = (1u << 8u),
|
||||
GP_FILL_TEXTURE_USE = (1u << 10u),
|
||||
GP_FILL_TEXTURE_PREMUL = (1u << 11u),
|
||||
GP_FILL_TEXTURE_CLIP = (1u << 12u),
|
||||
GP_FILL_GRADIENT_USE = (1u << 13u),
|
||||
GP_FILL_GRADIENT_RADIAL = (1u << 14u),
|
||||
GP_FILL = (1u << 15u),
|
||||
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 | GP_FILL),
|
||||
GP_DOTS_PLACEMENT_MODE = ((1u << 16u) | (1u << 17u)),
|
||||
GP_DOTS_PLACEMENT_MODE_COUNT = 0u,
|
||||
GP_DOTS_PLACEMENT_MODE_DENSITY = (1u << 16u),
|
||||
GP_DOTS_PLACEMENT_MODE_RADIUS = (1u << 17u),
|
||||
GP_DOTS_USE_RANDOMIZATION = (1u << 18u),
|
||||
};
|
||||
|
||||
enum [[host_shared]] gpLightType : uint32_t {
|
||||
GP_LIGHT_TYPE_POINT = 0u,
|
||||
GP_LIGHT_TYPE_SPOT = 1u,
|
||||
GP_LIGHT_TYPE_SUN = 2u,
|
||||
GP_LIGHT_TYPE_AMBIENT = 3u,
|
||||
};
|
||||
|
||||
#define GP_IS_STROKE_VERTEX_BIT (1 << 30)
|
||||
#define GP_VERTEX_ID_SHIFT 2
|
||||
#define GP_CORNER_TYPE_ROUND_BITS 0u
|
||||
#define GP_CORNER_TYPE_BEVEL_BITS 63u
|
||||
#define GP_CORNER_TYPE_MITER_NUMBER 62u
|
||||
|
||||
/* Avoid compiler funkiness with enum types not being strongly typed in C. */
|
||||
#ifndef GPU_SHADER
|
||||
# define gpMaterialFlag uint
|
||||
#endif
|
||||
|
||||
struct [[host_shared]] gpMaterial {
|
||||
float4 stroke_color;
|
||||
float4 fill_color;
|
||||
float4 fill_mix_color;
|
||||
float4 fill_uv_rot_scale;
|
||||
#ifndef GPU_SHADER
|
||||
float2 fill_uv_offset;
|
||||
float2 alignment_rot;
|
||||
float stroke_texture_mix;
|
||||
float stroke_u_scale;
|
||||
float fill_texture_mix;
|
||||
gpMaterialFlag flag;
|
||||
#else
|
||||
/* Some drivers are completely messing the alignment or the fetches here.
|
||||
* We are forced to pack these into float4 otherwise we only get 0.0 as value. */
|
||||
/* NOTE(@fclem): This was the case on MacOS OpenGL implementation.
|
||||
* This might be fixed in newer APIs. */
|
||||
float4 packed1;
|
||||
float4 packed2;
|
||||
# define _fill_uv_offset packed1.xy
|
||||
# define _alignment_rot packed1.zw
|
||||
# define _stroke_texture_mix packed2.x
|
||||
# define _stroke_u_scale packed2.y
|
||||
# define _fill_texture_mix packed2.z
|
||||
/** NOTE(@fclem): Needs floatBitsToUint(). */
|
||||
# define _flag packed2.w
|
||||
#endif
|
||||
uint4 random_packed;
|
||||
};
|
||||
|
||||
struct [[host_shared]] gpLight {
|
||||
packed_float3 light_color; /* Not using color because of macro in overlay_extra_wire_base. */
|
||||
enum gpLightType type;
|
||||
packed_float3 right;
|
||||
float spot_size;
|
||||
packed_float3 up;
|
||||
float spot_blend;
|
||||
packed_float3 forward;
|
||||
float _pad0;
|
||||
packed_float3 position;
|
||||
float _pad1;
|
||||
};
|
||||
|
||||
#ifndef GPU_SHADER
|
||||
# undef gpMaterialFlag
|
||||
#endif
|
||||
@@ -0,0 +1,55 @@
|
||||
# SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(INC_GLSL
|
||||
.
|
||||
..
|
||||
|
||||
# For variadic macros
|
||||
../../../../blenlib
|
||||
|
||||
../../../intern
|
||||
../../../intern/shaders
|
||||
|
||||
../../../../gpu
|
||||
../../../../gpu/intern
|
||||
../../../../gpu/shaders
|
||||
../../../../gpu/shaders/common
|
||||
../../../../gpu/shaders/infos
|
||||
)
|
||||
|
||||
set(SRC_GLSL_VERT
|
||||
gpencil_antialiasing_vert.glsl
|
||||
gpencil_depth_merge_vert.glsl
|
||||
gpencil_fullscreen_vert.glsl
|
||||
gpencil_vert.glsl
|
||||
)
|
||||
|
||||
set(SRC_GLSL_FRAG
|
||||
gpencil_antialiasing_accumulation_frag.glsl
|
||||
gpencil_antialiasing_frag.glsl
|
||||
gpencil_depth_merge_frag.glsl
|
||||
gpencil_depth_pass_merge_frag.glsl
|
||||
gpencil_frag.glsl
|
||||
gpencil_layer_blend_frag.glsl
|
||||
gpencil_mask_invert_frag.glsl
|
||||
gpencil_vfx_frag.glsl
|
||||
)
|
||||
|
||||
set(SRC_GLSL_COMP
|
||||
)
|
||||
|
||||
set(SRC_GLSL_LIB
|
||||
gpencil_common_lib.glsl
|
||||
)
|
||||
|
||||
# Compile shaders with shader code.
|
||||
if(WITH_GPU_SHADER_CPP_COMPILATION)
|
||||
compile_sources_as_cpp(gpencil_cpp_shaders_vert "${SRC_GLSL_VERT}" "GPU_VERTEX_SHADER")
|
||||
compile_sources_as_cpp(gpencil_cpp_shaders_frag "${SRC_GLSL_FRAG}" "GPU_FRAGMENT_SHADER")
|
||||
# compile_sources_as_cpp(gpencil_cpp_shaders_comp "${SRC_GLSL_COMP}" "GPU_COMPUTE_SHADER")
|
||||
# Only enable to make sure they compile on their own.
|
||||
# Otherwise it creates a warning about `pragma once`.
|
||||
# compile_sources_as_cpp(gpencil_cpp_shaders_lib "${SRC_GLSL_LIB}" "GPU_LIBRARY_SHADER")
|
||||
endif()
|
||||
@@ -0,0 +1,36 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpencil_infos.hh"
|
||||
|
||||
FRAGMENT_SHADER_CREATE_INFO(gpencil_antialiasing_accumulation)
|
||||
|
||||
float4 colorspace_scene_to_perceptual(float4 color)
|
||||
{
|
||||
return float4(log2(color.rgb + 0.5f), color.a);
|
||||
}
|
||||
|
||||
float4 colorspace_perceptual_to_scene(float4 color)
|
||||
{
|
||||
return float4(exp2(color.rgb) - 0.5f, color.a);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
int2 texel = int2(gl_FragCoord.xy);
|
||||
float4 data_src = colorspace_scene_to_perceptual(
|
||||
max(float4(0.0f), imageLoadFast(src_img, texel)));
|
||||
float4 data_dst = colorspace_scene_to_perceptual(
|
||||
max(float4(0.0f), imageLoadFast(dst_img, texel)));
|
||||
float4 result = data_src * weight_src;
|
||||
if (weight_dst > 0.0f) {
|
||||
/* Avoid uncleared data to mess with the result value. */
|
||||
result += data_dst * weight_dst;
|
||||
}
|
||||
if (data_src.a == 1.0f && data_dst.a == 1.0f) {
|
||||
/* Avoid float imprecision leading to non fully opaque renders. */
|
||||
result.a = 1.0f;
|
||||
}
|
||||
imageStoreFast(dst_img, texel, colorspace_perceptual_to_scene(result));
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpencil_infos.hh"
|
||||
|
||||
FRAGMENT_SHADER_CREATE_INFO(gpencil_antialiasing_stage_1)
|
||||
|
||||
#include "gpu_shader_smaa_lib.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
float4 offset[3];
|
||||
offset[0] = offset0;
|
||||
offset[1] = offset1;
|
||||
offset[2] = offset2;
|
||||
|
||||
#if SMAA_STAGE == 0
|
||||
/* Detect edges in color and revealage buffer. */
|
||||
out_edges = SMAALumaEdgeDetectionPS(uvs, offset, color_tx);
|
||||
out_edges = max(out_edges, SMAALumaEdgeDetectionPS(uvs, offset, reveal_tx));
|
||||
/* Discard if there is no edge. */
|
||||
if (dot(out_edges, float2(1.0f, 1.0f)) == 0.0f) {
|
||||
gpu_discard_fragment();
|
||||
return;
|
||||
}
|
||||
|
||||
#elif SMAA_STAGE == 1
|
||||
out_weights = SMAABlendingWeightCalculationPS(
|
||||
uvs, pixcoord, offset, edges_tx, area_tx, search_tx, float4(0));
|
||||
|
||||
#elif SMAA_STAGE == 2
|
||||
/* Resolve both buffers. */
|
||||
if (do_anti_aliasing) {
|
||||
out_color = SMAANeighborhoodBlendingPS(uvs, offset[0], color_tx, blend_tx);
|
||||
out_reveal = SMAANeighborhoodBlendingPS(uvs, offset[0], reveal_tx, blend_tx);
|
||||
}
|
||||
else {
|
||||
out_color = texture(color_tx, uvs);
|
||||
out_reveal = texture(reveal_tx, uvs);
|
||||
}
|
||||
|
||||
/* Revealage, how much light passes through. */
|
||||
/* Average for alpha channel. */
|
||||
out_reveal.a = clamp(dot(out_reveal.rgb, float3(0.333334f)), 0.0f, 1.0f);
|
||||
/* Color buffer is already pre-multiplied. Just add it to the color. */
|
||||
/* Add the alpha. */
|
||||
out_color.a = 1.0f - out_reveal.a;
|
||||
|
||||
if (only_alpha) {
|
||||
/* Special case in wire-frame X-ray mode. */
|
||||
out_color = float4(0.0f);
|
||||
out_reveal.rgb = out_reveal.aaa;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpencil_infos.hh"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpencil_antialiasing_stage_1)
|
||||
|
||||
#include "gpu_shader_smaa_lib.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
int v = gl_VertexID % 3;
|
||||
float x = -1.0f + float((v & 1) << 2);
|
||||
float y = -1.0f + float((v & 2) << 1);
|
||||
gl_Position = float4(x, y, 1.0f, 1.0f);
|
||||
uvs = (gl_Position.xy + 1.0f) * 0.5f;
|
||||
|
||||
float4 offset[3];
|
||||
|
||||
#if SMAA_STAGE == 0
|
||||
SMAAEdgeDetectionVS(uvs, offset);
|
||||
#elif SMAA_STAGE == 1
|
||||
SMAABlendingWeightCalculationVS(uvs, pixcoord, offset);
|
||||
#elif SMAA_STAGE == 2
|
||||
SMAANeighborhoodBlendingVS(uvs, offset[0]);
|
||||
#endif
|
||||
|
||||
offset0 = offset[0];
|
||||
offset1 = offset[1];
|
||||
offset2 = offset[2];
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
/* Must match eGPLayerBlendModes */
|
||||
#define MODE_REGULAR 0
|
||||
#define MODE_HARDLIGHT 1
|
||||
#define MODE_ADD 2
|
||||
#define MODE_SUB 3
|
||||
#define MODE_MULTIPLY 4
|
||||
#define MODE_DIVIDE 5
|
||||
#define MODE_HARDLIGHT_SECOND_PASS 999
|
||||
|
||||
void blend_mode_output(
|
||||
int blending_mode, float4 color, float opacity, float4 &frag_color, float4 &frag_revealage)
|
||||
{
|
||||
switch (blending_mode) {
|
||||
case MODE_REGULAR:
|
||||
/* Reminder: Blending func is pre-multiply alpha blend
|
||||
* `(dst.rgba * (1 - src.a) + src.rgb)`. */
|
||||
color *= opacity;
|
||||
frag_color = color;
|
||||
frag_revealage = float4(0.0f, 0.0f, 0.0f, color.a);
|
||||
break;
|
||||
case MODE_MULTIPLY:
|
||||
/* Reminder: Blending func is multiply blend `(dst.rgba * src.rgba)`. */
|
||||
frag_revealage = frag_color = (1.0f - color.a * opacity) + color * opacity;
|
||||
break;
|
||||
case MODE_DIVIDE:
|
||||
/* Reminder: Blending func is multiply blend `(dst.rgba * src.rgba)`. */
|
||||
color.a *= opacity;
|
||||
frag_revealage = frag_color = clamp(
|
||||
1.0f / max(float4(1e-6f), 1.0f - color * color.a), 0.0f, 1e18f);
|
||||
break;
|
||||
case MODE_HARDLIGHT: {
|
||||
/* Reminder: Blending func is multiply blend `(dst.rgba * src.rgba)`. */
|
||||
/**
|
||||
* We need to separate the overlay equation into 2 term (one multiply and one add).
|
||||
* This is the standard overlay equation (per channel):
|
||||
* `rtn = (src < 0.5f) ? (2.0f * src * dst) : (1.0f - 2.0f * (1.0f - src) * (1.0f - dst));`
|
||||
* We rewrite the second branch like this:
|
||||
* `rtn = 1 - 2 * (1 - src) * (1 - dst);`
|
||||
* `rtn = 1 - 2 (1 - dst + src * dst - src);`
|
||||
* `rtn = 1 - 2 (1 - dst * (1 - src) - src);`
|
||||
* `rtn = 1 - 2 + dst * (2 - 2 * src) + 2 * src;`
|
||||
* `rtn = (- 1 + 2 * src) + dst * (2 - 2 * src);`
|
||||
*/
|
||||
color = mix(float4(0.5f), color, color.a * opacity);
|
||||
float4 s = step(-0.5f, -color);
|
||||
frag_revealage = frag_color = 2.0f * s + 2.0f * color * (1.0f - s * 2.0f);
|
||||
frag_revealage = max(float4(0.0f), frag_revealage);
|
||||
break;
|
||||
}
|
||||
case MODE_HARDLIGHT_SECOND_PASS:
|
||||
/* Reminder: Blending func is additive blend `(dst.rgba + src.rgba)`. */
|
||||
color = mix(float4(0.5f), color, color.a * opacity);
|
||||
frag_revealage = frag_color = (-1.0f + 2.0f * color) * step(-0.5f, -color);
|
||||
frag_revealage = max(float4(0.0f), frag_revealage);
|
||||
break;
|
||||
case MODE_SUB:
|
||||
case MODE_ADD:
|
||||
/* Reminder: Blending func is additive / subtractive blend `(dst.rgba +/- src.rgba)`. */
|
||||
frag_color = color * color.a * opacity;
|
||||
frag_revealage = float4(0.0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpencil_infos.hh"
|
||||
|
||||
FRAGMENT_SHADER_CREATE_INFO(gpencil_depth_merge)
|
||||
|
||||
void main()
|
||||
{
|
||||
float depth = textureLod(depth_buf, gl_FragCoord.xy / float2(textureSize(depth_buf, 0)), 0).r;
|
||||
if (stroke_order3d) {
|
||||
gl_FragDepth = depth;
|
||||
}
|
||||
else {
|
||||
gl_FragDepth = (depth != 0.0f) ? gl_FragCoord.z : 1.0f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpencil_infos.hh"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpencil_depth_merge)
|
||||
|
||||
#include "draw_view_lib.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
int v = gl_VertexID % 3;
|
||||
float x = -1.0f + float((v & 1) << 2);
|
||||
float y = -1.0f + float((v & 2) << 1);
|
||||
gl_Position = drw_view().winmat *
|
||||
(drw_view().viewmat * (gp_model_matrix * float4(x, y, 0.0f, 1.0f)));
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/* Used to merge the object's depth to the viewport compositor depth pass. This is essentially the
|
||||
* same as the gpencil_depth_merge_frag.glsl shader but writes to the pass as an image output
|
||||
* instead of a depth frame buffer. However, it actually writes scene linear depth with manual
|
||||
* depth comparison. */
|
||||
|
||||
#include "infos/gpencil_infos.hh"
|
||||
|
||||
#include "draw_view_lib.glsl"
|
||||
|
||||
FRAGMENT_SHADER_CREATE_INFO(gpencil_depth_pass_merge)
|
||||
|
||||
void main()
|
||||
{
|
||||
const int2 texel = int2(gl_FragCoord.xy);
|
||||
const float2 normalized_coordinates = gl_FragCoord.xy / float2(textureSize(depth_buf, 0));
|
||||
|
||||
const float depth_3d = textureLod(depth_buf, normalized_coordinates, 0).x;
|
||||
const float depth_2d = depth_3d != 0.0f ? gl_FragCoord.z : 1.0f;
|
||||
const float depth = stroke_order3d ? depth_3d : depth_2d;
|
||||
const float view_depth = -drw_depth_screen_to_view(depth);
|
||||
|
||||
const float scene_depth = imageLoad(depth_pass_img, texel).x;
|
||||
|
||||
const float combined_depth = min(scene_depth, view_depth);
|
||||
imageStore(depth_pass_img, texel, float4(combined_depth));
|
||||
}
|
||||
@@ -0,0 +1,583 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpencil_infos.hh"
|
||||
|
||||
FRAGMENT_SHADER_CREATE_INFO(gpencil_geometry)
|
||||
|
||||
#include "draw_colormanagement_lib.glsl"
|
||||
#include "draw_grease_pencil_lib.glsl"
|
||||
#include "gpu_shader_common_color_utils.glsl"
|
||||
#include "gpu_shader_common_hash.glsl"
|
||||
#include "gpu_shader_math_vector_lib.glsl"
|
||||
|
||||
float3 gpencil_lighting()
|
||||
{
|
||||
float3 light_accum = float3(0.0f);
|
||||
for (int i = 0; i < GPENCIL_LIGHT_BUFFER_LEN; i++) {
|
||||
if (float3(gp_lights[i].light_color).x == -1.0f) {
|
||||
break;
|
||||
}
|
||||
float3 L = gp_lights[i].position - gp_interp.pos;
|
||||
float vis = 1.0f;
|
||||
gpLightType type = gp_lights[i].type;
|
||||
/* Spot Attenuation. */
|
||||
if (type == GP_LIGHT_TYPE_SPOT) {
|
||||
float3x3 rot_scale = float3x3(gp_lights[i].right, gp_lights[i].up, gp_lights[i].forward);
|
||||
float3 local_L = rot_scale * L;
|
||||
local_L /= abs(local_L.z);
|
||||
float ellipse = inversesqrt(length_squared(local_L));
|
||||
vis *= smoothstep(0.0f, 1.0f, (ellipse - gp_lights[i].spot_size) / gp_lights[i].spot_blend);
|
||||
/* Also mask +Z cone. */
|
||||
vis *= step(0.0f, 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.0f;
|
||||
}
|
||||
/* Lambertian falloff */
|
||||
if (type != GP_LIGHT_TYPE_AMBIENT) {
|
||||
L /= sqrt(L_len_sqr);
|
||||
vis *= clamp(dot(gp_normal, L), 0.0f, 1.0f);
|
||||
}
|
||||
light_accum += vis * gp_lights[i].light_color;
|
||||
}
|
||||
/* Clamp to avoid NaNs. */
|
||||
return clamp(light_accum, 0.0f, 1e10f);
|
||||
}
|
||||
|
||||
/* dx and dy are only needed for dots and squares. */
|
||||
float4 get_color(float2 uv, float2 dx, float2 dy)
|
||||
{
|
||||
float4 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 = textureGrad(gp_stroke_tx, uv, dx, dy);
|
||||
if (premul && !(col.a == 0.0f || col.a == 1.0f)) {
|
||||
col.rgb = col.rgb / col.a;
|
||||
}
|
||||
}
|
||||
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);
|
||||
float2 uvs = (use_clip) ? clamp(uv, 0.0f, 1.0f) : uv;
|
||||
bool premul = flag_test(gp_interp_flat.mat_flag, GP_FILL_TEXTURE_PREMUL);
|
||||
col = textureGrad(gp_fill_tx, uvs, dx, dy);
|
||||
if (premul && !(col.a == 0.0f || col.a == 1.0f)) {
|
||||
col.rgb = col.rgb / col.a;
|
||||
}
|
||||
}
|
||||
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(uv * 2.0f - 1.0f) : uv.x, 0.0f, 1.0f);
|
||||
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 = float4(1.0f);
|
||||
}
|
||||
col.rgb *= col.a;
|
||||
|
||||
/* When `gp_interp.color_mul` is interpolated for each fragment, it might not always be
|
||||
* exactly 1.0 in the default case. To fix this, clamp any value very close to 1.0 to 1.0. See
|
||||
* #156278. */
|
||||
float4 color_mul_fixed = max(step(1.0f - 1e-6f, gp_interp.color_mul), gp_interp.color_mul);
|
||||
/* Composite all other colors on top of texture color.
|
||||
* Everything is pre-multiply by `col.a` to have the stencil effect. */
|
||||
col = col * color_mul_fixed + col.a * gp_interp.color_add;
|
||||
|
||||
col.rgb *= gpencil_lighting();
|
||||
|
||||
if (flag_test(gp_interp_flat.mat_flag, GP_STROKE_ALIGNMENT)) // dot and squares
|
||||
{
|
||||
uv = uv * 2.0f - 1.0f;
|
||||
if (flag_test(gp_interp_flat.mat_flag, GP_STROKE_DOTS)) {
|
||||
col *= gpencil_stroke_hardess_mask(length(uv), gp_interp_noperspective.hardness);
|
||||
}
|
||||
else {
|
||||
uv = abs(uv);
|
||||
col *= gpencil_stroke_hardess_mask(max(uv.x, uv.y), gp_interp_noperspective.hardness);
|
||||
}
|
||||
}
|
||||
|
||||
return col;
|
||||
}
|
||||
|
||||
float2x2 calculate_rotation_matrix(float2 x_axis)
|
||||
{
|
||||
float2 y_axis = orthogonal(x_axis);
|
||||
return transpose(float2x2(x_axis, y_axis));
|
||||
}
|
||||
|
||||
struct RandomParameters {
|
||||
float random_size;
|
||||
float random_strength;
|
||||
float random_rotation;
|
||||
|
||||
float random_hue;
|
||||
float random_saturation;
|
||||
float random_value;
|
||||
|
||||
float random_noise_scale;
|
||||
};
|
||||
|
||||
RandomParameters unpack_random(uint4 random_packed)
|
||||
{
|
||||
float2 unpacked_x = unpackUnorm2x16(random_packed.x);
|
||||
float2 unpacked_y = unpackUnorm2x16(random_packed.y);
|
||||
float2 unpacked_z = unpackUnorm2x16(random_packed.z);
|
||||
return {unpacked_x.x,
|
||||
unpacked_x.y,
|
||||
unpacked_y.x,
|
||||
unpacked_y.y,
|
||||
unpacked_z.x,
|
||||
unpacked_z.y,
|
||||
uintBitsToFloat(random_packed.w)};
|
||||
}
|
||||
|
||||
float simple_noise(float x)
|
||||
{
|
||||
int int_x = int(x);
|
||||
float factor = smoothstep(0.0f, 1.0f, fract(x));
|
||||
return mix(hash_uint_to_float(int_x), hash_uint_to_float(int_x + 1), factor);
|
||||
}
|
||||
|
||||
float noise_level_2(float x)
|
||||
{
|
||||
return (simple_noise(x) + simple_noise(x * 0.353953f)) * 0.5f;
|
||||
}
|
||||
|
||||
float4 get_dot_color(float2 uv, int i, float2 dx, float2 dy)
|
||||
{
|
||||
uint matid = gp_interp_flat.mat_flag >> GPENCIL_MATID_SHIFT;
|
||||
RandomParameters Parameters = unpack_random(gp_materials[matid].random_packed);
|
||||
|
||||
float noise_x = float(i) * Parameters.random_noise_scale;
|
||||
|
||||
if (Parameters.random_rotation > 0.0f || Parameters.random_size > 0.0f) {
|
||||
float rand_rot = noise_level_2(noise_x + 69637.532f);
|
||||
rand_rot -= 0.5f;
|
||||
rand_rot *= 2.0f;
|
||||
rand_rot *= M_PI;
|
||||
rand_rot *= Parameters.random_rotation;
|
||||
|
||||
float2x2 mat = calculate_rotation_matrix(float2(cos(rand_rot), sin(rand_rot)));
|
||||
|
||||
if (Parameters.random_size > 0.0f) {
|
||||
float rand_siz = noise_level_2(noise_x + 18559.853f);
|
||||
rand_siz *= Parameters.random_size;
|
||||
rand_siz = 1.0f - rand_siz;
|
||||
|
||||
rand_siz = 1.0f / rand_siz;
|
||||
mat[0] = mat[0] * rand_siz;
|
||||
mat[1] = mat[1] * rand_siz;
|
||||
}
|
||||
|
||||
uv -= 0.5f;
|
||||
uv = mat * uv;
|
||||
dx = mat * dx;
|
||||
dy = mat * dy;
|
||||
uv += 0.5f;
|
||||
}
|
||||
|
||||
float4 col = get_color(uv, dx, dy);
|
||||
if (Parameters.random_hue > 0.0f || Parameters.random_saturation > 0.0f ||
|
||||
Parameters.random_value > 0.0f)
|
||||
{
|
||||
float4 col_hsva;
|
||||
rgb_to_hsv(col, col_hsva);
|
||||
|
||||
float rand_hue = noise_level_2(noise_x + 97715.184f);
|
||||
float rand_sat = noise_level_2(noise_x + 16430.953f);
|
||||
float rand_val = noise_level_2(noise_x + 86191.195f);
|
||||
|
||||
col_hsva.x += (rand_hue - 0.5f) * Parameters.random_hue;
|
||||
col_hsva.y *= 1.0f + (rand_sat * 2.0f - 1.0f) * Parameters.random_saturation;
|
||||
col_hsva.z *= 1.0f - Parameters.random_value + rand_val * 2.0f * Parameters.random_value;
|
||||
|
||||
col_hsva.x = fract(col_hsva.x);
|
||||
col_hsva.y = clamp(col_hsva.y, 0.0f, 1.0f);
|
||||
col_hsva.z = clamp(col_hsva.z, 0.0f, 1.0f);
|
||||
|
||||
hsv_to_rgb(col_hsva, col);
|
||||
}
|
||||
|
||||
if (Parameters.random_strength > 0.0f) {
|
||||
float rand = noise_level_2(noise_x + 68916.135f);
|
||||
|
||||
rand -= 1.0f;
|
||||
rand *= Parameters.random_strength;
|
||||
rand += 1.0f;
|
||||
|
||||
col *= rand;
|
||||
}
|
||||
|
||||
return col;
|
||||
}
|
||||
|
||||
float4 alpha_over(float4 base, float4 over)
|
||||
{
|
||||
return (1.0 - over.w) * base + over;
|
||||
}
|
||||
|
||||
float4 to_cam(float4 a)
|
||||
{
|
||||
if (drw_view_is_perspective()) {
|
||||
return float4(a.x / a.z, a.y / a.z, a.z, a.w / a.z);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
float4 from_cam(float4 a)
|
||||
{
|
||||
if (drw_view_is_perspective()) {
|
||||
return float4(a.x * a.z, a.y * a.z, a.z, a.w * a.z);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
float point_i_to_local_t(float i, float4 p1, float4 p2)
|
||||
{
|
||||
float i_start = gp_interp_flat.point_length.x;
|
||||
float i_end = gp_interp_flat.point_length.y;
|
||||
float point_density = gp_interp_flat.point_length.z;
|
||||
float i_delta = i_end - i_start;
|
||||
|
||||
uint placement_mode = gp_interp_flat.mat_flag & GP_DOTS_PLACEMENT_MODE;
|
||||
|
||||
if (placement_mode == GP_DOTS_PLACEMENT_MODE_RADIUS) {
|
||||
float4 P1 = from_cam(p1);
|
||||
float4 P2 = from_cam(p2);
|
||||
float r1 = P1.w;
|
||||
float r2 = P2.w;
|
||||
float a = r2 - r1;
|
||||
float l = length(P1.xyz - P2.xyz);
|
||||
|
||||
if (abs(a) < 0.001f * l) {
|
||||
return (i / point_density - i_start) / i_delta;
|
||||
}
|
||||
|
||||
if (!drw_view_is_perspective()) {
|
||||
float b = 2.0f * log(a / r1 + 1.0f) / i_delta;
|
||||
float exp_b = exp(b);
|
||||
l = a * (exp_b + 1.0f) / (exp_b - 1.0f);
|
||||
}
|
||||
|
||||
/* Avoid division by zero. */
|
||||
if (r1 <= 0.0f || l <= 0.0f || l == a) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float E = (l + a) / (l - a);
|
||||
float E_i = pow(E, (i / point_density - i_start) / 2.0f);
|
||||
|
||||
return r1 * (E_i - 1.0f) / a;
|
||||
}
|
||||
|
||||
return (i / point_density - i_start) / i_delta;
|
||||
}
|
||||
|
||||
float local_t_to_point_i(float t, float4 p1, float4 p2)
|
||||
{
|
||||
float i_start = gp_interp_flat.point_length.x;
|
||||
float i_end = gp_interp_flat.point_length.y;
|
||||
float point_density = gp_interp_flat.point_length.z;
|
||||
float i_delta = i_end - i_start;
|
||||
|
||||
uint placement_mode = gp_interp_flat.mat_flag & GP_DOTS_PLACEMENT_MODE;
|
||||
|
||||
if (placement_mode == GP_DOTS_PLACEMENT_MODE_RADIUS) {
|
||||
float4 P1 = from_cam(p1);
|
||||
float4 P2 = from_cam(p2);
|
||||
float r1 = P1.w;
|
||||
float r2 = P2.w;
|
||||
float a = r2 - r1;
|
||||
float l = length(P1.xyz - P2.xyz);
|
||||
|
||||
if (abs(a) < 0.001f * l) {
|
||||
return (t * i_delta + i_start) * point_density;
|
||||
}
|
||||
|
||||
if (!drw_view_is_perspective()) {
|
||||
float b = 2.0f * log(a / r1 + 1.0f) / i_delta;
|
||||
float exp_b = exp(b);
|
||||
l = a * (exp_b + 1.0f) / (exp_b - 1.0f);
|
||||
}
|
||||
|
||||
/* Avoid division by zero. */
|
||||
if (r1 <= 0.0f || l <= 0.0f || l == a) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float E = (l + a) / (l - a);
|
||||
float E_i = t * a / r1 + 1.0f;
|
||||
|
||||
return (2.0f * log(E_i) / log(E) + i_start) * point_density;
|
||||
}
|
||||
|
||||
return (t * i_delta + i_start) * point_density;
|
||||
}
|
||||
|
||||
float screen_t_to_local_t(float screen_t, float z1, float z2)
|
||||
{
|
||||
if (!drw_view_is_perspective()) {
|
||||
return screen_t;
|
||||
}
|
||||
|
||||
float f = (1.0f - screen_t);
|
||||
float k = z2 / z1 - 1.0f;
|
||||
return screen_t / (k * f + 1.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the `t` values for the two circles on a uneven capsule that intersection the point
|
||||
* `p0`.
|
||||
*/
|
||||
float2 uneven_capsule_intersection(float2 p0, float2 p1, float2 p2, float r1, float r2)
|
||||
{
|
||||
float l = distance(p1, p2);
|
||||
|
||||
float local_dis_sq = dot(p2 - p1, p2 - p1);
|
||||
float X = (dot(p0 - p1, p2 - p1) / local_dis_sq) * l;
|
||||
float2 p_t = p1 + (p2 - p1) * (X / l);
|
||||
float Y = distance(p_t, p0);
|
||||
|
||||
float a = l * l - (r2 - r1) * (r2 - r1);
|
||||
float b = -2.0f * (r1 * (r2 - r1) + l * X);
|
||||
float c = Y * Y + X * X - r1 * r1;
|
||||
|
||||
float discriminant = b * b - 4.0f * a * c;
|
||||
if (discriminant < 0.0f) {
|
||||
return float2(-1.0f, -1.0f);
|
||||
}
|
||||
|
||||
/* The quadratic equation. */
|
||||
float2 t = (float2(-1.0f, 1.0f) * sqrt(discriminant) - b) / (2.0f * a);
|
||||
|
||||
if (r1 < r2) {
|
||||
if (l - r2 < -r1) {
|
||||
return float2(t.x, 1.0f);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (l + r2 < r1) {
|
||||
return float2(0.0f, t.y);
|
||||
}
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
int2 get_bounds(float2 p0, float4 p1, float4 p2)
|
||||
{
|
||||
uint placement_mode = gp_interp_flat.mat_flag & GP_DOTS_PLACEMENT_MODE;
|
||||
if (placement_mode == GP_DOTS_PLACEMENT_MODE_COUNT && gp_interp_flat.point_length.z == 1.0f) {
|
||||
return int2(0, 1);
|
||||
}
|
||||
|
||||
int min_lower = int(ceil(local_t_to_point_i(0.0f, p1, p2)));
|
||||
int max_upper = int(ceil(local_t_to_point_i(1.0f, p1, p2)));
|
||||
|
||||
if (!(p1.z > 0.0f && p2.z > 0.0f)) {
|
||||
return int2(min_lower, max_upper);
|
||||
}
|
||||
|
||||
float r1 = p1.w;
|
||||
float r2 = p2.w;
|
||||
|
||||
/* Scale each circle up by the diagonal of the square. */
|
||||
bool is_squares = !flag_test(gp_interp_flat.mat_flag, GP_STROKE_DOTS);
|
||||
if (is_squares) {
|
||||
r1 *= M_SQRT2;
|
||||
r2 *= M_SQRT2;
|
||||
}
|
||||
|
||||
float2 ts = uneven_capsule_intersection(p0, p1.xy, p2.xy, r1, r2);
|
||||
|
||||
if (ts.x == -1.0f && ts.y == -1.0f) {
|
||||
return int2(0, 0);
|
||||
}
|
||||
|
||||
if (ts.y < 0.0f || ts.x > 1.0f) {
|
||||
return int2(0, 0);
|
||||
}
|
||||
|
||||
float t_min = screen_t_to_local_t(saturate(ts.x), p1.z, p2.z);
|
||||
float t_max = screen_t_to_local_t(saturate(ts.y), p1.z, p2.z);
|
||||
|
||||
int lower = int(floor(local_t_to_point_i(t_min, p1, p2)));
|
||||
int upper = int(ceil(local_t_to_point_i(t_max, p1, p2))) + 1;
|
||||
|
||||
lower = max(min_lower, lower);
|
||||
upper = min(max_upper, upper);
|
||||
|
||||
return int2(lower, upper);
|
||||
}
|
||||
|
||||
float3 ndc_to_view(float4 ndc)
|
||||
{
|
||||
if (drw_view_is_perspective()) {
|
||||
float3 view = (drw_view().wininv * ndc).xyz;
|
||||
view.z *= -1.0f;
|
||||
return view;
|
||||
}
|
||||
float aspect = viewport_size.x / viewport_size.y;
|
||||
return float3(ndc.xy / float2(1.0f, aspect), 1.0f);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
uint placement_mode = gp_interp_flat.mat_flag & GP_DOTS_PLACEMENT_MODE;
|
||||
bool is_single_dot = placement_mode == GP_DOTS_PLACEMENT_MODE_COUNT &&
|
||||
gp_interp_flat.point_length.z == 1.0f;
|
||||
|
||||
if (flag_test(gp_interp_flat.mat_flag, GP_FILL)) // fill
|
||||
{
|
||||
float2 dx = gpu_dfdx(gp_interp.uv);
|
||||
float2 dy = gpu_dfdy(gp_interp.uv);
|
||||
|
||||
frag_color = get_color(gp_interp.uv, dx, dy);
|
||||
}
|
||||
else {
|
||||
if (flag_test(gp_interp_flat.mat_flag, GP_STROKE_ALIGNMENT)) // dot and squares
|
||||
{
|
||||
if (!is_single_dot) {
|
||||
float radius1 = screen_space_to_radius(gp_interp_flat.sspos_1);
|
||||
float radius2 = screen_space_to_radius(gp_interp_flat.sspos_2);
|
||||
|
||||
float4 ndc1 = screen_space_to_ndc(gp_interp_flat.sspos_1, viewport_size);
|
||||
float4 ndc2 = screen_space_to_ndc(gp_interp_flat.sspos_2, viewport_size);
|
||||
|
||||
float3 v1 = ndc_to_view(ndc1);
|
||||
float3 v2 = ndc_to_view(ndc2);
|
||||
|
||||
float3 view_dir = ndc_to_view(
|
||||
float4(gl_FragCoord.xy / viewport_size.xy, 0.0f, 1.0f) * 2.0f - 1.0f);
|
||||
float2 view_coord = view_dir.xy / view_dir.z;
|
||||
|
||||
float scale_fac = 2.0f / viewport_size.x;
|
||||
if (drw_view_is_perspective()) {
|
||||
scale_fac *= drw_view().wininv[0][0] / view_dir.z;
|
||||
}
|
||||
|
||||
float4 P1 = float4(v1, radius1 * scale_fac);
|
||||
float4 P2 = float4(v2, radius2 * scale_fac);
|
||||
|
||||
float4 p1 = to_cam(P1);
|
||||
float4 p2 = to_cam(P2);
|
||||
|
||||
int2 bounds = get_bounds(view_coord, p1, p2);
|
||||
int lower = bounds.x;
|
||||
int upper = bounds.y;
|
||||
|
||||
float2 pre_dx = gpu_dfdx(view_coord);
|
||||
float2 pre_dy = gpu_dfdy(view_coord);
|
||||
|
||||
frag_color = float4(0.0f);
|
||||
/* Loop through backwards so we can break early. */
|
||||
for (int i = upper - 1; i >= lower; i--) {
|
||||
float t = point_i_to_local_t(i, p1, p2);
|
||||
|
||||
float4 pos = to_cam(P1 + (P2 - P1) * t);
|
||||
|
||||
float2 uv = (view_coord - pos.xy) / pos.w;
|
||||
float2 dx = pre_dx / pos.w;
|
||||
float2 dy = pre_dy / pos.w;
|
||||
|
||||
float2x2 mat = calculate_rotation_matrix(gp_interp_flat.aspect.zw);
|
||||
uv = mat * uv;
|
||||
dx = mat * dx;
|
||||
dy = mat * dy;
|
||||
|
||||
uv = uv * 0.5f + 0.5f;
|
||||
dx = dx * 0.5f;
|
||||
dy = dy * 0.5f;
|
||||
|
||||
frag_color = alpha_over(get_dot_color(uv, i, dx, dy), frag_color);
|
||||
|
||||
/* Break early if full opacity. */
|
||||
if (frag_color.w > 0.999f) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
int i = int(gp_interp_flat.point_length.x);
|
||||
float2 dx = gpu_dfdx(gp_interp.uv);
|
||||
float2 dy = gpu_dfdy(gp_interp.uv);
|
||||
|
||||
frag_color = get_dot_color(gp_interp.uv, i, dx, dy);
|
||||
}
|
||||
}
|
||||
else { // line
|
||||
float2 dx = gpu_dfdx(gp_interp.uv);
|
||||
float2 dy = gpu_dfdy(gp_interp.uv);
|
||||
|
||||
frag_color = get_color(gp_interp.uv, dx, dy);
|
||||
frag_color *= gpencil_stroke_mask(gp_interp_flat.sspos_1.xy,
|
||||
gp_interp_flat.sspos_2.xy,
|
||||
gp_interp_flat.sspos_0,
|
||||
gp_interp_flat.sspos_3,
|
||||
gp_interp.uv,
|
||||
gp_interp_flat.mat_flag,
|
||||
gp_interp_noperspective.thickness.x,
|
||||
gp_interp_noperspective.hardness,
|
||||
gp_interp_noperspective.thickness.zw);
|
||||
}
|
||||
}
|
||||
|
||||
/* To avoid aliasing artifacts, we reduce the opacity of small strokes. */
|
||||
frag_color *= smoothstep(0.0f, 1.0f, gp_interp_noperspective.thickness.y);
|
||||
|
||||
/* Holdout materials. */
|
||||
if (flag_test(gp_interp_flat.mat_flag, GP_STROKE_HOLDOUT | GP_FILL_HOLDOUT)) {
|
||||
revealColor = frag_color.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 = float4(0.0f, 0.0f, 0.0f, frag_color.a);
|
||||
|
||||
if (frag_color.a < 0.001f) {
|
||||
gpu_discard_fragment();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
float2 fb_size = max(float2(textureSize(gp_scene_depth_tx, 0).xy),
|
||||
float2(textureSize(gp_mask_tx, 0).xy));
|
||||
float2 uvs = gl_FragCoord.xy / fb_size;
|
||||
/* Manual depth test */
|
||||
float scene_depth = texture(gp_scene_depth_tx, uvs).r;
|
||||
if (gl_FragCoord.z > scene_depth) {
|
||||
gpu_discard_fragment();
|
||||
return;
|
||||
}
|
||||
|
||||
/* FIXME(fclem): Grrr. 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(gp_mask_tx, uvs).r;
|
||||
if (mask < 0.001f) {
|
||||
gpu_discard_fragment();
|
||||
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.0f) {
|
||||
gl_FragDepth = gp_interp_flat.depth;
|
||||
}
|
||||
else {
|
||||
gl_FragDepth = gl_FragCoord.z;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/* SPDX-FileCopyrightText: 2015-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpencil_vfx_infos.hh"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpencil_fx_common)
|
||||
|
||||
#include "gpu_shader_fullscreen_lib.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
fullscreen_vertex(gl_VertexID, gl_Position);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpencil_infos.hh"
|
||||
|
||||
FRAGMENT_SHADER_CREATE_INFO(gpencil_layer_blend)
|
||||
|
||||
#include "gpencil_common_lib.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
float2 screen_uv = gl_FragCoord.xy / float2(textureSize(color_buf, 0).xy);
|
||||
|
||||
float4 color;
|
||||
|
||||
/* Remember, this is associated alpha (aka. pre-multiply). */
|
||||
color.rgb = textureLod(color_buf, screen_uv, 0).rgb;
|
||||
/* Stroke only render mono-chromatic revealage. We convert to alpha. */
|
||||
color.a = 1.0f - textureLod(reveal_buf, screen_uv, 0).r;
|
||||
|
||||
float mask = textureLod(mask_buf, screen_uv, 0).r;
|
||||
mask *= blend_opacity;
|
||||
|
||||
frag_color = float4(1.0f, 0.0f, 1.0f, 1.0f);
|
||||
fragRevealage = float4(1.0f, 0.0f, 1.0f, 1.0f);
|
||||
|
||||
blend_mode_output(blend_mode, color, mask, frag_color, fragRevealage);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpencil_infos.hh"
|
||||
|
||||
FRAGMENT_SHADER_CREATE_INFO(gpencil_mask_invert)
|
||||
|
||||
void main()
|
||||
{
|
||||
/* Blend mode does the inversion. */
|
||||
fragRevealage = frag_color = float4(1.0f);
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpencil_infos.hh"
|
||||
|
||||
VERTEX_SHADER_CREATE_INFO(gpencil_geometry)
|
||||
|
||||
#include "draw_grease_pencil_lib.glsl"
|
||||
|
||||
void gpencil_color_output(float4 stroke_col, float4 vert_col, float vert_strength, float mix_tex)
|
||||
{
|
||||
/* Mix stroke with other colors. */
|
||||
float4 mixed_col = stroke_col;
|
||||
mixed_col.rgb = mix(mixed_col.rgb, vert_col.rgb, vert_col.a * gp_vertex_color_opacity);
|
||||
mixed_col.rgb = mix(mixed_col.rgb, gp_layer_tint.rgb, gp_layer_tint.a);
|
||||
mixed_col.a *= vert_strength * gp_layer_opacity;
|
||||
/**
|
||||
* 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 = float4(mixed_col.aaa, mixed_col.a);
|
||||
gp_interp.color_add = float4(mixed_col.rgb * mixed_col.a, 0.0f);
|
||||
/* 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.0f - mix_tex;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
float vert_strength;
|
||||
float4 vert_color;
|
||||
float3 vert_N;
|
||||
|
||||
int4 ma1 = floatBitsToInt(texelFetch(gp_pos_tx, gpencil_stroke_point_id() * 3 + 1));
|
||||
PointData point_data1 = decode_ma(ma1);
|
||||
gpMaterial gp_mat = gp_materials[point_data1.mat + gp_material_offset];
|
||||
gpMaterialFlag gp_flag = gpMaterialFlag(floatBitsToUint(gp_mat._flag));
|
||||
|
||||
gp_interp_flat.point_length.z = gp_mat._stroke_u_scale;
|
||||
|
||||
gl_Position = gpencil_vertex(float4(viewport_size, 1.0f / viewport_size),
|
||||
gp_flag,
|
||||
gp_mat._alignment_rot,
|
||||
gp_interp.pos,
|
||||
vert_N,
|
||||
vert_color,
|
||||
vert_strength,
|
||||
gp_interp.uv,
|
||||
gp_interp_flat.sspos_0,
|
||||
gp_interp_flat.sspos_1,
|
||||
gp_interp_flat.sspos_2,
|
||||
gp_interp_flat.sspos_3,
|
||||
gp_interp_flat.point_length,
|
||||
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 = float4(0.0f);
|
||||
}
|
||||
|
||||
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;
|
||||
gp_interp_flat.mat_flag |= uint(point_data1.mat + gp_material_offset) << GPENCIL_MATID_SHIFT;
|
||||
|
||||
if (gp_stroke_order3d) {
|
||||
/* Use the fragment depth (see fragment shader). */
|
||||
gp_interp_flat.depth = -1.0f;
|
||||
}
|
||||
else if (flag_test(gp_flag, GP_STROKE_OVERLAP)) {
|
||||
/* Use the index of the point as depth.
|
||||
* This means the stroke can overlap itself. */
|
||||
gp_interp_flat.depth = (point_data1.point_id + gp_stroke_index_offset + 2.0f) * 0.0000002f;
|
||||
}
|
||||
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. */
|
||||
gp_interp_flat.depth = (point_data1.stroke_id + gp_stroke_index_offset + 2.0f) * 0.0000002f;
|
||||
}
|
||||
}
|
||||
else {
|
||||
int stroke_point_id = gpencil_stroke_point_id();
|
||||
float4 uv1 = texelFetch(gp_pos_tx, stroke_point_id * 3 + 2);
|
||||
float4 fcol1 = texelFetch(gp_col_tx, stroke_point_id * 2 + 1);
|
||||
float4 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.0f;
|
||||
}
|
||||
|
||||
/* Decode fill opacity. */
|
||||
float4 fcol_decode = float4(fcol1.rgb, floor(fcol1.a / 10.0f));
|
||||
float fill_opacity = fcol1.a - (fcol_decode.a * 10);
|
||||
fcol_decode.a /= 10000.0f;
|
||||
|
||||
/* Special case: We don't use vertex color if material Holdout. */
|
||||
if (flag_test(gp_flag, GP_FILL_HOLDOUT)) {
|
||||
fcol_decode = float4(0.0f);
|
||||
}
|
||||
|
||||
/* Apply opacity. */
|
||||
fill_col.a *= fill_opacity;
|
||||
/* If factor is > 1 force opacity. */
|
||||
if (fill_opacity > 1.0f) {
|
||||
fill_col.a += fill_opacity - 1.0f;
|
||||
}
|
||||
|
||||
fill_col.a = clamp(fill_col.a, 0.0f, 1.0f);
|
||||
|
||||
gpencil_color_output(fill_col, fcol_decode, 1.0f, gp_mat._fill_texture_mix);
|
||||
|
||||
gp_interp_flat.mat_flag = gp_flag & GP_FILL_FLAGS;
|
||||
gp_interp_flat.mat_flag |= GP_FILL;
|
||||
gp_interp_flat.mat_flag |= uint(point_data1.mat + gp_material_offset) << GPENCIL_MATID_SHIFT;
|
||||
|
||||
gp_interp.uv = float2x2(gp_mat.fill_uv_rot_scale.xy, gp_mat.fill_uv_rot_scale.zw) * uv1.xy +
|
||||
gp_mat._fill_uv_offset;
|
||||
|
||||
if (gp_stroke_order3d) {
|
||||
/* Use the fragment depth (see fragment shader). */
|
||||
gp_interp_flat.depth = -1.0f;
|
||||
}
|
||||
else {
|
||||
/* Use the index of first point of the stroke as depth. */
|
||||
gp_interp_flat.depth = (point_data1.stroke_id + gp_stroke_index_offset + 1.0f) * 0.0000002f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "infos/gpencil_vfx_infos.hh"
|
||||
|
||||
FRAGMENT_SHADER_CREATE_INFO(gpencil_fx_composite)
|
||||
|
||||
#include "gpencil_common_lib.glsl"
|
||||
|
||||
float gaussian_weight(float x)
|
||||
{
|
||||
return exp(-x * x / (2.0f * 0.35f * 0.35f));
|
||||
}
|
||||
|
||||
#if defined(COMPOSITE)
|
||||
|
||||
void main()
|
||||
{
|
||||
float2 screen_uv = gl_FragCoord.xy / float2(textureSize(color_buf, 0).xy);
|
||||
|
||||
if (is_first_pass) {
|
||||
/* Blend mode is multiply. */
|
||||
frag_color.rgb = fragRevealage.rgb = texture(reveal_buf, screen_uv).rgb;
|
||||
frag_color.a = fragRevealage.a = 1.0f;
|
||||
}
|
||||
else {
|
||||
/* Blend mode is additive. */
|
||||
fragRevealage = float4(0.0f);
|
||||
frag_color.rgb = texture(color_buf, screen_uv).rgb;
|
||||
frag_color.a = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(COLORIZE)
|
||||
|
||||
# define sepia_mat \
|
||||
float3x3(float3(0.393f, 0.349f, 0.272f), \
|
||||
float3(0.769f, 0.686f, 0.534f), \
|
||||
float3(0.189f, 0.168f, 0.131f))
|
||||
|
||||
# define MODE_GRAYSCALE 0
|
||||
# define MODE_SEPIA 1
|
||||
# define MODE_DUOTONE 2
|
||||
# define MODE_CUSTOM 3
|
||||
# define MODE_TRANSPARENT 4
|
||||
|
||||
void main()
|
||||
{
|
||||
float2 screen_uv = gl_FragCoord.xy / float2(textureSize(color_buf, 0).xy);
|
||||
|
||||
frag_color = texture(color_buf, screen_uv);
|
||||
fragRevealage = texture(reveal_buf, screen_uv);
|
||||
|
||||
float luma = dot(frag_color.rgb, float3(0.2126f, 0.7152f, 0.723f));
|
||||
|
||||
/* No blending. */
|
||||
switch (mode) {
|
||||
case MODE_GRAYSCALE:
|
||||
frag_color.rgb = mix(frag_color.rgb, float3(luma), factor);
|
||||
break;
|
||||
case MODE_SEPIA:
|
||||
frag_color.rgb = mix(frag_color.rgb, sepia_mat * frag_color.rgb, factor);
|
||||
break;
|
||||
case MODE_DUOTONE:
|
||||
frag_color.rgb = luma * ((luma <= factor) ? low_color : high_color);
|
||||
break;
|
||||
case MODE_CUSTOM:
|
||||
frag_color.rgb = mix(frag_color.rgb, luma * low_color, factor);
|
||||
break;
|
||||
case MODE_TRANSPARENT:
|
||||
default:
|
||||
frag_color.rgb *= factor;
|
||||
fragRevealage.rgb = mix(float3(1.0f), fragRevealage.rgb, factor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(BLUR)
|
||||
|
||||
void main()
|
||||
{
|
||||
float2 screen_uv = gl_FragCoord.xy / float2(textureSize(color_buf, 0).xy);
|
||||
|
||||
float2 pixel_size = 1.0f / float2(textureSize(reveal_buf, 0).xy);
|
||||
float2 ofs = offset * pixel_size;
|
||||
|
||||
frag_color = float4(0.0f);
|
||||
fragRevealage = float4(0.0f);
|
||||
|
||||
/* No blending. */
|
||||
float weight_accum = 0.0f;
|
||||
for (int i = -samp_count; i <= samp_count; i++) {
|
||||
float x = float(i) / float(samp_count);
|
||||
float weight = gaussian_weight(x);
|
||||
weight_accum += weight;
|
||||
float2 uv = screen_uv + ofs * x;
|
||||
frag_color.rgb += texture(color_buf, uv).rgb * weight;
|
||||
fragRevealage.rgb += texture(reveal_buf, uv).rgb * weight;
|
||||
}
|
||||
|
||||
frag_color /= weight_accum;
|
||||
fragRevealage /= weight_accum;
|
||||
}
|
||||
|
||||
#elif defined(TRANSFORM)
|
||||
|
||||
void main()
|
||||
{
|
||||
float2 screen_uv = gl_FragCoord.xy / float2(textureSize(color_buf, 0).xy);
|
||||
|
||||
float2 uv = (screen_uv - 0.5f) * axis_flip + 0.5f;
|
||||
|
||||
/* Wave deform. */
|
||||
float wave_time = dot(uv, wave_dir.xy);
|
||||
uv += sin(wave_time + wave_phase) * wave_offset;
|
||||
/* Swirl deform. */
|
||||
if (swirl_radius > 0.0f) {
|
||||
float2 tex_size = float2(textureSize(color_buf, 0).xy);
|
||||
float2 pix_coord = uv * tex_size - swirl_center;
|
||||
float dist = length(pix_coord);
|
||||
float percent = clamp((swirl_radius - dist) / swirl_radius, 0.0f, 1.0f);
|
||||
float theta = percent * percent * swirl_angle;
|
||||
float s = sin(theta);
|
||||
float c = cos(theta);
|
||||
float2x2 rot = float2x2(float2(c, -s), float2(s, c));
|
||||
uv = (rot * pix_coord + swirl_center) / tex_size;
|
||||
}
|
||||
|
||||
frag_color = texture(color_buf, uv);
|
||||
fragRevealage = texture(reveal_buf, uv);
|
||||
}
|
||||
|
||||
#elif defined(GLOW)
|
||||
|
||||
void main()
|
||||
{
|
||||
float2 screen_uv = gl_FragCoord.xy / float2(textureSize(color_buf, 0).xy);
|
||||
|
||||
float2 pixel_size = 1.0f / float2(textureSize(reveal_buf, 0).xy);
|
||||
float2 ofs = offset * pixel_size;
|
||||
|
||||
frag_color = float4(0.0f);
|
||||
fragRevealage = float4(0.0f);
|
||||
|
||||
float weight_accum = 0.0f;
|
||||
for (int i = -samp_count; i <= samp_count; i++) {
|
||||
float x = float(i) / float(samp_count);
|
||||
float weight = gaussian_weight(x);
|
||||
weight_accum += weight;
|
||||
float2 uv = screen_uv + ofs * x;
|
||||
float3 col = texture(color_buf, uv).rgb;
|
||||
float3 rev = texture(reveal_buf, uv).rgb;
|
||||
if (threshold.x > -1.0f) {
|
||||
if (threshold.y > -1.0f) {
|
||||
if (any(greaterThan(abs(col - float3(threshold)), float3(threshold.w)))) {
|
||||
weight = 0.0f;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (dot(col, float3(1.0f / 3.0f)) < threshold.x) {
|
||||
weight = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
frag_color.rgb += col * weight;
|
||||
fragRevealage.rgb += (1.0f - rev) * weight;
|
||||
}
|
||||
|
||||
if (weight_accum > 0.0f) {
|
||||
frag_color *= glow_color.rgbb / weight_accum;
|
||||
fragRevealage = fragRevealage / weight_accum;
|
||||
}
|
||||
fragRevealage = 1.0f - fragRevealage;
|
||||
|
||||
if (glow_under) {
|
||||
if (first_pass) {
|
||||
/* In first pass we copy the revealage buffer in the alpha channel.
|
||||
* This let us do the alpha under in second pass. */
|
||||
float3 original_revealage = texture(reveal_buf, screen_uv).rgb;
|
||||
fragRevealage.a = clamp(dot(original_revealage.rgb, float3(0.333334f)), 0.0f, 1.0f);
|
||||
}
|
||||
else {
|
||||
/* Recover original revealage. */
|
||||
fragRevealage.a = texture(reveal_buf, screen_uv).a;
|
||||
}
|
||||
}
|
||||
|
||||
if (!first_pass) {
|
||||
frag_color.a = clamp(1.0f - dot(fragRevealage.rgb, float3(0.333334f)), 0.0f, 1.0f);
|
||||
fragRevealage.a *= glow_color.a;
|
||||
blend_mode_output(blend_mode, frag_color, fragRevealage.a, frag_color, fragRevealage);
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(RIM)
|
||||
|
||||
void main()
|
||||
{
|
||||
float2 screen_uv = gl_FragCoord.xy / float2(textureSize(color_buf, 0).xy);
|
||||
|
||||
/* Blur revealage buffer. */
|
||||
fragRevealage = float4(0.0f);
|
||||
float weight_accum = 0.0f;
|
||||
for (int i = -samp_count; i <= samp_count; i++) {
|
||||
float x = float(i) / float(samp_count);
|
||||
float weight = gaussian_weight(x);
|
||||
weight_accum += weight;
|
||||
float2 uv = screen_uv + blur_dir * x + uv_offset;
|
||||
float3 col = texture(reveal_buf, uv).rgb;
|
||||
if (any(not(equal(float2(0.0f), floor(uv))))) {
|
||||
col = float3(0.0f);
|
||||
}
|
||||
fragRevealage.rgb += col * weight;
|
||||
}
|
||||
fragRevealage /= weight_accum;
|
||||
|
||||
if (is_first_pass) {
|
||||
/* In first pass we copy the reveal buffer. This let us do alpha masking in second pass. */
|
||||
frag_color = texture(reveal_buf, screen_uv);
|
||||
/* Also add the masked color to the reveal buffer. */
|
||||
float3 col = texture(color_buf, screen_uv).rgb;
|
||||
if (all(lessThan(abs(col - mask_color), float3(0.05f)))) {
|
||||
frag_color = float4(1.0f);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Pre-multiply by foreground alpha (alpha mask). */
|
||||
float mask = 1.0f -
|
||||
clamp(dot(float3(0.333334f), texture(color_buf, screen_uv).rgb), 0.0f, 1.0f);
|
||||
|
||||
/* fragRevealage is blurred shadow. */
|
||||
float rim = clamp(dot(float3(0.333334f), fragRevealage.rgb), 0.0f, 1.0f);
|
||||
|
||||
float4 color = float4(rim_color, 1.0f);
|
||||
|
||||
blend_mode_output(blend_mode, color, rim * mask, frag_color, fragRevealage);
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(SHADOW)
|
||||
|
||||
float2 compute_uvs(float2 screen_uv, float x)
|
||||
{
|
||||
float2 uv = screen_uv;
|
||||
/* Transform UV (loc, rot, scale) */
|
||||
uv = uv.x * uv_rot_x + uv.y * uv_rot_y + uv_offset;
|
||||
uv += blur_dir * x;
|
||||
/* Wave deform. */
|
||||
float wave_time = dot(uv, wave_dir.xy);
|
||||
uv += sin(wave_time + wave_phase) * wave_offset;
|
||||
return uv;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
float2 screen_uv = gl_FragCoord.xy / float2(textureSize(color_buf, 0).xy);
|
||||
|
||||
/* Blur revealage buffer. */
|
||||
fragRevealage = float4(0.0f);
|
||||
float weight_accum = 0.0f;
|
||||
for (int i = -samp_count; i <= samp_count; i++) {
|
||||
float x = float(i) / float(samp_count);
|
||||
float weight = gaussian_weight(x);
|
||||
weight_accum += weight;
|
||||
float2 uv = compute_uvs(screen_uv, x);
|
||||
float3 col = texture(reveal_buf, uv).rgb;
|
||||
if (any(not(equal(float2(0.0f), floor(uv))))) {
|
||||
col = float3(1.0f);
|
||||
}
|
||||
fragRevealage.rgb += col * weight;
|
||||
}
|
||||
fragRevealage /= weight_accum;
|
||||
|
||||
/* No blending in first pass, alpha over pre-multiply in second pass. */
|
||||
if (is_first_pass) {
|
||||
/* In first pass we copy the reveal buffer. This let us do alpha under in second pass. */
|
||||
frag_color = texture(reveal_buf, screen_uv);
|
||||
}
|
||||
else {
|
||||
/* fragRevealage is blurred shadow. */
|
||||
float shadow_fac = 1.0f - clamp(dot(float3(0.333334f), fragRevealage.rgb), 0.0f, 1.0f);
|
||||
/* Pre-multiply by foreground revealage (alpha under). */
|
||||
float3 original_revealage = texture(color_buf, screen_uv).rgb;
|
||||
shadow_fac *= clamp(dot(float3(0.333334f), original_revealage), 0.0f, 1.0f);
|
||||
/* Modulate by opacity */
|
||||
shadow_fac *= shadow_color.a;
|
||||
/* Apply shadow color. */
|
||||
frag_color.rgb = mix(float3(0.0f), shadow_color.rgb, shadow_fac);
|
||||
/* Alpha over (mask behind the shadow). */
|
||||
frag_color.a = shadow_fac;
|
||||
|
||||
fragRevealage.rgb = original_revealage * (1.0f - shadow_fac);
|
||||
/* Replace the whole revealage buffer. */
|
||||
fragRevealage.a = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(PIXELIZE)
|
||||
|
||||
void main()
|
||||
{
|
||||
float2 screen_uv = gl_FragCoord.xy / float2(textureSize(color_buf, 0).xy);
|
||||
|
||||
float2 pixel = floor((screen_uv - target_pixel_offset) / target_pixel_size);
|
||||
float2 uv = (pixel + 0.5f) * target_pixel_size + target_pixel_offset;
|
||||
|
||||
frag_color = float4(0.0f);
|
||||
fragRevealage = float4(0.0f);
|
||||
|
||||
for (int i = -samp_count; i <= samp_count; i++) {
|
||||
float x = float(i) / float(samp_count + 1);
|
||||
float2 uv_ofs = uv + accum_offset * 0.5f * x;
|
||||
frag_color += texture(color_buf, uv_ofs);
|
||||
fragRevealage += texture(reveal_buf, uv_ofs);
|
||||
}
|
||||
|
||||
frag_color /= float(samp_count) * 2.0f + 1.0f;
|
||||
fragRevealage /= float(samp_count) * 2.0f + 1.0f;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,210 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifdef GPU_SHADER
|
||||
# pragma once
|
||||
|
||||
# include "gpu_shader_compat.hh"
|
||||
|
||||
# include "gpencil_shader_shared.hh"
|
||||
|
||||
# include "draw_object_infos_infos.hh"
|
||||
# include "draw_view_infos.hh"
|
||||
#endif
|
||||
|
||||
#ifdef GLSL_CPP_STUBS
|
||||
# undef SMAA_RT_METRICS
|
||||
# define SMAA_GLSL_3
|
||||
# define SMAA_STAGE 1
|
||||
# define SMAA_PRESET_HIGH
|
||||
# define SMAA_NO_DISCARD
|
||||
# define SMAA_RT_METRICS viewport_metrics
|
||||
# define SMAA_LUMA_WEIGHT float4(1.0f, 1.0f, 1.0f, 1.0f)
|
||||
#endif
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
#include "gpencil_defines.hh"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPencil Object rendering
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_NAMED_INTERFACE_INFO(gpencil_geometry_iface, gp_interp)
|
||||
SMOOTH(float4, color_mul)
|
||||
SMOOTH(float4, color_add)
|
||||
SMOOTH(float3, pos)
|
||||
SMOOTH(float2, uv)
|
||||
GPU_SHADER_NAMED_INTERFACE_END(gp_interp)
|
||||
GPU_SHADER_NAMED_INTERFACE_INFO(gpencil_geometry_flat_iface, gp_interp_flat)
|
||||
FLAT(float4, aspect)
|
||||
FLAT(float2, sspos_0)
|
||||
FLAT(float4, sspos_1)
|
||||
FLAT(float4, sspos_2)
|
||||
FLAT(float2, sspos_3)
|
||||
FLAT(float3, point_length)
|
||||
FLAT(uint, mat_flag)
|
||||
FLAT(float, depth)
|
||||
GPU_SHADER_NAMED_INTERFACE_END(gp_interp_flat)
|
||||
GPU_SHADER_NAMED_INTERFACE_INFO(gpencil_geometry_noperspective_iface, gp_interp_noperspective)
|
||||
NO_PERSPECTIVE(float4, thickness)
|
||||
NO_PERSPECTIVE(float, hardness)
|
||||
GPU_SHADER_NAMED_INTERFACE_END(gp_interp_noperspective)
|
||||
|
||||
GPU_SHADER_CREATE_INFO(gpencil_geometry)
|
||||
DO_STATIC_COMPILATION()
|
||||
TYPEDEF_SOURCE("gpencil_defines.hh")
|
||||
SAMPLER(2, sampler2D, gp_fill_tx)
|
||||
SAMPLER(3, sampler2D, gp_stroke_tx)
|
||||
SAMPLER(4, sampler2DDepth, gp_scene_depth_tx)
|
||||
SAMPLER(5, sampler2D, gp_mask_tx)
|
||||
UNIFORM_BUF_FREQ(4, gpMaterial, gp_materials[GPENCIL_MATERIAL_BUFFER_LEN], BATCH)
|
||||
UNIFORM_BUF_FREQ(3, gpLight, gp_lights[GPENCIL_LIGHT_BUFFER_LEN], BATCH)
|
||||
PUSH_CONSTANT(float2, viewport_size)
|
||||
/* Per Object */
|
||||
PUSH_CONSTANT(float3, gp_normal)
|
||||
PUSH_CONSTANT(bool, gp_stroke_order3d)
|
||||
PUSH_CONSTANT(int, gp_material_offset)
|
||||
/* Per Layer */
|
||||
PUSH_CONSTANT(float, gp_vertex_color_opacity)
|
||||
PUSH_CONSTANT(float4, gp_layer_tint)
|
||||
PUSH_CONSTANT(float, gp_layer_opacity)
|
||||
PUSH_CONSTANT(float, gp_stroke_index_offset)
|
||||
FRAGMENT_OUT(0, float4, frag_color)
|
||||
FRAGMENT_OUT(1, float4, revealColor)
|
||||
VERTEX_OUT(gpencil_geometry_iface)
|
||||
VERTEX_OUT(gpencil_geometry_flat_iface)
|
||||
VERTEX_OUT(gpencil_geometry_noperspective_iface)
|
||||
VERTEX_SOURCE("gpencil_vert.glsl")
|
||||
FRAGMENT_SOURCE("gpencil_frag.glsl")
|
||||
DEPTH_WRITE(DepthWrite::ANY)
|
||||
ADDITIONAL_INFO(draw_view)
|
||||
ADDITIONAL_INFO(draw_modelmat)
|
||||
ADDITIONAL_INFO(draw_gpencil)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Full-Screen Shaders
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_CREATE_INFO(gpencil_layer_blend)
|
||||
DO_STATIC_COMPILATION()
|
||||
SAMPLER(0, sampler2D, color_buf)
|
||||
SAMPLER(1, sampler2D, reveal_buf)
|
||||
SAMPLER(2, sampler2D, mask_buf)
|
||||
PUSH_CONSTANT(int, blend_mode)
|
||||
PUSH_CONSTANT(float, blend_opacity)
|
||||
/* Reminder: This is considered SRC color in blend equations.
|
||||
* Same operation on all buffers. */
|
||||
FRAGMENT_OUT(0, float4, frag_color)
|
||||
FRAGMENT_OUT(1, float4, fragRevealage)
|
||||
FRAGMENT_SOURCE("gpencil_layer_blend_frag.glsl")
|
||||
VERTEX_SOURCE("gpencil_fullscreen_vert.glsl")
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(gpencil_mask_invert)
|
||||
DO_STATIC_COMPILATION()
|
||||
FRAGMENT_OUT(0, float4, frag_color)
|
||||
FRAGMENT_OUT(1, float4, fragRevealage)
|
||||
FRAGMENT_SOURCE("gpencil_mask_invert_frag.glsl")
|
||||
VERTEX_SOURCE("gpencil_fullscreen_vert.glsl")
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(gpencil_depth_merge)
|
||||
DO_STATIC_COMPILATION()
|
||||
PUSH_CONSTANT(float4x4, gp_model_matrix)
|
||||
PUSH_CONSTANT(bool, stroke_order3d)
|
||||
SAMPLER(0, sampler2DDepth, depth_buf)
|
||||
VERTEX_SOURCE("gpencil_depth_merge_vert.glsl")
|
||||
FRAGMENT_SOURCE("gpencil_depth_merge_frag.glsl")
|
||||
DEPTH_WRITE(DepthWrite::ANY)
|
||||
ADDITIONAL_INFO(draw_view)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(gpencil_depth_pass_merge)
|
||||
DO_STATIC_COMPILATION()
|
||||
PUSH_CONSTANT(float4x4, gp_model_matrix)
|
||||
PUSH_CONSTANT(bool, stroke_order3d)
|
||||
SAMPLER(0, sampler2DDepth, depth_buf)
|
||||
IMAGE(0, SFLOAT_32, read_write, image2D, depth_pass_img)
|
||||
VERTEX_SOURCE("gpencil_depth_merge_vert.glsl")
|
||||
FRAGMENT_SOURCE("gpencil_depth_pass_merge_frag.glsl")
|
||||
ADDITIONAL_INFO(draw_view)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Anti-Aliasing
|
||||
* \{ */
|
||||
|
||||
GPU_SHADER_INTERFACE_INFO(gpencil_antialiasing_iface)
|
||||
SMOOTH(float2, uvs)
|
||||
SMOOTH(float2, pixcoord)
|
||||
SMOOTH(float4, offset0)
|
||||
SMOOTH(float4, offset1)
|
||||
SMOOTH(float4, offset2)
|
||||
GPU_SHADER_INTERFACE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(gpencil_antialiasing)
|
||||
DEFINE("SMAA_GLSL_3")
|
||||
DEFINE_VALUE("SMAA_RT_METRICS", "viewport_metrics")
|
||||
DEFINE("SMAA_PRESET_HIGH")
|
||||
DEFINE_VALUE("SMAA_LUMA_WEIGHT", "float4(luma_weight, luma_weight, luma_weight, 0.0f)")
|
||||
DEFINE("SMAA_NO_DISCARD")
|
||||
VERTEX_OUT(gpencil_antialiasing_iface)
|
||||
PUSH_CONSTANT(float4, viewport_metrics)
|
||||
PUSH_CONSTANT(float, luma_weight)
|
||||
VERTEX_SOURCE("gpencil_antialiasing_vert.glsl")
|
||||
FRAGMENT_SOURCE("gpencil_antialiasing_frag.glsl")
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(gpencil_antialiasing_stage_0)
|
||||
DEFINE_VALUE("SMAA_STAGE", "0")
|
||||
SAMPLER(0, sampler2D, color_tx)
|
||||
SAMPLER(1, sampler2D, reveal_tx)
|
||||
FRAGMENT_OUT(0, float2, out_edges)
|
||||
ADDITIONAL_INFO(gpencil_antialiasing)
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(gpencil_antialiasing_stage_1)
|
||||
DEFINE_VALUE("SMAA_STAGE", "1")
|
||||
SAMPLER(0, sampler2D, edges_tx)
|
||||
SAMPLER(1, sampler2D, area_tx)
|
||||
SAMPLER(2, sampler2D, search_tx)
|
||||
FRAGMENT_OUT(0, float4, out_weights)
|
||||
ADDITIONAL_INFO(gpencil_antialiasing)
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(gpencil_antialiasing_stage_2)
|
||||
DEFINE_VALUE("SMAA_STAGE", "2")
|
||||
SAMPLER(0, sampler2D, color_tx)
|
||||
SAMPLER(1, sampler2D, reveal_tx)
|
||||
SAMPLER(2, sampler2D, blend_tx)
|
||||
PUSH_CONSTANT(float, mix_factor)
|
||||
PUSH_CONSTANT(float, taa_accumulated_weight)
|
||||
PUSH_CONSTANT(bool, do_anti_aliasing)
|
||||
PUSH_CONSTANT(bool, only_alpha)
|
||||
/* Reminder: Blending func is `fragRevealage * DST + frag_color`. */
|
||||
FRAGMENT_OUT_DUAL(0, float4, out_color, SRC_0)
|
||||
FRAGMENT_OUT_DUAL(0, float4, out_reveal, SRC_1)
|
||||
ADDITIONAL_INFO(gpencil_antialiasing)
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(gpencil_antialiasing_accumulation)
|
||||
IMAGE(0, GPENCIL_RENDER_FORMAT, read, image2D, src_img)
|
||||
IMAGE(1, GPENCIL_ACCUM_FORMAT, read_write, image2D, dst_img)
|
||||
PUSH_CONSTANT(float, weight_src)
|
||||
PUSH_CONSTANT(float, weight_dst)
|
||||
FRAGMENT_SOURCE("gpencil_antialiasing_accumulation_frag.glsl")
|
||||
VERTEX_SOURCE("gpencil_fullscreen_vert.glsl")
|
||||
DO_STATIC_COMPILATION()
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
/** \} */
|
||||
@@ -0,0 +1,120 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifdef GPU_SHADER
|
||||
# pragma once
|
||||
|
||||
# include "gpu_shader_compat.hh"
|
||||
|
||||
# include "gpencil_shader_shared.hh"
|
||||
|
||||
# include "draw_view_infos.hh"
|
||||
#endif
|
||||
|
||||
#ifdef GLSL_CPP_STUBS
|
||||
# define COMPOSITE
|
||||
#endif
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
GPU_SHADER_CREATE_INFO(gpencil_fx_common)
|
||||
SAMPLER(0, sampler2D, color_buf)
|
||||
SAMPLER(1, sampler2D, reveal_buf)
|
||||
/* Reminder: This is considered SRC color in blend equations.
|
||||
* Same operation on all buffers. */
|
||||
FRAGMENT_OUT(0, float4, frag_color)
|
||||
FRAGMENT_OUT(1, float4, fragRevealage)
|
||||
FRAGMENT_SOURCE("gpencil_vfx_frag.glsl")
|
||||
VERTEX_SOURCE("gpencil_fullscreen_vert.glsl")
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(gpencil_fx_composite)
|
||||
DO_STATIC_COMPILATION()
|
||||
DEFINE("COMPOSITE")
|
||||
PUSH_CONSTANT(bool, is_first_pass)
|
||||
ADDITIONAL_INFO(gpencil_fx_common)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(gpencil_fx_colorize)
|
||||
DO_STATIC_COMPILATION()
|
||||
DEFINE("COLORIZE")
|
||||
PUSH_CONSTANT(float3, low_color)
|
||||
PUSH_CONSTANT(float3, high_color)
|
||||
PUSH_CONSTANT(float, factor)
|
||||
PUSH_CONSTANT(int, mode)
|
||||
ADDITIONAL_INFO(gpencil_fx_common)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(gpencil_fx_blur)
|
||||
DO_STATIC_COMPILATION()
|
||||
DEFINE("BLUR")
|
||||
PUSH_CONSTANT(float2, offset)
|
||||
PUSH_CONSTANT(int, samp_count)
|
||||
ADDITIONAL_INFO(gpencil_fx_common)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(gpencil_fx_transform)
|
||||
DO_STATIC_COMPILATION()
|
||||
DEFINE("TRANSFORM")
|
||||
PUSH_CONSTANT(float2, axis_flip)
|
||||
PUSH_CONSTANT(float2, wave_dir)
|
||||
PUSH_CONSTANT(float2, wave_offset)
|
||||
PUSH_CONSTANT(float, wave_phase)
|
||||
PUSH_CONSTANT(float2, swirl_center)
|
||||
PUSH_CONSTANT(float, swirl_angle)
|
||||
PUSH_CONSTANT(float, swirl_radius)
|
||||
ADDITIONAL_INFO(gpencil_fx_common)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(gpencil_fx_glow)
|
||||
DO_STATIC_COMPILATION()
|
||||
DEFINE("GLOW")
|
||||
PUSH_CONSTANT(float4, glow_color)
|
||||
PUSH_CONSTANT(float2, offset)
|
||||
PUSH_CONSTANT(int, samp_count)
|
||||
PUSH_CONSTANT(float4, threshold)
|
||||
PUSH_CONSTANT(bool, first_pass)
|
||||
PUSH_CONSTANT(bool, glow_under)
|
||||
PUSH_CONSTANT(int, blend_mode)
|
||||
ADDITIONAL_INFO(gpencil_fx_common)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(gpencil_fx_rim)
|
||||
DO_STATIC_COMPILATION()
|
||||
DEFINE("RIM")
|
||||
PUSH_CONSTANT(float2, blur_dir)
|
||||
PUSH_CONSTANT(float2, uv_offset)
|
||||
PUSH_CONSTANT(float3, rim_color)
|
||||
PUSH_CONSTANT(float3, mask_color)
|
||||
PUSH_CONSTANT(int, samp_count)
|
||||
PUSH_CONSTANT(int, blend_mode)
|
||||
PUSH_CONSTANT(bool, is_first_pass)
|
||||
ADDITIONAL_INFO(gpencil_fx_common)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(gpencil_fx_shadow)
|
||||
DO_STATIC_COMPILATION()
|
||||
DEFINE("SHADOW")
|
||||
PUSH_CONSTANT(float4, shadow_color)
|
||||
PUSH_CONSTANT(float2, uv_rot_x)
|
||||
PUSH_CONSTANT(float2, uv_rot_y)
|
||||
PUSH_CONSTANT(float2, uv_offset)
|
||||
PUSH_CONSTANT(float2, blur_dir)
|
||||
PUSH_CONSTANT(float2, wave_dir)
|
||||
PUSH_CONSTANT(float2, wave_offset)
|
||||
PUSH_CONSTANT(float, wave_phase)
|
||||
PUSH_CONSTANT(int, samp_count)
|
||||
PUSH_CONSTANT(bool, is_first_pass)
|
||||
ADDITIONAL_INFO(gpencil_fx_common)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
GPU_SHADER_CREATE_INFO(gpencil_fx_pixelize)
|
||||
DO_STATIC_COMPILATION()
|
||||
DEFINE("PIXELIZE")
|
||||
PUSH_CONSTANT(float2, target_pixel_size)
|
||||
PUSH_CONSTANT(float2, target_pixel_offset)
|
||||
PUSH_CONSTANT(float2, accum_offset)
|
||||
PUSH_CONSTANT(int, samp_count)
|
||||
ADDITIONAL_INFO(gpencil_fx_common)
|
||||
GPU_SHADER_CREATE_END()
|
||||
Reference in New Issue
Block a user