Changeset View
Changeset View
Standalone View
Standalone View
src/scenegraph/shadowedborderrectangle.frag
- This file was added.
1 | /* | ||||
---|---|---|---|---|---|
2 | * SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl> | ||||
3 | * | ||||
4 | * SPDX-License-Identifier: LGPL-2.0-or-later | ||||
5 | */ | ||||
6 | | ||||
7 | #line 7 | ||||
8 | | ||||
9 | // This is based on the 2D SDF functions provided by Inigo Quilez: | ||||
10 | // https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm | ||||
11 | | ||||
12 | // This shader renders a rectangle with rounded corners and a shadow below it. | ||||
13 | // In addition it renders a border around it. | ||||
14 | | ||||
15 | uniform lowp float opacity; | ||||
16 | uniform lowp float size; | ||||
17 | uniform lowp float radius; | ||||
18 | uniform lowp vec4 color; | ||||
19 | uniform lowp vec4 shadowColor; | ||||
20 | uniform lowp vec2 offset; | ||||
21 | uniform lowp vec2 aspect; | ||||
22 | uniform lowp float borderWidth; | ||||
23 | uniform lowp vec4 borderColor; | ||||
24 | | ||||
25 | varying lowp vec2 uv; | ||||
26 | | ||||
27 | const lowp float minimum_shadow_radius = 0.05; | ||||
28 | const lowp float smoothing = 0.001; | ||||
29 | | ||||
30 | // Calculate the distance to a rectangle with rounded corners. | ||||
31 | // \param point The point to calculate the distance of. | ||||
32 | // \param rect The rectangle to calculate the distance of. | ||||
33 | // \param translation The amount of translation to apply to the rectangle. | ||||
34 | // \param radius A vec4 with the radius of each corner. Order is top right, bottom right, top left, bottom left. | ||||
35 | lowp float sdf_rounded_rectangle(in lowp vec2 point, in lowp vec2 rect, in lowp vec2 translation, in lowp vec4 radius) | ||||
36 | { | ||||
37 | radius.xy = (point.x > 0.0) ? radius.xy : radius.zw; | ||||
38 | radius.x = (point.y > 0.0) ? radius.x : radius.y; | ||||
39 | lowp vec2 d = abs(point - translation) - rect + radius.x; | ||||
40 | return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - radius.x; | ||||
41 | } | ||||
42 | | ||||
43 | // Render an sdf value into a color. | ||||
44 | lowp vec4 sdf_render(in lowp float sdf, in lowp vec4 sourceColor, in lowp vec4 sdfColor, in lowp float sdfAlpha) | ||||
45 | { | ||||
46 | lowp float g = fwidth(sdf); | ||||
47 | return mix(sourceColor, sdfColor, sdfAlpha * (1.0 - smoothstep(smoothing - g, smoothing + g, sdf))); | ||||
48 | } | ||||
49 | | ||||
50 | void main() | ||||
51 | { | ||||
52 | // Scaling factor that is the inverse of the amount of scaling applied to the geometry. | ||||
53 | lowp float inverse_scale = 1.0 / (1.0 + size + length(offset) * 2.0 + borderWidth * 2.0); | ||||
54 | | ||||
55 | // Correction factor to round the corners of a larger shadow. | ||||
56 | // We want to account for size in regards to shadow radius, so that a larger shadow is | ||||
57 | // more rounded, but only if we are not already rounding the corners due to corner radius. | ||||
58 | lowp float size_factor = 0.5 * (minimum_shadow_radius / max(radius, minimum_shadow_radius)); | ||||
59 | | ||||
60 | lowp float shadow_radius = radius + size * size_factor; | ||||
61 | | ||||
62 | lowp vec4 col = vec4(0.0); | ||||
63 | | ||||
64 | // Calculate the shadow's distance field. | ||||
65 | lowp float shadow = sdf_rounded_rectangle(uv, (aspect + borderWidth) * inverse_scale, offset * inverse_scale, vec4(shadow_radius * inverse_scale)); | ||||
66 | // Render it, interpolating the color over the distance. | ||||
67 | col = mix(col, shadowColor * sign(size), shadowColor.a * (1.0 - smoothstep(-size * 0.5, size * 0.5, shadow))); | ||||
68 | | ||||
69 | // Scale corrected corner radius | ||||
70 | lowp vec4 corner_radius = vec4(radius * inverse_scale); | ||||
71 | | ||||
72 | // Calculate the outer rectangle distance field. | ||||
73 | lowp float outer_rect = sdf_rounded_rectangle(uv, (aspect + borderWidth) * inverse_scale, vec2(0.0), corner_radius); | ||||
74 | | ||||
75 | // First, remove anything that was rendered by the shadow if it is inside the rectangle. | ||||
76 | // This allows us to use colors with alpha without rendering artifacts. | ||||
77 | col = sdf_render(outer_rect, col, vec4(0.0), 1.0); | ||||
78 | | ||||
79 | // Then, render it again but this time with the proper color and properly alpha blended. | ||||
80 | col = sdf_render(outer_rect, col, borderColor, borderColor.a); | ||||
81 | | ||||
82 | // Calculate the inner rectangle distance field. | ||||
83 | // This uses a reduced corner radius because the inner corners need to be smaller than the outer corners. | ||||
84 | lowp vec4 inner_radius = vec4((radius - borderWidth) * inverse_scale); | ||||
85 | lowp float inner_rect = sdf_rounded_rectangle(uv, (aspect - borderWidth) * inverse_scale, vec2(0.0), inner_radius); | ||||
86 | | ||||
87 | // Like above, but this time cut out the inner rectangle. | ||||
88 | col = sdf_render(inner_rect, col, vec4(0.0), 1.0); | ||||
89 | | ||||
90 | // Finally, render the inner rectangle. | ||||
91 | col = sdf_render(inner_rect, col, color, color.a); | ||||
92 | | ||||
93 | gl_FragColor = col * opacity; | ||||
94 | } |