Changeset View
Changeset View
Standalone View
Standalone View
src/scenegraph/shaders/sdf.glsl
- This file was added.
1 | // SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl> | ||||
---|---|---|---|---|---|
2 | // SPDX-FileCopyrightText: 2017 Inigo Quilez | ||||
3 | // | ||||
4 | // SPDX-License-Identifier: MIT | ||||
5 | // | ||||
6 | // This file is based on | ||||
7 | // https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm | ||||
8 | | ||||
9 | //if not GLES | ||||
10 | // include "desktop_header.glsl" | ||||
11 | //else | ||||
12 | // include "es_header.glsl" | ||||
13 | | ||||
14 | // A maximum point count to be used for sdf_polygon input arrays. | ||||
15 | // Unfortunately even function inputs require a fixed size at declaration time | ||||
16 | // for arrays, unless we were to use OpenGL 4.5. | ||||
17 | // Since the polygon is most likely to be defined in a uniform, this should be | ||||
18 | // at least less than MAX_FRAGMENT_UNIFORM_COMPONENTS / 2 (since we need vec2). | ||||
19 | #define SDF_POLYGON_MAX_POINT_COUNT 400 | ||||
20 | | ||||
21 | /********************************* | ||||
22 | Shapes | ||||
23 | *********************************/ | ||||
24 | | ||||
25 | // Distance field for a circle. | ||||
26 | // | ||||
27 | // \param point A point on the distance field. | ||||
28 | // \param radius The radius of the circle. | ||||
29 | // | ||||
30 | // \return The signed distance from point to the circle. If negative, point is | ||||
31 | // inside the circle. | ||||
32 | lowp float sdf_circle(in lowp vec2 point, in lowp float radius) | ||||
33 | { | ||||
34 | return length(point) - radius; | ||||
35 | } | ||||
36 | | ||||
37 | // Distance field for a triangle. | ||||
38 | // | ||||
39 | // \param point A point on the distance field. | ||||
40 | // \param p0 The first vertex of the triangle. | ||||
41 | // \param p0 The second vertex of the triangle. | ||||
42 | // \param p0 The third vertex of the triangle. | ||||
43 | // | ||||
44 | // \note The ordering of the three vertices does not matter. | ||||
45 | // | ||||
46 | // \return The signed distance from point to triangle. If negative, point is | ||||
47 | // inside the triangle. | ||||
48 | lowp float sdf_triangle(in lowp vec2 point, in lowp vec2 p0, in lowp vec2 p1, in lowp vec2 p2) | ||||
49 | { | ||||
50 | lowp vec2 e0 = p1 - p0; | ||||
51 | lowp vec2 e1 = p2 - p1; | ||||
52 | lowp vec2 e2 = p0 - p2; | ||||
53 | | ||||
54 | lowp vec2 v0 = point - p0; | ||||
55 | lowp vec2 v1 = point - p1; | ||||
56 | lowp vec2 v2 = point - p2; | ||||
57 | | ||||
58 | lowp vec2 pq0 = v0 - e0 * clamp( dot(v0, e0) / dot(e0, e0), 0.0, 1.0 ); | ||||
59 | lowp vec2 pq1 = v1 - e1 * clamp( dot(v1, e1) / dot(e1, e1), 0.0, 1.0 ); | ||||
60 | lowp vec2 pq2 = v2 - e2 * clamp( dot(v2, e2) / dot(e2, e2), 0.0, 1.0 ); | ||||
61 | | ||||
62 | lowp float s = sign( e0.x*e2.y - e0.y*e2.x ); | ||||
63 | lowp vec2 d = min(min(vec2(dot(pq0,pq0), s*(v0.x*e0.y-v0.y*e0.x)), | ||||
64 | vec2(dot(pq1,pq1), s*(v1.x*e1.y-v1.y*e1.x))), | ||||
65 | vec2(dot(pq2,pq2), s*(v2.x*e2.y-v2.y*e2.x))); | ||||
66 | | ||||
67 | return -sqrt(d.x)*sign(d.y); | ||||
68 | } | ||||
69 | | ||||
70 | // Distance field for an arbitrary polygon. | ||||
71 | // | ||||
72 | // \param point A point on the distance field. | ||||
73 | // \param vertices An array of points that make up the polygon. | ||||
74 | // \param count The amount of points to use for the polygon. | ||||
75 | // | ||||
76 | // \note points should be an array of vec2 of size SDF_POLYGON_MAX_POINT_COUNT. | ||||
77 | // Use count to indicate how many items of that array should be used. | ||||
78 | // | ||||
79 | // \return The signed distance from point to triangle. If negative, point is | ||||
80 | // inside the triangle. | ||||
81 | | ||||
82 | // Strictly speaking, GLES 2.0 doesn't support function array arguments (apparently), so our validation fails here. | ||||
83 | // But at least Mesa GLES 2.0 accepts it, so skip validation here instead. | ||||
84 | #if !defined(GL_ES) || !defined(VALIDATING) | ||||
85 | lowp float sdf_polygon(in lowp vec2 point, in lowp vec2[SDF_POLYGON_MAX_POINT_COUNT] vertices, in lowp int count) | ||||
86 | { | ||||
87 | lowp float d = dot(point - vertices[0], point - vertices[0]); | ||||
88 | lowp float s = 1.0; | ||||
89 | for (int i = 0, j = count - 1; i < count && i < SDF_POLYGON_MAX_POINT_COUNT; j = i, i++) | ||||
90 | { | ||||
91 | lowp vec2 e = vertices[j] - vertices[i]; | ||||
92 | lowp vec2 w = point - vertices[i]; | ||||
93 | lowp float h = clamp( dot(w, e) / dot(e, e), 0.0, 1.0 ); | ||||
94 | lowp vec2 b = w - e * h; | ||||
95 | d = min(d, dot(b, b)); | ||||
96 | | ||||
97 | bvec3 c = bvec3(point.y >= vertices[i].y, point.y < vertices[j].y, e.x * w.y > e.y * w.x); | ||||
98 | if(all(c) || all(not(c))) s *= -1.0; | ||||
99 | } | ||||
100 | return s * sqrt(d); | ||||
101 | } | ||||
102 | #endif | ||||
103 | | ||||
104 | // Distance field for a rectangle. | ||||
105 | // | ||||
106 | // \param point A point on the distance field. | ||||
107 | // \param rect A vec2 with the size of the rectangle. | ||||
108 | // | ||||
109 | // \return The signed distance from point to rectangle. If negative, point is | ||||
110 | // inside the rectangle. | ||||
111 | lowp float sdf_rectangle(in lowp vec2 point, in lowp vec2 rect) | ||||
112 | { | ||||
113 | lowp vec2 d = abs(point) - rect; | ||||
114 | return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0); | ||||
115 | } | ||||
116 | | ||||
117 | // Distance field for a rectangle with rounded corners. | ||||
118 | // | ||||
119 | // \param point The point to calculate the distance of. | ||||
120 | // \param rect The rectangle to calculate the distance of. | ||||
121 | // \param radius A vec4 with the radius of each corner. Order is top right, bottom right, top left, bottom left. | ||||
122 | // | ||||
123 | // \return The signed distance from point to rectangle. If negative, point is | ||||
124 | // inside the rectangle. | ||||
125 | lowp float sdf_rounded_rectangle(in lowp vec2 point, in lowp vec2 rect, in lowp vec4 radius) | ||||
126 | { | ||||
127 | radius.xy = (point.x > 0.0) ? radius.xy : radius.zw; | ||||
128 | radius.x = (point.y > 0.0) ? radius.x : radius.y; | ||||
129 | lowp vec2 d = abs(point) - rect + radius.x; | ||||
130 | return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - radius.x; | ||||
131 | } | ||||
132 | | ||||
133 | /********************* | ||||
134 | Operators | ||||
135 | *********************/ | ||||
136 | | ||||
137 | // Convert a distance field to an annular (hollow) distance field. | ||||
138 | // | ||||
139 | // \param sdf The result of an sdf shape to convert. | ||||
140 | // \param thickness The thickness of the resulting shape. | ||||
141 | // | ||||
142 | // \return The value of sdf modified to an annular shape. | ||||
143 | lowp float sdf_annular(in lowp float sdf, in lowp float thickness) | ||||
144 | { | ||||
145 | return abs(sdf) - thickness; | ||||
146 | } | ||||
147 | | ||||
148 | // Union two sdf shapes together. | ||||
149 | // | ||||
150 | // \param sdf1 The first sdf shape. | ||||
151 | // \param sdf2 The second sdf shape. | ||||
152 | // | ||||
153 | // \return The union of sdf1 and sdf2, that is, the distance to both sdf1 and | ||||
154 | // sdf2. | ||||
155 | lowp float sdf_union(in lowp float sdf1, in lowp float sdf2) | ||||
156 | { | ||||
157 | return min(sdf1, sdf2); | ||||
158 | } | ||||
159 | | ||||
160 | // Subtract two sdf shapes. | ||||
161 | // | ||||
162 | // \param sdf1 The first sdf shape. | ||||
163 | // \param sdf2 The second sdf shape. | ||||
164 | // | ||||
165 | // \return sdf1 with sdf2 subtracted from it. | ||||
166 | lowp float sdf_subtract(in lowp float sdf1, in lowp float sdf2) | ||||
167 | { | ||||
168 | return max(sdf1, -sdf2); | ||||
169 | } | ||||
170 | | ||||
171 | // Intersect two sdf shapes. | ||||
172 | // | ||||
173 | // \param sdf1 The first sdf shape. | ||||
174 | // \param sdf2 The second sdf shape. | ||||
175 | // | ||||
176 | // \return The intersection between sdf1 and sdf2, that is, the area where both | ||||
177 | // sdf1 and sdf2 provide the same distance value. | ||||
178 | lowp float sdf_intersect(in lowp float sdf1, in lowp float sdf2) | ||||
179 | { | ||||
180 | return max(sdf1, sdf2); | ||||
181 | } | ||||
182 | | ||||
183 | // Smoothly intersect two sdf shapes. | ||||
184 | // | ||||
185 | // \param sdf1 The first sdf shape. | ||||
186 | // \param sdf2 The second sdf shape. | ||||
187 | // \param smoothing The amount of smoothing to apply. | ||||
188 | // | ||||
189 | // \return A smoothed version of the intersect operation. | ||||
190 | lowp float sdf_intersect_smooth(in lowp float sdf1, in lowp float sdf2, in lowp float smoothing) | ||||
191 | { | ||||
192 | lowp float h = clamp(0.5 - 0.5 * (sdf1 - sdf2) / smoothing, 0.0, 1.0); | ||||
193 | return mix(sdf1, sdf2, h) + smoothing * h * (1.0 - h); | ||||
194 | } | ||||
195 | | ||||
196 | // Round an sdf shape. | ||||
197 | // | ||||
198 | // \param sdf The sdf shape to round. | ||||
199 | // \param amount The amount of rounding to apply. | ||||
200 | // | ||||
201 | // \return The rounded shape of sdf. | ||||
202 | // Note that rounding happens by basically selecting an isoline of sdf, | ||||
203 | // therefore, the resulting shape may be larger than the input shape. | ||||
204 | lowp float sdf_round(in lowp float sdf, in lowp float amount) | ||||
205 | { | ||||
206 | return sdf - amount; | ||||
207 | } | ||||
208 | | ||||
209 | // Convert an sdf shape to an outline of its shape. | ||||
210 | // | ||||
211 | // \param sdf The sdf shape to turn into an outline. | ||||
212 | // | ||||
213 | // \return The outline of sdf. | ||||
214 | lowp float sdf_outline(in lowp float sdf) | ||||
215 | { | ||||
216 | return abs(sdf); | ||||
217 | } | ||||
218 | | ||||
219 | /******************** | ||||
220 | Convenience | ||||
221 | ********************/ | ||||
222 | | ||||
223 | // A constant to represent a "null" value of an sdf. | ||||
224 | // | ||||
225 | // Since 0 is a point exactly on the outline of an sdf shape, and negative | ||||
226 | // values are inside the shape, this uses a very large positive constant to | ||||
227 | // indicate a value that is really far away from the actual sdf shape. | ||||
228 | const lowp float sdf_null = 99999.0; | ||||
229 | | ||||
230 | // A constant for a default level of smoothing when rendering an sdf. | ||||
231 | // | ||||
232 | // This | ||||
233 | const lowp float sdf_default_smoothing = 0.001; | ||||
234 | | ||||
235 | // Render an sdf shape. | ||||
236 | // | ||||
237 | // This will render the sdf shape on top of whatever source color is input, | ||||
238 | // making sure to apply smoothing if desired. | ||||
239 | // | ||||
240 | // \param sdf The sdf shape to render. | ||||
241 | // \param sourceColor The source color to render on top of. | ||||
242 | // \param sdfColor The color to use for rendering the sdf shape. | ||||
243 | // | ||||
244 | // \return sourceColor with the sdf shape rendered on top. | ||||
245 | lowp vec4 sdf_render(in lowp float sdf, in lowp vec4 sourceColor, in lowp vec4 sdfColor) | ||||
246 | { | ||||
247 | lowp float g = fwidth(sdf); | ||||
248 | return mix(sourceColor, sdfColor, 1.0 - smoothstep(sdf_default_smoothing - g, sdf_default_smoothing + g, sdf)); | ||||
249 | } | ||||
250 | | ||||
251 | // Render an sdf shape. | ||||
252 | // | ||||
253 | // This is an overload of sdf_render(float, vec4, vec4) that allows specifying a | ||||
254 | // smoothing amount. | ||||
255 | // | ||||
256 | // \param smoothing The amount of smoothing to apply to the sdf. | ||||
257 | // | ||||
258 | lowp vec4 sdf_render(in lowp float sdf, in lowp vec4 sourceColor, in lowp vec4 sdfColor, in lowp float smoothing) | ||||
259 | { | ||||
260 | lowp float g = fwidth(sdf); | ||||
261 | return mix(sourceColor, sdfColor, 1.0 - smoothstep(smoothing - g, smoothing + g, sdf)); | ||||
262 | } | ||||
263 | | ||||
264 | // Render an sdf shape alpha-blended onto an existing color. | ||||
265 | // | ||||
266 | // This is an overload of sdf_render(float, vec4, vec4) that allows specifying a | ||||
267 | // blending amount and a smoothing amount. | ||||
268 | // | ||||
269 | // \param alpha The alpha to use for blending. | ||||
270 | // \param smoothing The amount of smoothing to apply to the sdf. | ||||
271 | // | ||||
272 | lowp vec4 sdf_render(in lowp float sdf, in lowp vec4 sourceColor, in lowp vec4 sdfColor, in lowp float alpha, in lowp float smoothing) | ||||
273 | { | ||||
274 | lowp float g = fwidth(sdf); | ||||
275 | return mix(sourceColor, sdfColor, alpha * (1.0 - smoothstep(smoothing - g, smoothing + g, sdf))); | ||||
276 | } |