Changeset View
Changeset View
Standalone View
Standalone View
src/scenegraph/shadowedrectangle_core.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 | // This is based on the 2D SDF functions provided by Inigo Quilez: | ||||
8 | // https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm | ||||
9 | | ||||
10 | // This shader renders a rectangle with rounded corners and a shadow below it. | ||||
11 | | ||||
12 | uniform lowp float opacity; | ||||
13 | uniform lowp float size; | ||||
14 | uniform lowp float radius; | ||||
15 | uniform lowp vec4 color; | ||||
16 | uniform lowp vec4 shadowColor; | ||||
17 | uniform lowp vec2 offset; | ||||
18 | uniform lowp vec2 aspect; | ||||
19 | | ||||
20 | in lowp vec2 uv; | ||||
21 | | ||||
22 | const lowp float minimum_shadow_radius = 0.05; | ||||
23 | | ||||
24 | // Calculate the distance to a rectangle with rounded corners. | ||||
25 | // \param point The point to calculate the distance of. | ||||
26 | // \param rect The rectangle to calculate the distance of. | ||||
27 | // \param translation The amount of translation to apply to the rectangle. | ||||
28 | // \param radius A vec4 with the radius of each corner. Order is top right, bottom right, top left, bottom left. | ||||
29 | lowp float sdf_rounded_rectangle(in lowp vec2 point, in lowp vec2 rect, in lowp vec2 translation, in lowp vec4 radius) | ||||
30 | { | ||||
31 | radius.xy = (point.x > 0.0) ? radius.xy : radius.zw; | ||||
32 | radius.x = (point.y > 0.0) ? radius.x : radius.y; | ||||
33 | lowp vec2 d = abs(point - translation) - rect + radius.x; | ||||
34 | return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - radius.x; | ||||
35 | } | ||||
36 | | ||||
37 | void main() | ||||
38 | { | ||||
39 | // Scaling factor that is the inverse of the amount of scaling applied to the geometry. | ||||
40 | lowp float inverse_scale = 1.0 / (1.0 + size + length(offset) * 2.0); | ||||
41 | | ||||
42 | // Correction factor to round the corners of a larger shadow. | ||||
43 | // We want to account for size in regards to shadow radius, so that a larger shadow is | ||||
44 | // more rounded, but only if we are not already rounding the corners due to corner radius. | ||||
45 | lowp float size_factor = 0.5 * (minimum_shadow_radius / max(radius, minimum_shadow_radius)); | ||||
46 | | ||||
47 | lowp float shadowRadius = radius + size * size_factor; | ||||
48 | | ||||
49 | lowp vec4 col = vec4(0.0); | ||||
50 | | ||||
51 | // Calculate the shadow's distance field. | ||||
52 | lowp float shadow = sdf_rounded_rectangle(uv, aspect * inverse_scale, offset * inverse_scale, vec4(shadowRadius * inverse_scale)); | ||||
53 | // Render it, interpolating the color over the distance. | ||||
54 | col = mix(col, shadowColor * sign(size), shadowColor.a * (1.0 - smoothstep(-size * 0.5, size * 0.5, shadow))); | ||||
55 | | ||||
56 | // Calculate the main rectangle distance field. | ||||
57 | lowp float rect = sdf_rounded_rectangle(uv, aspect * inverse_scale, vec2(0.0), vec4(radius * inverse_scale)); | ||||
58 | | ||||
59 | lowp float g = fwidth(rect); | ||||
60 | | ||||
61 | // First, remove anything that was rendered by the shadow if it is inside the rectangle. | ||||
62 | // This allows us to use colors with alpha without rendering artifacts. | ||||
63 | col = mix(col, vec4(0.0), 1.0 - smoothstep(0.001 - g, 0.001 + g, rect)); | ||||
64 | | ||||
65 | // Then, render it again but this time with the proper color and properly alpha blended. | ||||
66 | col = mix(col, color, color.a * (1.0 - smoothstep(0.001 - g, 0.001 + g, rect))); | ||||
67 | | ||||
68 | gl_FragColor = col * opacity; | ||||
69 | } |