Skip to content

Commit

Permalink
Fix shadow rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
dancazarin committed Feb 19, 2025
1 parent 381b46b commit db4b8b1
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 90 deletions.
8 changes: 1 addition & 7 deletions include/brisk/graphics/RenderState.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,11 +208,6 @@ struct TextureChannel {
static void apply(const Type& value, RenderStateEx& state);
};

struct ContourFlags {
using Type = int;
static void apply(const Type& value, RenderStateEx& state);
};

struct CoordMatrix {
using Type = Matrix;
static void apply(const Type& value, RenderStateEx& state);
Expand Down Expand Up @@ -245,7 +240,6 @@ constexpr inline Argument<Tag::Patterns> patterns{};
constexpr inline Argument<Tag::BlurRadius> blurRadius{};
constexpr inline Argument<Tag::BlurDirections> blurDirections{};
constexpr inline Argument<Tag::TextureChannel> textureChannel{};
constexpr inline Argument<Tag::ContourFlags> contourFlags{};
constexpr inline Argument<Tag::CoordMatrix> coordMatrix{};
constexpr inline Argument<Tag::SamplerMode> samplerMode{};

Expand Down Expand Up @@ -313,7 +307,7 @@ struct RenderState {

float strokeWidth = 1.f; ///< Stroke or shadow width. Defaults to 1. Set to 0 to disable
GradientType gradient = GradientType::Linear;
int shadowFlags = 3; // 1 - inner, 2 - outer
int reserved4 = 0;
float reserved5 = 0;

union {
Expand Down
2 changes: 1 addition & 1 deletion include/brisk/gui/GUI.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1066,7 +1066,7 @@ class WIDGET Widget : public BindingObject<Widget, &uiScheduler> {
Internal::Transition<ColorF> m_backgroundColor{ Palette::transparent };
Internal::Transition<ColorF> m_borderColor{ Palette::transparent };
Internal::Transition<ColorF> m_color{ Palette::white };
Internal::Transition<ColorF> m_shadowColor{ Palette::black.multiplyAlpha(0.4f) };
Internal::Transition<ColorF> m_shadowColor{ Palette::black.multiplyAlpha(0.66f) };
Internal::Transition<ColorF> m_scrollBarColor{ Palette::grey };
float m_backgroundColorTransition = 0;
float m_borderColorTransition = 0;
Expand Down
60 changes: 46 additions & 14 deletions resources/shaders/webgpu.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ struct UniformBlock {

stroke_width: f32,
gradient: gradient_type,
shadow_flags: i32,
reserved_4: i32,
reserved_5: f32,
}

Expand Down Expand Up @@ -547,21 +547,53 @@ fn atlasSubpixel(sprite: i32, pos: vec2i, stride: u32) -> vec3f {
}
}

fn shadow(signed_distance: f32) -> vec4f {
var op: f32 = 1.;
if (constants.shadow_flags & 1) == 0 && signed_distance < 0. {
op = 0.;
}
if (constants.shadow_flags & 2) == 0 && signed_distance > 0. {
op = 0.;
// Code by Evan Wallace, CC0 license

fn gaussian(x: f32, sigma: f32) -> f32 {
let pi: f32 = 3.141592653589793;
return exp(-((x * x) / (2.0 * sigma * sigma))) / (sqrt(2.0 * pi) * sigma);
}

// This approximates the error function, needed for the gaussian integral
fn erf(x: vec2<f32>) -> vec2<f32> {
let s: vec2<f32> = sign(x);
let a: vec2<f32> = abs(x);
let x1 = 1.0 + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a;
let x2 = x1 * x1;
return s - s / (x2 * x2);
}

// Return the blurred mask along the x dimension
fn roundedBoxShadowX(x: f32, y: f32, sigma: f32, corner: f32, halfSize: vec2<f32>) -> f32 {
let delta: f32 = min(halfSize.y - corner - abs(y), 0.0);
let curved: f32 = halfSize.x - corner + sqrt(max(0.0, corner * corner - delta * delta));
let integral: vec2<f32> = 0.5 + 0.5 * erf((vec2<f32>(x) + vec2<f32>(-curved, curved)) * (sqrt(0.5) / sigma));
return integral.y - integral.x;
}

// Return the mask for the shadow of a box
fn roundedBoxShadow(halfSize: vec2<f32>, point: vec2<f32>, sigma: f32, corner: f32) -> f32 {
// The signal is only non-zero in a limited range, so don't waste samples
let low: f32 = point.y - halfSize.y;
let high: f32 = point.y + halfSize.y;
let start: f32 = clamp(-3.0 * sigma, low, high);
let end: f32 = clamp(3.0 * sigma, low, high);

// Accumulate samples (we can get away with surprisingly few samples)
let step: f32 = (end - start) / 4.0;
var y: f32 = start + step * 0.5;
var value: f32 = 0.0;

for (var i: i32 = 0; i < 4; i = i + 1) {
value = value + roundedBoxShadowX(point.x, point.y - y, sigma, corner, halfSize) * gaussian(y, sigma) * step;
y = y + step;
}
let shadow_size = constants.stroke_width / 2.0;
var sh = (signed_distance + shadow_size * 0.25) / (shadow_size * 0.5);
sh = clamp(exp(-sh * sh), 0.0, 1.0);
let col = vec4f(constants.fill_color1 * sh) * sign(shadow_size);
return op * col;

return value;
}

// End of code by Evan Wallace, CC0 license

fn applyGamma(in: vec4f, gamma: f32) -> vec4f {
return pow(max(in, vec4f(0.)), vec4f(gamma));
}
Expand Down Expand Up @@ -636,7 +668,7 @@ fn postprocessColor(in: FragOut, mask_value: f32, canvas_coord: vec2u) -> FragOu
}
outColor = signedDistanceToColor(sd, in.canvas_coord, in.uv, in.data0.xy);
} else if constants.shader == shader_shadow {
outColor = shadow(sd_rectangle(in.uv, in.data0.xy, in.data1.y, i32(in.data1.z)));
outColor = constants.fill_color1 * (roundedBoxShadow(in.data0.xy * 0.5, in.uv, constants.stroke_width * 0.18, in.data1.y));
} else if constants.shader == shader_mask || constants.shader == shader_color_mask || constants.shader == shader_text {
let sprite = i32(in.data0.z);
let stride = u32(in.data0.w);
Expand Down
134 changes: 76 additions & 58 deletions src/graphics/D3D11Renderer/fragment.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ SignedDistance signedDistanceArc(float2 pt, float outer_radius, float inner_radi
}
circle = max(circle, pie);
}
SignedDistance tint_symbol_36 = {circle, -1000.0f};
return tint_symbol_36;
SignedDistance tint_symbol_40 = {circle, -1000.0f};
return tint_symbol_40;
}

SignedDistance signedDistanceRectangle(float2 uv, float2 rectSize, float borderRadius, int corners) {
Expand Down Expand Up @@ -134,8 +134,8 @@ SignedDistance signedDistanceRectangle(float2 uv, float2 rectSize, float borderR
}
}
}
SignedDistance tint_symbol_37 = {sd, intersect_sd};
return tint_symbol_37;
SignedDistance tint_symbol_41 = {sd, intersect_sd};
return tint_symbol_41;
}

