Overlay-Next: Wireframe

Straightforward port.

Left out particle hair to a separate patch for consistency
and simplicity.

Grease Pencil is also missing but that's also missing in
main.

Rel #102179

Pull Request: https://projects.blender.org/blender/blender/pulls/126242
This commit is contained in:
Clément Foucault
2024-08-19 12:31:08 +02:00
committed by Clément Foucault
parent 51461d0561
commit 86e9254a63
10 changed files with 437 additions and 80 deletions

View File

@@ -309,6 +309,7 @@ set(SRC
engines/overlay/overlay_next_private.hh
engines/overlay/overlay_next_relation.hh
engines/overlay/overlay_next_speaker.hh
engines/overlay/overlay_next_wireframe.hh
engines/overlay/overlay_private.hh
engines/select/select_defines.hh
engines/select/select_engine.hh

View File

@@ -103,6 +103,7 @@ void Instance::begin_sync()
layer.prepass.begin_sync(resources, state);
layer.relations.begin_sync();
layer.speakers.begin_sync();
layer.wireframe.begin_sync(resources, state);
};
begin_sync_layer(regular);
begin_sync_layer(infront);
@@ -117,6 +118,8 @@ void Instance::object_sync(ObjectRef &ob_ref, Manager &manager)
const bool in_edit_mode = object_is_edit_mode(ob_ref.object);
const bool in_paint_mode = object_is_paint_mode(ob_ref.object);
const bool in_sculpt_mode = object_is_sculpt_mode(ob_ref);
const bool in_edit_paint_mode = object_is_edit_paint_mode(
ob_ref, in_edit_mode, in_paint_mode, in_sculpt_mode);
const bool needs_prepass = !state.xray_enabled; /* TODO */
OverlayLayer &layer = (state.use_in_front && ob_ref.object->dtx & OB_DRAW_IN_FRONT) ? infront :
@@ -158,6 +161,10 @@ void Instance::object_sync(ObjectRef &ob_ref, Manager &manager)
}
}
if (state.is_wireframe_mode || !state.hide_overlays) {
layer.wireframe.object_sync(manager, ob_ref, resources, in_edit_paint_mode);
}
if (!state.hide_overlays) {
switch (ob_ref.object->type) {
case OB_EMPTY:
@@ -195,20 +202,8 @@ void Instance::object_sync(ObjectRef &ob_ref, Manager &manager)
layer.bounds.object_sync(ob_ref, resources, state);
layer.relations.object_sync(ob_ref, resources, state);
if (object_is_selected(ob_ref)) {
if (in_edit_mode || in_paint_mode || in_sculpt_mode) {
/* Disable outlines for objects in sculpt, paint or edit mode. */
}
else if ((ob_ref.object->base_flag & BASE_FROM_DUPLI) &&
(object_is_edit_mode(ob_ref.dupli_parent) ||
object_is_sculpt_mode(ob_ref.dupli_parent) ||
object_is_paint_mode(ob_ref.dupli_parent)))
{
/* Disable outlines for objects instanced by an object in sculpt, paint or edit mode. */
}
else {
outline.object_sync(manager, ob_ref, state);
}
if (object_is_selected(ob_ref) && !in_edit_paint_mode) {
outline.object_sync(manager, ob_ref, state);
}
}
}
@@ -259,6 +254,15 @@ void Instance::draw(Manager &manager)
const DRWView *view_legacy = DRW_view_default_get();
View view("OverlayView", view_legacy);
if (state.xray_enabled) {
/* For Xray we render the scene to a separate depth buffer. */
resources.xray_depth_tx.acquire(render_size, GPU_DEPTH_COMPONENT24);
resources.depth_target_tx.wrap(resources.xray_depth_tx);
}
else {
resources.depth_target_tx.wrap(resources.depth_tx);
}
/* TODO(fclem): Remove mandatory allocation. */
if (!resources.depth_in_front_tx.is_valid()) {
resources.depth_in_front_alloc_tx.acquire(render_size, GPU_DEPTH_COMPONENT24);
@@ -277,10 +281,10 @@ void Instance::draw(Manager &manager)
resources.line_tx.acquire(int2(1, 1), GPU_RGBA8);
resources.overlay_tx.acquire(int2(1, 1), GPU_SRGB8_A8);
resources.overlay_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx));
resources.overlay_line_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx));
resources.overlay_in_front_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx));
resources.overlay_line_in_front_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx));
resources.overlay_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_target_tx));
resources.overlay_line_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_target_tx));
resources.overlay_in_front_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_target_tx));
resources.overlay_line_in_front_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_target_tx));
}
else {
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE |
@@ -288,9 +292,9 @@ void Instance::draw(Manager &manager)
resources.line_tx.acquire(render_size, GPU_RGBA8, usage);
resources.overlay_tx.acquire(render_size, GPU_SRGB8_A8, usage);
resources.overlay_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx),
resources.overlay_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_target_tx),
GPU_ATTACHMENT_TEXTURE(resources.overlay_tx));
resources.overlay_line_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx),
resources.overlay_line_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_target_tx),
GPU_ATTACHMENT_TEXTURE(resources.overlay_tx),
GPU_ATTACHMENT_TEXTURE(resources.line_tx));
resources.overlay_in_front_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_in_front_tx),
@@ -308,9 +312,15 @@ void Instance::draw(Manager &manager)
resources.overlay_output_fb.ensure(GPU_ATTACHMENT_NONE,
GPU_ATTACHMENT_TEXTURE(resources.color_overlay_tx));
float4 clear_color(0.0f);
GPU_framebuffer_bind(resources.overlay_line_fb);
GPU_framebuffer_clear_color(resources.overlay_line_fb, clear_color);
float4 clear_color(0.0f);
if (state.xray_enabled) {
/* Rendering to a new depth buffer that needs to be cleared. */
GPU_framebuffer_clear_color_depth(resources.overlay_line_fb, clear_color, 1.0f);
}
else {
GPU_framebuffer_clear_color(resources.overlay_line_fb, clear_color);
}
regular.prepass.draw(resources.overlay_line_fb, manager, view);
infront.prepass.draw(resources.overlay_line_in_front_fb, manager, view);
@@ -325,6 +335,7 @@ void Instance::draw(Manager &manager)
auto draw_layer = [&](OverlayLayer &layer, Framebuffer &framebuffer) {
layer.bounds.draw(framebuffer, manager, view);
layer.wireframe.draw(framebuffer, manager, view);
layer.cameras.draw(framebuffer, manager, view);
layer.empties.draw(framebuffer, manager, view);
layer.force_fields.draw(framebuffer, manager, view);
@@ -357,6 +368,7 @@ void Instance::draw(Manager &manager)
resources.line_tx.release();
resources.overlay_tx.release();
resources.xray_depth_tx.release();
resources.depth_in_front_alloc_tx.release();
resources.color_overlay_alloc_tx.release();
resources.color_render_alloc_tx.release();
@@ -400,6 +412,21 @@ bool Instance::object_is_sculpt_mode(const Object *object)
return false;
}
bool Instance::object_is_edit_paint_mode(const ObjectRef &ob_ref,
bool in_edit_mode,
bool in_paint_mode,
bool in_sculpt_mode)
{
bool in_edit_paint_mode = in_edit_mode || in_paint_mode || in_sculpt_mode;
if (ob_ref.object->base_flag & BASE_FROM_DUPLI) {
/* Disable outlines for objects instanced by an object in sculpt, paint or edit mode. */
in_edit_paint_mode |= object_is_edit_mode(ob_ref.dupli_parent) ||
object_is_sculpt_mode(ob_ref.dupli_parent) ||
object_is_paint_mode(ob_ref.dupli_parent);
}
return in_edit_paint_mode;
}
bool Instance::object_is_edit_mode(const Object *object)
{
if (DRW_object_is_in_edit_mode(object)) {

View File

@@ -28,6 +28,7 @@
#include "overlay_next_prepass.hh"
#include "overlay_next_relation.hh"
#include "overlay_next_speaker.hh"
#include "overlay_next_wireframe.hh"
namespace blender::draw::overlay {
@@ -69,6 +70,7 @@ class Instance {
Prepass prepass = {selection_type_};
Relations relations;
Speakers speakers = {selection_type_};
Wireframe wireframe;
} regular{selection_type_}, infront{selection_type_};
Grid grid;
@@ -96,6 +98,11 @@ class Instance {
bool object_is_sculpt_mode(const ObjectRef &ob_ref);
/* Checks only for sculpt mode. */
bool object_is_sculpt_mode(const Object *object);
/* Any mode that requires to view the object without distraction. */
bool object_is_edit_paint_mode(const ObjectRef &ob_ref,
bool in_edit_mode,
bool in_paint_mode,
bool in_sculpt_mode);
};
} // namespace blender::draw::overlay

View File

@@ -338,6 +338,18 @@ class Meshes {
GPU_debug_group_end();
}
static bool mesh_has_edit_cage(const Object *ob)
{
const Mesh &mesh = *static_cast<const Mesh *>(ob->data);
if (mesh.runtime->edit_mesh.get() != nullptr) {
const Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob);
const Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(ob);
return (editmesh_eval_cage != nullptr) && (editmesh_eval_cage != editmesh_eval_final);
}
return false;
}
private:
uint4 data_mask_get(const int flag)
{
@@ -360,18 +372,6 @@ class Meshes {
}
return false;
}
static bool mesh_has_edit_cage(const Object *ob)
{
const Mesh &mesh = *static_cast<const Mesh *>(ob->data);
if (mesh.runtime->edit_mesh.get() != nullptr) {
const Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob);
const Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(ob);
return (editmesh_eval_cage != nullptr) && (editmesh_eval_cage != editmesh_eval_final);
}
return false;
}
};
} // namespace blender::draw::overlay

