~rycwo/forge

forge/shader/gui_rect.frag.glsl -rw-r--r-- 2.2 KiB
d7ee94d6Ryan Chan Fix missing cd in .build.yml 2 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// This file is part of Forge, the foundation library for Forge tools.
//
// Copyright (C) 2021 Ryan Chan <rycwo@posteo.net>
// SPDX-License-Identifier: GPL-3.0-only
//
// This Source Code Form is subject to the terms of the GNU General Public
// License v3.0 only. You should have received a copy of the license along with
// this program. If not, see <https://www.gnu.org/licenses/>.

#version 460 core

layout(location = 0) in vertex_attr {
	flat vec4 color;
	flat vec4 border_color;
	flat vec2 size;
	flat vec2 center;
	flat float border_width;
	flat float corner_radius;
	flat int circle;
} frag_in;

layout(location = 0) out vec4 color;

const float aa_width = 0.9;

// Distance functions adapted from:
// https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm

float
circle(in vec2 pos, in float radius) {
	return length(pos) - radius;
}

float
rounded_box(in vec2 pos, in vec2 size, in float radius) {
	vec2 d = abs(pos) - size + radius;
	return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - radius;
}

void
main(void) {
	// Express fragment position relative to rect center
	vec2 pos = gl_FragCoord.xy - frag_in.center;

	// Shape excluding border
	vec2 s0 = frag_in.size * 0.5;
	float d0 = rounded_box(pos, s0, frag_in.corner_radius);
	d0 = bool(frag_in.circle) ? circle(pos, s0.x) : d0;

	// Shape including border
	vec2 s1 = s0 + frag_in.border_width;
	float d1 = rounded_box(pos, s1, frag_in.corner_radius);
	d1 = bool(frag_in.circle) ? circle(pos, s1.x) : d1;

	float dd0 = fwidth(d0) * aa_width;
	color = frag_in.color * (1.0 - smoothstep(-dd0, dd0, d0));

	// Subtract the shape to w/o border from the full shape to get only the
	// border. This allows the border and fill to have separate translucencies.
	// FIXME: Anti-aliasing on the outer edge of the fill and the inner edge of
	// the border creates a small translucent gap through which elements beneath
	// peek through.
	d1 = mix(1.0, max(-d0, d1), float(frag_in.border_width > 0.0));

	float dd1 = fwidth(d1) * aa_width;
	color = mix(color, frag_in.border_color, 1.0 - smoothstep(-dd1, dd1, d1));

	// Un-premultiply alpha to avoid artifacts when blending anti-aliased
	// translucent pixels. TODO: Does this break translucent fills?
	color.xyz *= 1.0 / color.w;
}