struct Colors {
Expand Down Expand Up @@ -384,27 +384,45 @@ float3 atlasSubpixel(int sprite, int2 pos, uint stride) {
}
}

float4 shadow(float signed_distance) {
float op = 1.0f;
bool tint_tmp_4 = ((asint(constants[14].z) & 1) == 0);
if (tint_tmp_4) {
tint_tmp_4 = (signed_distance < 0.0f);
}
if ((tint_tmp_4)) {
op = 0.0f;
}
bool tint_tmp_5 = ((asint(constants[14].z) & 2) == 0);
if (tint_tmp_5) {
tint_tmp_5 = (signed_distance > 0.0f);
}
if ((tint_tmp_5)) {
op = 0.0f;
float gaussian(float x, float sigma) {
float pi = 3.14159274101257324219f;
return (exp(-(((x * x) / ((2.0f * sigma) * sigma)))) / (sqrt((2.0f * pi)) * sigma));
}

float2 erf(float2 x) {
float2 s = float2(sign(x));
float2 a = abs(x);
float2 x1 = (1.0f + ((0.27839300036430358887f + ((0.23038899898529052734f + (0.07810799777507781982f * (a * a))) * a)) * a));
float2 x2 = (x1 * x1);
return (s - (s / (x2 * x2)));
}

float roundedBoxShadowX(float x, float y, float sigma, float corner, float2 halfSize) {
float delta = min(((halfSize.y - corner) - abs(y)), 0.0f);
float curved = ((halfSize.x - corner) + sqrt(max(0.0f, ((corner * corner) - (delta * delta)))));
float2 tint_symbol_28 = erf(((float2((x).xx) + float2(-(curved), curved)) * (0.70710676908493041992f / sigma)));
float2 integral = (0.5f + (0.5f * tint_symbol_28));
return (integral.y - integral.x);
}

float roundedBoxShadow(float2 halfSize, float2 tint_symbol, float sigma, float corner) {
float low = (tint_symbol.y - halfSize.y);
float high = (tint_symbol.y + halfSize.y);
float start = clamp((-3.0f * sigma), low, high);
float end = clamp((3.0f * sigma), low, high);
float step = ((end - start) / 4.0f);
float y = (start + (step * 0.5f));
float value = 0.0f;
{
for(int i = 0; (i < 4); i = (i + 1)) {
float tint_symbol_29 = value;
float tint_symbol_30 = roundedBoxShadowX(tint_symbol.x, (tint_symbol.y - y), sigma, corner, halfSize);
float tint_symbol_31 = gaussian(y, sigma);
value = (tint_symbol_29 + ((tint_symbol_30 * tint_symbol_31) * step));
y = (y + step);
}
}
float shadow_size = (asfloat(constants[14].x) / 2.0f);
float sh = ((signed_distance + (shadow_size * 0.25f)) / (shadow_size * 0.5f));
sh = clamp(exp((-(sh) * sh)), 0.0f, 1.0f);
float4 col = (float4((asfloat(constants[9]) * sh)) * float(sign(shadow_size)));
return (op * col);
return value;
}

float4 applyGamma(float4 tint_symbol_2, float gamma) {
Expand All @@ -421,24 +439,24 @@ struct FragOut {
};

bool useBlending() {
bool tint_tmp_7 = (asint(constants[1].x) == 2);
if (!tint_tmp_7) {
tint_tmp_7 = (asint(constants[1].x) == 4);
bool tint_tmp_5 = (asint(constants[1].x) == 2);
if (!tint_tmp_5) {
tint_tmp_5 = (asint(constants[1].x) == 4);
}
bool tint_tmp_6 = (tint_tmp_7);
if (tint_tmp_6) {
tint_tmp_6 = (asint(constants[3].w) != 0);
bool tint_tmp_4 = (tint_tmp_5);
if (tint_tmp_4) {
tint_tmp_4 = (asint(constants[3].w) != 0);
}
return (tint_tmp_6);
return (tint_tmp_4);
}

FragOut postprocessColor(FragOut tint_symbol_2, float mask_value, uint2 canvas_coord) {
FragOut tint_symbol_3 = tint_symbol_2;
float opacity = (asfloat(constants[4].w) * mask_value);
if (((constants[4].x | constants[4].y) != 0u)) {
uint tint_symbol_28 = get_pattern(tint_div(canvas_coord.x, uint(asint(constants[4].z))), constants[4].x);
uint tint_symbol_29 = get_pattern(tint_div(canvas_coord.y, uint(asint(constants[4].z))), constants[4].y);
uint p = (tint_symbol_28 & tint_symbol_29);
uint tint_symbol_32 = get_pattern(tint_div(canvas_coord.x, uint(asint(constants[4].z))), constants[4].x);
uint tint_symbol_33 = get_pattern(tint_div(canvas_coord.y, uint(asint(constants[4].z))), constants[4].y);
uint p = (tint_symbol_32 & tint_symbol_33);
opacity = (opacity * float(p));
}
tint_symbol_3.color = (tint_symbol_3.color * opacity);
Expand All @@ -463,14 +481,14 @@ FragOut postprocessColor(FragOut tint_symbol_2, float mask_value, uint2 canvas_c
return tint_symbol_3;
}

struct tint_symbol_34 {
struct tint_symbol_38 {
noperspective float4 data0 : TEXCOORD0;
noperspective float4 data1 : TEXCOORD1;
noperspective float2 uv : TEXCOORD2;
noperspective float2 canvas_coord : TEXCOORD3;
float4 position : SV_Position;
};
struct tint_symbol_35 {
struct tint_symbol_39 {
float4 color : SV_Target0;
float4 blend : SV_Target1;
};
Expand All @@ -489,11 +507,11 @@ FragOut fragmentMain_inner(VertexOutput tint_symbol_2) {
float4 outColor = float4(0.0f, 0.0f, 0.0f, 0.0f);
float4 outBlend = float4(0.0f, 0.0f, 0.0f, 0.0f);
bool useBlend = false;
bool tint_tmp_8 = (asint(constants[1].x) == 0);
if (!tint_tmp_8) {
tint_tmp_8 = (asint(constants[1].x) == 1);
bool tint_tmp_6 = (asint(constants[1].x) == 0);
if (!tint_tmp_6) {
tint_tmp_6 = (asint(constants[1].x) == 1);
}
if ((tint_tmp_8)) {
if ((tint_tmp_6)) {
SignedDistance sd = (SignedDistance)0;
if ((asint(constants[1].x) == 0)) {
sd = signedDistanceRectangle(tint_symbol_2.uv, tint_symbol_2.data0.xy, tint_symbol_2.data1.y, tint_ftoi(tint_symbol_2.data1.z));
Expand All @@ -503,18 +521,18 @@ FragOut fragmentMain_inner(VertexOutput tint_symbol_2) {
outColor = signedDistanceToColor(sd, tint_symbol_2.canvas_coord, tint_symbol_2.uv, tint_symbol_2.data0.xy);
} else {
if ((asint(constants[1].x) == 3)) {
float tint_symbol_30 = sd_rectangle(tint_symbol_2.uv, tint_symbol_2.data0.xy, tint_symbol_2.data1.y, tint_ftoi(tint_symbol_2.data1.z));
outColor = shadow(tint_symbol_30);
float tint_symbol_34 = roundedBoxShadow((tint_symbol_2.data0.xy * 0.5f), tint_symbol_2.uv, (asfloat(constants[14].x) * 0.18000000715255737305f), tint_symbol_2.data1.y);
outColor = (asfloat(constants[9]) * tint_symbol_34);
} else {
bool tint_tmp_10 = (asint(constants[1].x) == 4);
if (!tint_tmp_10) {
tint_tmp_10 = (asint(constants[1].x) == 5);
bool tint_tmp_8 = (asint(constants[1].x) == 4);
if (!tint_tmp_8) {
tint_tmp_8 = (asint(constants[1].x) == 5);
}
bool tint_tmp_9 = (tint_tmp_10);
if (!tint_tmp_9) {
tint_tmp_9 = (asint(constants[1].x) == 2);
bool tint_tmp_7 = (tint_tmp_8);
if (!tint_tmp_7) {
tint_tmp_7 = (asint(constants[1].x) == 2);
}
if ((tint_tmp_9)) {
if ((tint_tmp_7)) {
int sprite = tint_ftoi(tint_symbol_2.data0.z);
uint stride = tint_ftou_1(tint_symbol_2.data0.w);
int2 tuv = tint_ftoi_1(tint_symbol_2.uv);
Expand All @@ -525,9 +543,9 @@ FragOut fragmentMain_inner(VertexOutput tint_symbol_2) {
outBlend = float4((colors.brush.a * rgb), 1.0f);
} else {
if ((asint(constants[1].x) == 5)) {
float4 tint_symbol_31 = colors.brush;
float4 tint_symbol_32 = atlasRGBA(sprite, tuv, stride);
outColor = (tint_symbol_31 * tint_symbol_32);
float4 tint_symbol_35 = colors.brush;
float4 tint_symbol_36 = atlasRGBA(sprite, tuv, stride);
outColor = (tint_symbol_35 * tint_symbol_36);
} else {
float alpha = atlasAccum(sprite, tuv, stride);
outColor = (colors.brush * float4((alpha).xxxx));
Expand All @@ -536,14 +554,14 @@ FragOut fragmentMain_inner(VertexOutput tint_symbol_2) {
}
}
}
FragOut tint_symbol_38 = {outColor, outBlend};
return postprocessColor(tint_symbol_38, mask_value, tint_ftou(tint_symbol_2.canvas_coord));
FragOut tint_symbol_42 = {outColor, outBlend};
return postprocessColor(tint_symbol_42, mask_value, tint_ftou(tint_symbol_2.canvas_coord));
}

tint_symbol_35 fragmentMain(tint_symbol_34 tint_symbol_33) {
VertexOutput tint_symbol_39 = {float4(tint_symbol_33.position.xyz, (1.0f / tint_symbol_33.position.w)), tint_symbol_33.data0, tint_symbol_33.data1, tint_symbol_33.uv, tint_symbol_33.canvas_coord};
FragOut inner_result = fragmentMain_inner(tint_symbol_39);
tint_symbol_35 wrapper_result = (tint_symbol_35)0;
tint_symbol_39 fragmentMain(tint_symbol_38 tint_symbol_37) {
VertexOutput tint_symbol_43 = {float4(tint_symbol_37.position.xyz, (1.0f / tint_symbol_37.position.w)), tint_symbol_37.data0, tint_symbol_37.data1, tint_symbol_37.uv, tint_symbol_37.canvas_coord};
FragOut inner_result = fragmentMain_inner(tint_symbol_43);
tint_symbol_39 wrapper_result = (tint_symbol_39)0;
wrapper_result.color = inner_result.color;
wrapper_result.blend = inner_result.blend;
if (tint_discarded) {
Expand Down
4 changes: 0 additions & 4 deletions src/graphics/RenderState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,6 @@ void Tag::TextureChannel::apply(const Tag::TextureChannel::Type& value, RenderSt
state.textureChannel = value;
}

void Tag::ContourFlags::apply(const Tag::ContourFlags::Type& value, RenderStateEx& state) {
state.shadowFlags = value;
}

void Tag::CoordMatrix::apply(const Tag::CoordMatrix::Type& value, RenderStateEx& state) {
state.coordMatrix = value;
}
Expand Down
30 changes: 30 additions & 0 deletions src/graphics/Renderer_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -571,4 +571,34 @@ TEST_CASE("Multi-pass render") {
});
}

TEST_CASE("Shadow") {
renderTest(
"shadows", Size{ 1536, 256 },
[&](RenderContext& context) {
RawCanvas canvas(context);
for (int i = 0; i < 6; ++i) {
RectangleF box{ 256.f * i, 0, 256.f * i + 256, 256 };
context.setClipRect(box);
float shadowSize = 2 << i;
canvas.drawShadow(box.withPadding(64.f), 0.f, 0.f, contourSize = shadowSize,
contourColor = Palette::black);
}
},
Palette::white);

renderTest(
"shadows-rounded", Size{ 1536, 256 },
[&](RenderContext& context) {
RawCanvas canvas(context);
for (int i = 0; i < 6; ++i) {
RectangleF box{ 256.f * i, 0, 256.f * i + 256, 256 };
context.setClipRect(box);
float boxRadius = 2 << i;
canvas.drawShadow(box.withPadding(64.f), boxRadius, 0.f, contourSize = 16.f,
contourColor = Palette::black);
}
},
Palette::white);
}

} // namespace Brisk
Binary file added src/graphics/testdata/shadows-rounded.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/graphics/testdata/shadows.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit db4b8b1

Please sign in to comment.