UI: Improve menu dropshadow

Improvements to the drawing of shadows, used with blocks, menus, nodes,
etc. Improvements to shape, especially at the top corner or at extremes
of widget roundness. Allows transparent objects to have shadows. This
is a nice refactor that removes a lot of code.

Pull Request: https://projects.blender.org/blender/blender/pulls/111794
This commit is contained in:
Leon Schittek
2023-11-24 22:50:20 +01:00
committed by Harley Acheson
parent 0370feb1bf
commit 0335b6a3b7
9 changed files with 82 additions and 256 deletions

View File

@@ -493,7 +493,8 @@ void UI_draw_roundbox_4fv_ex(const rctf *rect,
int UI_draw_roundbox_corner_get();
#endif
void UI_draw_box_shadow(const rctf *rect, unsigned char alpha);
void ui_draw_dropshadow(const rctf *rct, float radius, float width, float aspect, float alpha);
void UI_draw_text_underline(int pos_x, int pos_y, int len, int height, const float color[4]);
/**

View File

@@ -2178,108 +2178,20 @@ void ui_draw_but_TRACKPREVIEW(ARegion * /*region*/,
/* ****************************************************** */
/* TODO(merwin): high quality UI drop shadows using GLSL shader and single draw call
* would replace / modify the following 3 functions. */
static void ui_shadowbox(const rctf *rect, uint pos, uint color, float shadsize, uchar alpha)
void ui_draw_dropshadow(
const rctf *rct, const float radius, const float width, const float aspect, const float alpha)
{
/**
* <pre>
* v1-_
* | -_v2
* | |
* | |
* | |
* v7_______v3____v4
* \ | /
* \ | _v5
* v8______v6_-
* </pre>
*/
const float v1[2] = {rect->xmax, rect->ymax - 0.3f * shadsize};
const float v2[2] = {rect->xmax + shadsize, rect->ymax - 0.75f * shadsize};
const float v3[2] = {rect->xmax, rect->ymin};
const float v4[2] = {rect->xmax + shadsize, rect->ymin};
if (width == 0.0f) {
return;
}
const float v5[2] = {rect->xmax + 0.7f * shadsize, rect->ymin - 0.7f * shadsize};
const float v6[2] = {rect->xmax, rect->ymin - shadsize};
const float v7[2] = {rect->xmin + 0.3f * shadsize, rect->ymin};
const float v8[2] = {rect->xmin + 0.5f * shadsize, rect->ymin - shadsize};
/* right quad */
immAttr4ub(color, 0, 0, 0, alpha);
immVertex2fv(pos, v3);
immVertex2fv(pos, v1);
immAttr4ub(color, 0, 0, 0, 0);
immVertex2fv(pos, v2);
immVertex2fv(pos, v2);
immVertex2fv(pos, v4);
immAttr4ub(color, 0, 0, 0, alpha);
immVertex2fv(pos, v3);
/* corner shape */
// immAttr4ub(color, 0, 0, 0, alpha); /* Not needed, done above in previous tri. */
immVertex2fv(pos, v3);
immAttr4ub(color, 0, 0, 0, 0);
immVertex2fv(pos, v4);
immVertex2fv(pos, v5);
immVertex2fv(pos, v5);
immVertex2fv(pos, v6);
immAttr4ub(color, 0, 0, 0, alpha);
immVertex2fv(pos, v3);
/* bottom quad */
// immAttr4ub(color, 0, 0, 0, alpha); /* Not needed, done above in previous tri. */
immVertex2fv(pos, v3);
immAttr4ub(color, 0, 0, 0, 0);
immVertex2fv(pos, v6);
immVertex2fv(pos, v8);
immVertex2fv(pos, v8);
immAttr4ub(color, 0, 0, 0, alpha);
immVertex2fv(pos, v7);
immVertex2fv(pos, v3);
}
void UI_draw_box_shadow(const rctf *rect, uchar alpha)
{
GPU_blend(GPU_BLEND_ALPHA);
GPUVertFormat *format = immVertexFormat();
const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
uint color = GPU_vertformat_attr_add(
format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
immBindBuiltinProgram(GPU_SHADER_3D_SMOOTH_COLOR);
immBegin(GPU_PRIM_TRIS, 54);
/* accumulated outline boxes to make shade not linear, is more pleasant */
ui_shadowbox(rect, pos, color, 11.0, (20 * alpha) >> 8);
ui_shadowbox(rect, pos, color, 7.0, (40 * alpha) >> 8);
ui_shadowbox(rect, pos, color, 5.0, (80 * alpha) >> 8);
immEnd();
immUnbindProgram();
GPU_blend(GPU_BLEND_NONE);
}
void ui_draw_dropshadow(const rctf *rct, float radius, float aspect, float alpha, int /*select*/)
{
/* This undoes the scale of the view for higher zoom factors to clamp the shadow size. */
const float clamped_aspect = smoothminf(aspect, 1.0f, 0.5f);
const float shadow_width = width * clamped_aspect;
const float shadow_offset = min_ff(shadow_width, BLI_rctf_size_y(rct) - 2.0f * radius);
const float shadow_softness = 0.6f * U.widget_unit * clamped_aspect;
const float shadow_offset = 0.5f * U.widget_unit * clamped_aspect;
const float shadow_alpha = 0.5f * alpha;
const float max_radius = (BLI_rctf_size_y(rct) - shadow_offset) * 0.5f;
const float rad = min_ff(radius, max_radius);
const float inner_radius = max_ff(radius - U.pixelsize, 0.0);
const float shadow_radius = radius + shadow_width - U.pixelsize;
GPU_blend(GPU_BLEND_ALPHA);
@@ -2287,13 +2199,13 @@ void ui_draw_dropshadow(const rctf *rct, float radius, float aspect, float alpha
widget_params.recti.xmin = rct->xmin;
widget_params.recti.ymin = rct->ymin;
widget_params.recti.xmax = rct->xmax;
widget_params.recti.ymax = rct->ymax - shadow_offset;
widget_params.rect.xmin = rct->xmin - shadow_softness;
widget_params.rect.ymin = rct->ymin - shadow_softness;
widget_params.rect.xmax = rct->xmax + shadow_softness;
widget_params.rect.ymax = rct->ymax - shadow_offset + shadow_softness;
widget_params.radi = rad;
widget_params.rad = rad + shadow_softness;
widget_params.recti.ymax = rct->ymax;
widget_params.rect.xmin = rct->xmin - shadow_width;
widget_params.rect.ymin = rct->ymin - shadow_width;
widget_params.rect.xmax = rct->xmax + shadow_width;
widget_params.rect.ymax = rct->ymax + shadow_width - shadow_offset;
widget_params.radi = inner_radius;
widget_params.rad = shadow_radius;
widget_params.round_corners[0] = (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f;
widget_params.round_corners[1] = (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f;
widget_params.round_corners[2] = (roundboxtype & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f;
@@ -2303,17 +2215,8 @@ void ui_draw_dropshadow(const rctf *rct, float radius, float aspect, float alpha
GPUBatch *batch = ui_batch_roundbox_shadow_get();
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_SHADOW);
GPU_batch_uniform_4fv_array(batch, "parameters", 4, (const float(*)[4]) & widget_params);
GPU_batch_uniform_1f(batch, "alpha", shadow_alpha);
GPU_batch_uniform_1f(batch, "alpha", alpha);
GPU_batch_draw(batch);
/* outline emphasis */
const float color[4] = {0.0f, 0.0f, 0.0f, 0.4f};
rctf rect{};
rect.xmin = rct->xmin - 0.5f;
rect.xmax = rct->xmax + 0.5f;
rect.ymin = rct->ymin - 0.5f;
rect.ymax = rct->ymax + 0.5f;
UI_draw_roundbox_4fv(&rect, false, radius + 0.5f, color);
GPU_blend(GPU_BLEND_NONE);
}

View File

@@ -1028,10 +1028,6 @@ void ui_draw_aligned_panel(const uiStyle *style,
bool region_search_filter_active);
void ui_panel_tag_search_filter_match(Panel *panel);
/* interface_draw.cc */
void ui_draw_dropshadow(const rctf *rct, float radius, float aspect, float alpha, int select);
/**
* Draws in resolution of 48x4 colors.
*/

View File

@@ -599,86 +599,6 @@ static void widget_init(uiWidgetBase *wtb)
/** \name Draw Round Box
* \{ */
/* helper call, makes shadow rect, with 'sun' above menu, so only shadow to left/right/bottom */
/* return tot */
static int round_box_shadow_edges(
float (*vert)[2], const rcti *rect, float rad, int roundboxalign, float step)
{
float vec[WIDGET_CURVE_RESOLU][2];
int tot = 0;
rad += step;
if (2.0f * rad > BLI_rcti_size_y(rect)) {
rad = 0.5f * BLI_rcti_size_y(rect);
}
const float minx = rect->xmin - step;
const float miny = rect->ymin - step;
const float maxx = rect->xmax + step;
const float maxy = rect->ymax + step;
/* Multiply. */
for (int a = 0; a < WIDGET_CURVE_RESOLU; a++) {
vec[a][0] = rad * cornervec[a][0];
vec[a][1] = rad * cornervec[a][1];
}
/* start with left-top, anti clockwise */
if (roundboxalign & UI_CNR_TOP_LEFT) {
for (int a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
vert[tot][0] = minx + rad - vec[a][0];
vert[tot][1] = maxy - vec[a][1];
}
}
else {
for (int a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
vert[tot][0] = minx;
vert[tot][1] = maxy;
}
}
if (roundboxalign & UI_CNR_BOTTOM_LEFT) {
for (int a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
vert[tot][0] = minx + vec[a][1];
vert[tot][1] = miny + rad - vec[a][0];
}
}
else {
for (int a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
vert[tot][0] = minx;
vert[tot][1] = miny;
}
}
if (roundboxalign & UI_CNR_BOTTOM_RIGHT) {
for (int a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
vert[tot][0] = maxx - rad + vec[a][0];
vert[tot][1] = miny + vec[a][1];
}
}
else {
for (int a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
vert[tot][0] = maxx;
vert[tot][1] = miny;
}
}
if (roundboxalign & UI_CNR_TOP_RIGHT) {
for (int a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
vert[tot][0] = maxx - vec[a][1];
vert[tot][1] = maxy - rad + vec[a][0];
}
}
else {
for (int a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
vert[tot][0] = maxx;
vert[tot][1] = maxy;
}
}
return tot;
}
/* this call has 1 extra arg to allow mask outline */
static void round_box__edges(
uiWidgetBase *wt, int roundboxalign, const rcti *rect, float rad, float radi)
@@ -2828,54 +2748,18 @@ static void widget_state_menu_item(uiWidgetType *wt,
/* outside of rect, rad to left/bottom/right */
static void widget_softshadow(const rcti *rect, int roundboxalign, const float radin)
{
bTheme *btheme = UI_GetTheme();
uiWidgetBase wtb;
rcti rect1 = *rect;
float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2];
const float radout = UI_ThemeMenuShadowWidth();
const float outline = U.pixelsize;
/* disabled shadow */
if (radout == 0.0f) {
return;
}
rctf shadow_rect;
BLI_rctf_rcti_copy(&shadow_rect, rect);
BLI_rctf_pad(&shadow_rect, -outline, -outline);
/* prevent tooltips to not show round shadow */
if (radout > 0.2f * BLI_rcti_size_y(&rect1)) {
rect1.ymax -= 0.2f * BLI_rcti_size_y(&rect1);
}
else {
rect1.ymax -= radout;
}
UI_draw_roundbox_corner_set(roundboxalign);
/* inner part */
const int totvert = round_box_shadow_edges(wtb.inner_v,
&rect1,
radin,
roundboxalign &
(UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT),
0.0f);
const float shadow_alpha = UI_GetTheme()->tui.menu_shadow_fac;
const float shadow_width = UI_ThemeMenuShadowWidth();
/* we draw a number of increasing size alpha quad strips */
const float alphastep = 3.0f * btheme->tui.menu_shadow_fac / radout;
const uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
for (int step = 1; step <= int(radout); step++) {
const float expfac = sqrtf(step / radout);
round_box_shadow_edges(wtb.outer_v, &rect1, radin, UI_CNR_ALL, float(step));
immUniformColor4f(0.0f, 0.0f, 0.0f, alphastep * (1.0f - expfac));
widget_verts_to_triangle_strip(&wtb, totvert, triangle_strip);
widget_draw_vertex_buffer(pos, 0, GPU_PRIM_TRI_STRIP, triangle_strip, nullptr, totvert * 2);
}
immUnbindProgram();
ui_draw_dropshadow(&shadow_rect, radin, shadow_width, 1.0f, shadow_alpha);
}
static void widget_menu_back(
@@ -5534,9 +5418,7 @@ static void ui_draw_widget_back_color(uiWidgetTypeEnum type,
uiWidgetType *wt = widget_type(type);
if (use_shadow) {
GPU_blend(GPU_BLEND_ALPHA);
widget_softshadow(rect, UI_CNR_ALL, 0.25f * U.widget_unit);
GPU_blend(GPU_BLEND_NONE);
}
rcti rect_copy = *rect;

View File

@@ -91,8 +91,6 @@
#include "GEO_fillet_curves.hh"
#include "../interface/interface_intern.hh" /* TODO: Remove */
#include "node_intern.hh" /* own include */
#include <fmt/format.h>
@@ -1857,7 +1855,20 @@ static void node_draw_shadow(const SpaceNode &snode,
{
const rctf &rct = node.runtime->totr;
UI_draw_roundbox_corner_set(UI_CNR_ALL);
ui_draw_dropshadow(&rct, radius, snode.runtime->aspect, alpha, node.flag & SELECT);
const float shadow_width = 0.6f * U.widget_unit;
const float shadow_alpha = 0.5f * alpha;
ui_draw_dropshadow(&rct, radius, shadow_width, snode.runtime->aspect, shadow_alpha);
/* Outline emphasis. Slight darkening _inside_ the outline. */
const float color[4] = {0.0f, 0.0f, 0.0f, 0.4f};
rctf rect{};
rect.xmin = rct.xmin - 0.5f;
rect.xmax = rct.xmax + 0.5f;
rect.ymin = rct.ymin - 0.5f;
rect.ymax = rct.ymax + 0.5f;
UI_draw_roundbox_4fv(&rect, false, radius + 0.5f, color);
}
static void node_draw_sockets(const View2D &v2d,

View File

@@ -1056,7 +1056,7 @@ static void draw_suggestion_list(const SpaceText *st, const TextDrawContext *tdc
rect.xmax = x + boxw;
rect.ymin = y - boxh;
rect.ymax = y;
UI_draw_box_shadow(&rect, 220);
ui_draw_dropshadow(&rect, 0.0f, 8.0f, 1.0f, 0.5f);
}
uint pos = GPU_vertformat_attr_add(

View File

@@ -6,5 +6,8 @@ void main()
{
fragColor = vec4(0.0);
/* Manual curve fit of the falloff curve of previous drawing method. */
fragColor.a = alpha * (shadowFalloff * shadowFalloff * 0.722 + shadowFalloff * 0.277);
float shadow_alpha = alpha * (shadowFalloff * shadowFalloff * 0.722 + shadowFalloff * 0.277);
float inner_alpha = smoothstep(0.0, 0.05, innerMask);
fragColor.a = inner_alpha * shadow_alpha;
}

View File

@@ -67,17 +67,41 @@ void main()
vec2(0.02, -0.805),
vec2(0.0, -1.0));
const vec2 center_offset[4] = vec2[4](
vec2(1.0, 1.0), vec2(-1.0, 1.0), vec2(-1.0, -1.0), vec2(1.0, -1.0));
uint cflag = vflag & CNR_FLAG_RANGE;
uint vofs = (vflag >> CORNER_VEC_OFS) & CORNER_VEC_RANGE;
vec2 v = cornervec[cflag * 9u + vofs];
bool is_inner = (vflag & INNER_FLAG) != 0u;
shadowFalloff = (is_inner) ? 1.0 : 0.0;
float shadow_width = rads - radsi;
float shadow_width_top = rect.w - recti.w;
/* Scale by corner radius */
v *= roundCorners[cflag] * ((is_inner) ? radsi : rads);
float rad_inner = radsi * roundCorners[cflag];
float rad_outer = rad_inner + shadow_width;
float radius = (is_inner) ? rad_inner : rad_outer;
float shadow_offset = (is_inner && (cflag > BOTTOM_RIGHT)) ? (shadow_width - shadow_width_top) :
0.0;
vec2 c = center_offset[cflag];
vec2 center_outer = rad_outer * c;
vec2 center = radius * c;
/* First expand all vertices to the outer shadow border. */
vec2 v = rad_outer * cornervec[cflag * 9u + vofs];
/* Now shrink the inner vertices onto the inner rectangle.
* At the top corners we keep the vertical offset to distribute a few of the vertices along the
* straight part of the rectangle. This allows us to get a better falloff at the top. */
if (is_inner && (cflag > BOTTOM_RIGHT) && (v.y < (shadow_offset - rad_outer))) {
v.y += shadow_width_top;
v.x = 0.0;
}
else {
v = radius * normalize(v - (center_outer + vec2(0.0, shadow_offset))) + center;
}
/* Position to corner */
vec4 rct = (is_inner) ? recti : rect;
@@ -94,5 +118,9 @@ void main()
v += rct.xw;
}
float inner_shadow_strength = min((rect.w - v.y) / rad_outer + 0.1, 1.0);
shadowFalloff = (is_inner) ? inner_shadow_strength : 0.0;
innerMask = (is_inner) ? 0.0 : 1.0;
gl_Position = ModelViewProjectionMatrix * vec4(v, 0.0, 1.0);
}

View File

@@ -49,7 +49,9 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_widget_base_inst)
.push_constant(Type::VEC4, "parameters", (MAX_PARAM * MAX_INSTANCE))
.additional_info("gpu_shader_2D_widget_shared");
GPU_SHADER_INTERFACE_INFO(gpu_widget_shadow_iface, "").smooth(Type::FLOAT, "shadowFalloff");
GPU_SHADER_INTERFACE_INFO(gpu_widget_shadow_iface, "")
.smooth(Type::FLOAT, "shadowFalloff")
.smooth(Type::FLOAT, "innerMask");
GPU_SHADER_CREATE_INFO(gpu_shader_2D_widget_shadow)
.do_static_compilation(true)