View File

@@ -200,6 +200,9 @@ class ShaderModule {
ShaderPtr facing;
ShaderPtr lattice_points;
ShaderPtr lattice_wire;
ShaderPtr wireframe_mesh;
ShaderPtr wireframe_curve;
ShaderPtr wireframe_points; /* Draw objects without edges for the wireframe overlay. */
ShaderModule(const SelectionType selection_type, const bool clipping_enabled);
@@ -243,6 +246,8 @@ struct Resources : public select::SelectMap {
TextureFromPool line_tx = {"line_tx"};
/* Target containing overlay color before anti-aliasing. */
TextureFromPool overlay_tx = {"overlay_tx"};
/* Target containing depth of overlays when xray is enabled. */
TextureFromPool xray_depth_tx = {"xray_depth_tx"};
/* Texture that are usually allocated inside. These are fallback when they aren't.
* They are then wrapped inside the #TextureRefs below. */
@@ -259,10 +264,23 @@ struct Resources : public select::SelectMap {
GPUUniformBuf *globals_buf;
TextureRef weight_ramp_tx;
/* Wrappers around #DefaultTextureList members. */
TextureRef depth_tx;
TextureRef depth_in_front_tx;
TextureRef color_overlay_tx;
TextureRef color_render_tx;
/**
* Scene depth buffer that can also be used as render target for overlays.
*
* Can only be bound as a texture if either:
* - the current frame-buffer has no depth buffer attached.
* - `state.xray_enabled` is true.
*/
TextureRef depth_tx;
/**
* Depth target.
* Can either be default depth buffer texture from #DefaultTextureList
* or `xray_depth_tx` if Xray is enabled.
*/
TextureRef depth_target_tx;
Resources(const SelectionType selection_type_, ShaderModule &shader_module)
: select::SelectMap(selection_type_), shaders(shader_module){};

View File

@@ -66,6 +66,7 @@ ShaderModule::ShaderPtr ShaderModule::selectable_shader(
if (selection_type_ != SelectionType::DISABLED) {
info.define("SELECT_ENABLE");
info.depth_write(gpu::shader::DepthWrite::UNCHANGED);
/* Replace additional info. */
for (StringRefNull &str : info.additional_infos_) {
if (str == "draw_modelmat_new") {
@@ -277,6 +278,23 @@ ShaderModule::ShaderModule(const SelectionType selection_type, const bool clippi
info.define("inst_pos", "data_buf[gl_InstanceID].xyz");
info.vertex_inputs_.pop_last();
});
wireframe_mesh = selectable_shader("overlay_wireframe", [](gpu::shader::ShaderCreateInfo &info) {
info.additional_infos_.clear();
info.define("CUSTOM_DEPTH_BIAS_CONST");
info.specialization_constant(gpu::shader::Type::BOOL, "use_custom_depth_bias", true);
info.additional_info("draw_view",
"draw_modelmat_new",
"draw_resource_handle_new",
"draw_object_infos_new",
"draw_globals");
});
wireframe_points = selectable_shader("overlay_wireframe_points",
[](gpu::shader::ShaderCreateInfo &info) {});
wireframe_curve = selectable_shader("overlay_wireframe_curve",
[](gpu::shader::ShaderCreateInfo &info) {});
}
ShaderModule &ShaderModule::module_get(SelectionType selection_type, bool clipping_enabled)

View File

@@ -0,0 +1,180 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup overlay
*/
#pragma once
#include "DNA_volume_types.h"
#include "draw_common.hh"
#include "overlay_next_mesh.hh"
namespace blender::draw::overlay {
class Wireframe {
private:
PassMain wireframe_ps_ = {"Wireframe"};
struct ColoringPass {
PassMain::Sub *curves_ps_ = nullptr;
PassMain::Sub *gpencil_ps_ = nullptr;
PassMain::Sub *mesh_ps_ = nullptr;
PassMain::Sub *pointcloud_ps_ = nullptr;
/* Variant for meshes that force drawing all edges. */
PassMain::Sub *mesh_all_edges_ps_ = nullptr;
} colored, non_colored;
bool enabled = false;
public:
void begin_sync(Resources &res, const State &state)
{
enabled = state.is_wireframe_mode || (state.overlay.flag & V3D_OVERLAY_WIREFRAMES);
if (!enabled) {
return;
}
const bool do_smooth_lines = (U.gpu_flag & USER_GPU_FLAG_OVERLAY_SMOOTH_WIRE) != 0;
const bool is_transform = (G.moving & G_TRANSFORM_OBJ) != 0;
const float wire_threshold = wire_discard_threshold_get(state.overlay.wireframe_threshold);
GPUTexture **depth_tex = (state.xray_enabled) ? &res.depth_tx : &res.dummy_depth_tx;
{
auto &pass = wireframe_ps_;
pass.init();
pass.state_set(DRW_STATE_FIRST_VERTEX_CONVENTION | DRW_STATE_WRITE_COLOR |
DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | state.clipping_state);
res.select_bind(pass);
auto shader_pass =
[&](GPUShader *shader, const char *name, bool use_coloring, float wire_threshold) {
auto &sub = pass.sub(name);
if (res.shaders.wireframe_mesh.get() == shader) {
sub.specialize_constant(shader, "use_custom_depth_bias", do_smooth_lines);
}
sub.shader_set(shader);
sub.bind_ubo("globalsBlock", &res.globals_buf);
sub.bind_texture("depthTex", depth_tex);
sub.push_constant("wireOpacity", state.overlay.wireframe_opacity);
sub.push_constant("isTransform", is_transform);
sub.push_constant("colorType", state.v3d->shading.wire_color_type);
sub.push_constant("useColoring", use_coloring);
sub.push_constant("wireStepParam", wire_threshold);
sub.push_constant("isHair", false);
return &sub;
};
auto coloring_pass = [&](ColoringPass &ps, bool use_color) {
overlay::ShaderModule &sh = res.shaders;
ps.mesh_ps_ = shader_pass(sh.wireframe_mesh.get(), "Mesh", use_color, wire_threshold);
ps.mesh_all_edges_ps_ = shader_pass(sh.wireframe_mesh.get(), "Wire", use_color, 1.0f);
ps.pointcloud_ps_ = shader_pass(sh.wireframe_points.get(), "PtCloud", use_color, 1.0f);
ps.curves_ps_ = shader_pass(sh.wireframe_curve.get(), "Curve", use_color, 1.0f);
};
coloring_pass(non_colored, false);
coloring_pass(colored, true);
}
}
void object_sync(Manager &manager,
const ObjectRef &ob_ref,
Resources &res,
const bool in_edit_paint_mode)
{
if (!enabled) {
return;
}
const bool all_edges = (ob_ref.object->dtx & OB_DRAW_ALL_EDGES) != 0;
/* TODO(fclem): Non-mandatory handle creation and reuse with other overlays. */
ResourceHandle res_handle = manager.resource_handle(ob_ref);
ColoringPass &coloring = in_edit_paint_mode ? non_colored : colored;
gpu::Batch *geom;
switch (ob_ref.object->type) {
case OB_CURVES_LEGACY:
geom = DRW_cache_curve_edge_wire_get(ob_ref.object);
coloring.curves_ps_->draw(geom, res_handle, res.select_id(ob_ref).get());
break;
case OB_FONT:
geom = DRW_cache_text_edge_wire_get(ob_ref.object);
coloring.curves_ps_->draw(geom, res_handle, res.select_id(ob_ref).get());
break;
case OB_SURF:
geom = DRW_cache_surf_edge_wire_get(ob_ref.object);
coloring.curves_ps_->draw(geom, res_handle, res.select_id(ob_ref).get());
break;
case OB_CURVES:
/* TODO(fclem): Not yet implemented. */
break;
case OB_GREASE_PENCIL:
/* TODO(fclem): Not yet implemented. */
break;
case OB_MESH:
geom = DRW_cache_mesh_face_wireframe_get(ob_ref.object);
(all_edges ? coloring.mesh_all_edges_ps_ : coloring.mesh_ps_)
->draw(geom, res_handle, res.select_id(ob_ref).get());
/* Draw loose geometry. */
if (!in_edit_paint_mode || Meshes::mesh_has_edit_cage(ob_ref.object)) {
const Mesh *mesh = static_cast<const Mesh *>(ob_ref.object->data);
if ((mesh->edges_num == 0) && (mesh->verts_num > 0)) {
geom = DRW_cache_mesh_all_verts_get(ob_ref.object);
coloring.pointcloud_ps_->draw(geom, res_handle, res.select_id(ob_ref).get());
}
else if ((geom = DRW_cache_mesh_loose_edges_get(ob_ref.object))) {
coloring.mesh_all_edges_ps_->draw(geom, res_handle, res.select_id(ob_ref).get());
}
}
break;
case OB_POINTCLOUD:
geom = DRW_pointcloud_batch_cache_get_dots(ob_ref.object);
coloring.pointcloud_ps_->draw(geom, res_handle, res.select_id(ob_ref).get());
break;
case OB_VOLUME:
geom = DRW_cache_volume_face_wireframe_get(ob_ref.object);
if (static_cast<Volume *>(ob_ref.object->data)->display.wireframe_type ==
VOLUME_WIREFRAME_POINTS)
{
coloring.pointcloud_ps_->draw(geom, res_handle, res.select_id(ob_ref).get());
}
else {
coloring.mesh_ps_->draw(geom, res_handle, res.select_id(ob_ref).get());
}
break;
default:
/* Would be good to have. */
// BLI_assert_unreachable();
break;
}
}
void draw(Framebuffer &framebuffer, Manager &manager, View &view)
{
if (!enabled) {
return;
}
GPU_framebuffer_bind(framebuffer);
manager.submit(wireframe_ps_, view);
}
private:
float wire_discard_threshold_get(float threshold)
{
/* Use `sqrt` since the value stored in the edge is a variation of the cosine, so its square
* becomes more proportional with a variation of angle. */
threshold = sqrt(abs(threshold));
/* The maximum value (255 in the VBO) is used to force hide the edge. */
return math::interpolate(0.0f, 1.0f - (1.0f / 255.0f), threshold);
}
};
} // namespace blender::draw::overlay

View File

@@ -5,8 +5,8 @@
#include "gpu_shader_create_info.hh"
GPU_SHADER_INTERFACE_INFO(overlay_wireframe_iface, "")
.flat(Type::VEC2, "edgeStart")
.smooth(Type::VEC4, "finalColor")
.flat(Type::VEC2, "edgeStart")
.no_perspective(Type::VEC2, "edgePos");
GPU_SHADER_CREATE_INFO(overlay_wireframe)
@@ -31,6 +31,47 @@ GPU_SHADER_CREATE_INFO(overlay_wireframe)
.depth_write(DepthWrite::ANY)
.additional_info("draw_mesh", "draw_object_infos", "draw_globals");
GPU_SHADER_CREATE_INFO(overlay_wireframe_curve)
.do_static_compilation(true)
.define("CURVES")
.push_constant(Type::FLOAT, "wireOpacity")
.push_constant(Type::BOOL, "useColoring")
.push_constant(Type::BOOL, "isTransform")
.push_constant(Type::INT, "colorType")
.vertex_in(0, Type::VEC3, "pos")
.vertex_out(overlay_wireframe_iface)
.vertex_source("overlay_wireframe_vert.glsl")
.fragment_source("overlay_wireframe_frag.glsl")
.fragment_out(0, Type::VEC4, "fragColor")
.fragment_out(1, Type::VEC4, "lineOutput")
.additional_info("draw_view",
"draw_modelmat_new",
"draw_resource_handle_new",
"draw_object_infos_new",
"draw_globals");
GPU_SHADER_INTERFACE_INFO(overlay_wireframe_points_iface, "")
.flat(Type::VEC4, "finalColor")
.flat(Type::VEC4, "finalColorInner");
GPU_SHADER_CREATE_INFO(overlay_wireframe_points)
.do_static_compilation(true)
.define("POINTS")
.push_constant(Type::BOOL, "useColoring")
.push_constant(Type::BOOL, "isTransform")
.push_constant(Type::INT, "colorType")
.vertex_in(0, Type::VEC3, "pos")
.vertex_out(overlay_wireframe_points_iface)
.vertex_source("overlay_wireframe_vert.glsl")
.fragment_source("overlay_wireframe_frag.glsl")
.fragment_out(0, Type::VEC4, "fragColor")
.fragment_out(1, Type::VEC4, "lineOutput")
.additional_info("draw_view",
"draw_modelmat_new",
"draw_resource_handle_new",
"draw_object_infos_new",
"draw_globals");
GPU_SHADER_CREATE_INFO(overlay_wireframe_clipped)
.do_static_compilation(true)
.additional_info("overlay_wireframe", "drw_clipped");

View File

@@ -2,47 +2,81 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(select_lib.glsl)
void main()
{
#if !defined(POINTS) && !defined(CURVES)
/* Needed only because of wireframe slider.
* If we could get rid of it would be nice because of performance drain of discard. */
if (edgeStart.r == -1.0) {
discard;
return;
}
#endif
#ifndef SELECT_EDGES
lineOutput = vec4(0.0);
#if defined(POINTS)
vec2 centered = abs(gl_PointCoord - vec2(0.5));
float dist = max(centered.x, centered.y);
float fac = dist * dist * 4.0;
/* Create a small gradient so that dense objects have a small fresnel effect. */
/* Non linear blend. */
vec3 rim_col = sqrt(finalColorInner.rgb);
vec3 wire_col = sqrt(finalColor.rgb);
vec3 final_front_col = mix(rim_col, wire_col, 0.35);
fragColor = vec4(mix(final_front_col, rim_col, saturate(fac)), 1.0);
fragColor *= fragColor;
#elif !defined(SELECT_EDGES)
lineOutput = pack_line_data(gl_FragCoord.xy, edgeStart, edgePos);
fragColor = finalColor;
# ifdef CUSTOM_DEPTH_BIAS
vec2 dir = lineOutput.xy * 2.0 - 1.0;
bool dir_horiz = abs(dir.x) > abs(dir.y);
# ifndef CUSTOM_DEPTH_BIAS_CONST
/* TODO(fclem): Cleanup after overlay next. */
# ifdef CUSTOM_DEPTH_BIAS
const bool use_custom_depth_bias = true;
# else
const bool use_custom_depth_bias = false;
# endif
# endif
vec2 uv = gl_FragCoord.xy * sizeViewportInv;
float depth_occluder = texture(depthTex, uv).r;
float depth_min = depth_occluder;
vec2 texel_uv_size = sizeViewportInv;
# if !defined(CURVES)
if (use_custom_depth_bias) {
vec2 dir = lineOutput.xy * 2.0 - 1.0;
bool dir_horiz = abs(dir.x) > abs(dir.y);
if (dir_horiz) {
depth_min = min(depth_min, texture(depthTex, uv + vec2(-texel_uv_size.x, 0.0)).r);
depth_min = min(depth_min, texture(depthTex, uv + vec2(texel_uv_size.x, 0.0)).r);
}
else {
depth_min = min(depth_min, texture(depthTex, uv + vec2(0, -texel_uv_size.y)).r);
depth_min = min(depth_min, texture(depthTex, uv + vec2(0, texel_uv_size.y)).r);
}
vec2 uv = gl_FragCoord.xy * sizeViewportInv;
float depth_occluder = texture(depthTex, uv).r;
float depth_min = depth_occluder;
vec2 texel_uv_size = sizeViewportInv;
float delta = abs(depth_occluder - depth_min);
if (dir_horiz) {
depth_min = min(depth_min, texture(depthTex, uv + vec2(-texel_uv_size.x, 0.0)).r);
depth_min = min(depth_min, texture(depthTex, uv + vec2(texel_uv_size.x, 0.0)).r);
}
else {
depth_min = min(depth_min, texture(depthTex, uv + vec2(0, -texel_uv_size.y)).r);
depth_min = min(depth_min, texture(depthTex, uv + vec2(0, texel_uv_size.y)).r);
}
if (gl_FragCoord.z < (depth_occluder + delta) && gl_FragCoord.z > depth_occluder) {
gl_FragDepth = depth_occluder;
}
else {
gl_FragDepth = gl_FragCoord.z;
float delta = abs(depth_occluder - depth_min);
# ifndef SELECT_ENABLE
if (gl_FragCoord.z < (depth_occluder + delta) && gl_FragCoord.z > depth_occluder) {
gl_FragDepth = depth_occluder;
}
else {
gl_FragDepth = gl_FragCoord.z;
}
# endif
}
# endif
#endif
select_id_output(select_id);
}

View File

@@ -5,11 +5,14 @@
#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
#pragma BLENDER_REQUIRE(select_lib.glsl)
bool is_edge_sharpness_visible(float wd)
#if !defined(POINTS) && !defined(CURVES)
bool is_edge_sharpness_visible(float wire_data)
{
return wd <= wireStepParam;
return wire_data <= wireStepParam;
}
#endif
void wire_color_get(out vec3 rim_col, out vec3 wire_col)
{
@@ -83,9 +86,16 @@ void wire_object_color_get(out vec3 rim_col, out vec3 wire_col)
void main()
{
select_id_set(drw_CustomID);
vec3 wpos = point_object_to_world(pos);
#if defined(POINTS)
gl_PointSize = sizeVertex * 2.0;
#elif defined(CURVES)
/* Noop */
#else
bool no_attr = all(equal(nor, vec3(0)));
vec3 wnor = no_attr ? drw_view.viewinv[2].xyz : normalize(normal_object_to_world(nor));
vec3 wpos = point_object_to_world(pos);
if (isHair) {
mat4 obmat = hairDupliMatrix;
@@ -97,28 +107,34 @@ void main()
vec3 V = (is_persp) ? normalize(drw_view.viewinv[3].xyz - wpos) : drw_view.viewinv[2].xyz;
float facing = dot(wnor, V);
#endif
gl_Position = point_world_to_ndc(wpos);
#ifndef CUSTOM_DEPTH_BIAS
float facing_ratio = clamp(1.0 - facing * facing, 0.0, 1.0);
float flip = sign(facing); /* Flip when not facing the normal (i.e.: back-facing). */
float curvature = (1.0 - wd * 0.75); /* Avoid making things worse for curvy areas. */
vec3 wofs = wnor * (facing_ratio * curvature * flip);
wofs = normal_world_to_view(wofs);
/* Push vertex half a pixel (maximum) in normal direction. */
gl_Position.xy += wofs.xy * sizeViewportInv * gl_Position.w;
/* Push the vertex towards the camera. Helps a bit. */
gl_Position.z -= facing_ratio * curvature * 1.0e-6 * gl_Position.w;
#ifndef CUSTOM_DEPTH_BIAS_CONST
/* TODO(fclem): Cleanup after overlay next. */
# ifdef CUSTOM_DEPTH_BIAS
const bool use_custom_depth_bias = true;
# else
const bool use_custom_depth_bias = false;
# endif
#endif
/* Convert to screen position [0..sizeVp]. */
edgeStart = ((gl_Position.xy / gl_Position.w) * 0.5 + 0.5) * sizeViewport.xy;
#if !defined(POINTS) && !defined(CURVES)
if (!use_custom_depth_bias) {
float facing_ratio = clamp(1.0 - facing * facing, 0.0, 1.0);
float flip = sign(facing); /* Flip when not facing the normal (i.e.: back-facing). */
float curvature = (1.0 - wd * 0.75); /* Avoid making things worse for curvy areas. */
vec3 wofs = wnor * (facing_ratio * curvature * flip);
wofs = normal_world_to_view(wofs);
#ifndef SELECT_EDGES
edgePos = edgeStart;
/* Push vertex half a pixel (maximum) in normal direction. */
gl_Position.xy += wofs.xy * sizeViewportInv * gl_Position.w;
/* Push the vertex towards the camera. Helps a bit. */
gl_Position.z -= facing_ratio * curvature * 1.0e-6 * gl_Position.w;
}
#endif
vec3 rim_col, wire_col;
if (colorType == V3D_SHADING_OBJECT_COLOR || colorType == V3D_SHADING_RANDOM_COLOR) {
@@ -128,27 +144,42 @@ void main()
wire_color_get(rim_col, wire_col);
}
facing = clamp(abs(facing), 0.0, 1.0);
#if defined(POINTS)
finalColor = wire_col.rgbb;
finalColorInner = rim_col.rgbb;
#else
/* Convert to screen position [0..sizeVp]. */
edgeStart = ((gl_Position.xy / gl_Position.w) * 0.5 + 0.5) * sizeViewport.xy;
edgePos = edgeStart;
# if defined(CURVES)
finalColor.rgb = rim_col;
# elif !defined(SELECT_EDGES)
facing = clamp(abs(facing), 0.0, 1.0);
/* Do interpolation in a non-linear space to have a better visual result. */
rim_col = pow(rim_col, vec3(1.0 / 2.2));
wire_col = pow(wire_col, vec3(1.0 / 2.2));
vec3 final_front_col = mix(rim_col, wire_col, 0.35);
finalColor.rgb = mix(rim_col, final_front_col, facing);
finalColor.rgb = pow(finalColor.rgb, vec3(2.2));
# endif
finalColor.a = wireOpacity;
finalColor.rgb *= wireOpacity;
#endif
# if !defined(CURVES)
/* Cull flat edges below threshold. */
if (!no_attr && !is_edge_sharpness_visible(wd)) {
edgeStart = vec2(-1.0);
}
# endif
#ifdef SELECT_EDGES
# ifdef SELECT_EDGES
/* HACK: to avoid losing sub-pixel object in selections, we add a bit of randomness to the
* wire to at least create one fragment that will pass the occlusion query. */
gl_Position.xy += sizeViewportInv * gl_Position.w * ((gl_VertexID % 2 == 0) ? -1.0 : 1.0);
# endif
#endif
view_clipping_distances(wpos);