1 /*
2 * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package shade.shaders;
26
27 import hat.Accelerator;
28 import hat.backend.Backend;
29 import hat.types.vec2;
30 import hat.types.vec3;
31 import hat.types.vec4;
32 import shade.Config;
33 import shade.Shader;
34 import shade.ShaderApp;
35 import hat.buffer.Uniforms;
36
37 import java.io.IOException;
38 import java.lang.invoke.MethodHandles;
39
40 import static hat.types.F32.abs;
41 import static hat.types.F32.cos;
42 import static hat.types.F32.max;
43 import static hat.types.F32.min;
44 import static hat.types.F32.sin;
45 import static hat.types.F32.smoothstep;
46 import static hat.types.vec2.add;
47 import static hat.types.vec2.div;
48 import static hat.types.vec2.dot;
49 import static hat.types.vec2.mul;
50 import static hat.types.vec2.round;
51 import static hat.types.vec2.sub;
52 import static hat.types.vec2.vec2;
53 import static hat.types.vec3.mix;
54 import static hat.types.vec3.vec3;
55 import static hat.types.vec4.normalize;
56 import static hat.types.vec4.vec4;
57
58 //https://www.shadertoy.com/view/4tXyWs
59 public class MobiusShader implements Shader {
60 String glsSource = """
61 vec2 ortho(vec2 v)
62 {
63 return vec2(v.y, -v.x);
64 }
65
66 void stroke(float dist, vec3 color, inout vec3 fragColor, float thickness, float aa)
67 {
68 float alpha = smoothstep(0.5 * (thickness + aa), 0.5 * (thickness - aa), abs(dist));
69 fragColor = mix(fragColor, color, alpha);
70 }
71
72 void fill(float dist, vec3 color, inout vec3 fragColor, float aa)
73 {
74 float alpha = smoothstep(0.5*aa, -0.5*aa, dist);
75 fragColor = mix(fragColor, color, alpha);
76 }
77
78 void renderGrid(vec2 pos, out vec3 fragColor)
79 {
80 vec3 background = vec3(1.0);
81 vec3 axes = vec3(0.4);
82 vec3 lines = vec3(0.7);
83 vec3 sublines = vec3(0.95);
84 float subdiv = 10.0;
85
86 float thickness = 0.003;
87 float aa = length(fwidth(pos));
88
89 fragColor = background;
90
91 vec2 toSubGrid = pos - round(pos*subdiv)/subdiv;
92 stroke(min(abs(toSubGrid.x), abs(toSubGrid.y)), sublines, fragColor, thickness, aa);
93
94 vec2 toGrid = pos - round(pos);
95 stroke(min(abs(toGrid.x), abs(toGrid.y)), lines, fragColor, thickness, aa);
96
97 stroke(min(abs(pos.x), abs(pos.y)), axes, fragColor, thickness, aa);
98 }
99
100 float sdistLine(vec2 a, vec2 b, vec2 pos)
101 {
102 return dot(pos - a, normalize(ortho(b - a)));
103 }
104
105 float sdistTri(vec2 a, vec2 b, vec2 c, vec2 pos)
106 {
107 return max( sdistLine(a, b, pos),
108 max(sdistLine(b, c, pos),
109 sdistLine(c, a, pos)));
110 }
111
112 float sdistQuadConvex(vec2 a, vec2 b, vec2 c, vec2 d, vec2 pos)
113 {
114 return max( sdistLine(a, b, pos),
115 max( sdistLine(b, c, pos),
116 max(sdistLine(c, d, pos),
117 sdistLine(d, a, pos))));
118 }
119
120 void renderUnitSquare(vec2 pos, inout vec3 fragColor)
121 {
122 #if 0
123 // Put a texture in there
124 if (pos.x >= 0.0 && pos.y >= 0.0 && pos.x <= 1.0 && pos.y <= 1.0)
125 {
126 fragColor.rgb = texture(iChannel0, pos).rgb;
127 }
128 #endif
129
130 float dist = sdistQuadConvex(vec2(0, 0),
131 vec2(1, 0),
132 vec2(1, 1),
133 vec2(0, 1), pos);
134 stroke(dist, vec3(0, 0, 1), fragColor, 0.007, length(fwidth(pos)));
135 }
136
137 void renderAxes(vec2 origin, vec2 pos, inout vec3 fragColor)
138 {
139 float len = 0.1;
140 float thickness = 0.0075;
141 float aa = length(fwidth(pos));
142
143 float xshaft = sdistQuadConvex(origin + vec2(0.5*thickness),
144 origin - vec2(0.5*thickness),
145 origin + vec2(len, -0.5*thickness),
146 origin + vec2(len, 0.5*thickness), pos);
147 float xhead = sdistTri(origin + vec2(len, -2.0*thickness),
148 origin + vec2(len + 6.0*thickness, 0),
149 origin + vec2(len, 2.0*thickness), pos);
150
151 fill(min(xshaft, xhead), vec3(1, 0, 0), fragColor, aa);
152
153 float yshaft = sdistQuadConvex(origin - vec2(0.5*thickness),
154 origin + vec2(0.5*thickness),
155 origin + vec2(0.5*thickness, len),
156 origin + vec2(-0.5*thickness, len), pos);
157 float yhead = sdistTri(origin + vec2(2.0*thickness, len),
158 origin + vec2(0, len + 6.0*thickness),
159 origin + vec2(-2.0*thickness, len), pos);
160
161 fill(min(yshaft, yhead), vec3(0, 0.75, 0), fragColor, aa);
162
163 }
164
165 vec2 cmul(vec2 a, vec2 b)
166 {
167 return vec2(a.x*b.x - a.y*b.y, a.x*b.y + a.y*b.x);
168 }
169
170 vec2 cdiv(vec2 a, vec2 b)
171 {
172 return cmul(a, vec2(b.x, -b.y)) / dot(b, b);
173 }
174
175 void mainImage(out vec4 fragColor, in vec2 fragCoord)
176 {
177 float aspect = iResolution.x / iResolution.y;
178 vec2 pos = (fragCoord / iResolution.y) * 1.5 - vec2((1.5*aspect - 1.0)/2.0, 0.25);
179
180 // apply a Möbius transformation to the plane
181 vec2 a = vec2(1, sin(0.4*iTime));
182 vec2 b = vec2(0);
183 vec2 c = vec2(0.5*cos(0.6*iTime), 0.5*sin(0.5*iTime));
184 vec2 d = vec2(1, cos(0.3*iTime));
185 pos -= vec2(0.5);
186 pos = cdiv(cmul(a, pos) + b, cmul(c, pos) + d);
187 pos += vec2(0.5);
188
189 // render the grid and stuff
190 fragColor.a = 1.0;
191 renderGrid(pos, fragColor.rgb);
192 renderUnitSquare(pos, fragColor.rgb);
193 renderAxes(vec2(0), pos, fragColor.rgb);
194 }
195
196 """;
197
198 /*
199 vec2 ortho(vec2 v)
200 {
201 return vec2(v.y, -v.x);
202 }
203 */
204 vec2 ortho(vec2 v) {
205 return vec2(v.y(), -v.x());
206 }
207
208 /*
209 void stroke(float dist, vec3 color, inout vec3 fragColor, float thickness, float aa)
210 {
211 float alpha = smoothstep(0.5 * (thickness + aa), 0.5 * (thickness - aa), abs(dist));
212 fragColor = mix(fragColor, color, alpha);
213 }
214 */
215 vec3 stroke(float dist, vec3 color, vec3 fragColor, float thickness, float aa) {
216 float alpha = smoothstep(0.5f * (thickness + aa), 0.5f * (thickness - aa), abs(dist));
217 return mix(fragColor, color, alpha);
218 }
219 /*
220
221 void fill(float dist, vec3 color, inout vec3 fragColor, float aa)
222 {
223 float alpha = smoothstep(0.5*aa, -0.5*aa, dist);
224 fragColor = mix(fragColor, color, alpha);
225 }
226
227 */
228
229 vec3 fill(float dist, vec3 color, vec3 fragColor, float aa) {
230 float alpha = smoothstep(0.5f * aa, -0.5f * aa, dist);
231 return mix(fragColor, color, alpha);
232 }
233
234 /*
235 void renderGrid(vec2 pos, out vec3 fragColor)
236 {
237 vec3 background = vec3(1.0);
238 vec3 axes = vec3(0.4);
239 vec3 lines = vec3(0.7);
240 vec3 sublines = vec3(0.95);
241 float subdiv = 10.0;
242
243 float thickness = 0.003;
244 float aa = length(fwidth(pos));
245
246 fragColor = background;
247
248 vec2 toSubGrid = pos - round(pos*subdiv)/subdiv;
249 stroke(min(abs(toSubGrid.x), abs(toSubGrid.y)), sublines, fragColor, thickness, aa);
250
251 vec2 toGrid = pos - round(pos);
252 stroke(min(abs(toGrid.x), abs(toGrid.y)), lines, fragColor, thickness, aa);
253
254 stroke(min(abs(pos.x), abs(pos.y)), axes, fragColor, thickness, aa);
255 }
256 */
257 void renderGrid(vec2 pos, vec3 fragColor) {
258 vec3 background = vec3(1.0f);
259 vec3 axes = vec3(0.4f);
260 vec3 lines = vec3(0.7f);
261 vec3 sublines = vec3(0.95f);
262 float subdiv = 10.0f;
263
264 float thickness = 0.003f;
265 float fwidthPos = 0.01f;
266 float aa = fwidthPos;//?length(fwidthPos);
267
268 fragColor = background;
269
270 vec2 toSubGrid = sub(pos, div(vec2.round(mul(pos, subdiv)), subdiv));
271 stroke(min(abs(toSubGrid.x()), abs(toSubGrid.y())), sublines, fragColor, thickness, aa);
272
273 vec2 toGrid = sub(pos, round(pos));
274 stroke(min(abs(toGrid.x()), abs(toGrid.y())), lines, fragColor, thickness, aa);
275
276 stroke(min(abs(pos.x()), abs(pos.y())), axes, fragColor, thickness, aa);
277 }
278
279 /*
280 float sdistLine(vec2 a, vec2 b, vec2 pos)
281 {
282 return dot(pos - a, normalize(ortho(b - a)));
283 }
284 */
285 float sdistLine(vec2 a, vec2 b, vec2 pos) {
286 return dot(sub(pos, a), vec2.normalize(ortho(sub(b, a))));
287 }
288 /*
289 float sdistTri(vec2 a, vec2 b, vec2 c, vec2 pos)
290 {
291 return max( sdistLine(a, b, pos),
292 max(sdistLine(b, c, pos),
293 sdistLine(c, a, pos)));
294 }
295 */
296
297 float sdistTri(vec2 a, vec2 b, vec2 c, vec2 pos) {
298 return max(sdistLine(a, b, pos),
299 max(sdistLine(b, c, pos),
300 sdistLine(c, a, pos)));
301 }
302
303 /*
304 float sdistQuadConvex(vec2 a, vec2 b, vec2 c, vec2 d, vec2 pos)
305 {
306 return max( sdistLine(a, b, pos),
307 max( sdistLine(b, c, pos),
308 max(sdistLine(c, d, pos),
309 sdistLine(d, a, pos))));
310 }
311 */
312 float sdistQuadConvex(vec2 a, vec2 b, vec2 c, vec2 d, vec2 pos) {
313 return max(sdistLine(a, b, pos),
314 max(sdistLine(b, c, pos),
315 max(sdistLine(c, d, pos),
316 sdistLine(d, a, pos))));
317 }
318
319 /*
320 void renderUnitSquare(vec2 pos, inout vec3 fragColor)
321 {
322 #if 0
323 // Put a texture in there
324 if (pos.x >= 0.0 && pos.y >= 0.0 && pos.x <= 1.0 && pos.y <= 1.0)
325 {
326 fragColor.rgb = texture(iChannel0, pos).rgb;
327 }
328 #endif
329
330 float dist = sdistQuadConvex(vec2(0, 0),
331 vec2(1, 0),
332 vec2(1, 1),
333 vec2(0, 1), pos);
334 stroke(dist, vec3(0, 0, 1), fragColor, 0.007, length(fwidth(pos)));
335 }
336 */
337 vec3 renderUnitSquare(vec2 pos, vec3 fragColor) {
338
339 float dist = sdistQuadConvex(vec2(0, 0),
340 vec2(1, 0),
341 vec2(1, 1),
342 vec2(0, 1), pos);
343 float fwidthPos = .0f;
344 return stroke(dist, vec3(0, 0, 1), fragColor, 0.007f, fwidthPos/*length(fwidth(pos)*/);
345 }
346
347 /*
348 void renderAxes(vec2 origin, vec2 pos, inout vec3 fragColor)
349 {
350 float len = 0.1;
351 float thickness = 0.0075;
352 float aa = length(fwidth(pos));
353
354 float xshaft = sdistQuadConvex(origin + vec2(0.5*thickness),
355 origin - vec2(0.5*thickness),
356 origin + vec2(len, -0.5*thickness),
357 origin + vec2(len, 0.5*thickness), pos);
358 float xhead = sdistTri(origin + vec2(len, -2.0*thickness),
359 origin + vec2(len + 6.0*thickness, 0),
360 origin + vec2(len, 2.0*thickness), pos);
361
362 fill(min(xshaft, xhead), vec3(1, 0, 0), fragColor, aa);
363
364 float yshaft = sdistQuadConvex(origin - vec2(0.5*thickness),
365 origin + vec2(0.5*thickness),
366 origin + vec2(0.5*thickness, len),
367 origin + vec2(-0.5*thickness, len), pos);
368 float yhead = sdistTri(origin + vec2(2.0*thickness, len),
369 origin + vec2(0, len + 6.0*thickness),
370 origin + vec2(-2.0*thickness, len), pos);
371
372 fill(min(yshaft, yhead), vec3(0, 0.75, 0), fragColor, aa);
373
374 }
375 */
376 vec3 renderAxes(vec2 origin, vec2 pos, vec3 fragColor) {
377 float len = 0.1f;
378 float thickness = 0.0075f;
379 float fwidthPos = 0.01f;
380 float aa = fwidthPos;//length(fwidth(pos));
381
382 float xshaft = sdistQuadConvex(add(origin, vec2(0.5f * thickness)),
383 sub(origin, vec2(0.5f * thickness)),
384 add(origin, vec2(len, -0.5f * thickness)),
385 add(origin, vec2(len, 0.5f * thickness)), pos);
386
387 float xhead = sdistTri(add(origin, vec2(len, -2.0f * thickness)),
388 add(origin, vec2(len + 6.0f * thickness, 0f)),
389 add(origin, vec2(len, 2.0f * thickness)), pos);
390
391 fragColor = fill(min(xshaft, xhead), vec3(1f, 0f, 0f), fragColor, aa);
392
393 float yshaft = sdistQuadConvex(add(origin, vec2(0.5f * thickness)),
394 add(origin, vec2(0.5f * thickness)),
395 add(origin, vec2(0.5f * thickness, len)),
396 add(origin, vec2(-0.5f * thickness, len)), pos);
397
398 float yhead = sdistTri(add(origin, vec2(2.0f * thickness, len)),
399 add(origin, vec2(0, len + 6.0f * thickness)),
400 add(origin, vec2(-2.0f * thickness, len)), pos);
401
402 fragColor = fill(min(yshaft, yhead), vec3(0f, 0.75f, 0f), fragColor, aa);
403
404 return fragColor;
405 }
406
407 /*
408 vec2 cmul(vec2 a, vec2 b)
409 {
410 return vec2(a.x*b.x - a.y*b.y, a.x*b.y + a.y*b.x);
411 }
412 */
413 vec2 cmul(vec2 a, vec2 b) {
414 return vec2(a.x() * b.x() - a.y() * b.y(), a.x() * b.y() + a.y() * b.x());
415 }
416 /*
417 vec2 cdiv(vec2 a, vec2 b)
418 {
419 return cmul(a, vec2(b.x, -b.y)) / dot(b, b);
420 }
421 */
422
423 vec2 cdiv(vec2 a, vec2 b) {
424 return div(cmul(a, vec2(b.x(), -b.y())), dot(b, b));
425 }
426
427 @Override
428 public vec4 mainImage(Uniforms uniforms, vec4 fragColor, vec2 fragCoord) {
429 fragColor = vec4(1f, 1f, 1f, 1f);
430 float aspect = uniforms.iResolution().x() / uniforms.iResolution().y();
431 vec2 pos = sub(mul(div(fragCoord, uniforms.iResolution().y()), 1.5f), vec2((1.5f * aspect - 1.0f) / 2.0f, 0.25f));
432
433 // apply a Möbius transformation to the plane
434 vec2 a = vec2(1f, sin(0.4f * uniforms.iTime()));
435 vec2 b = vec2(0f);
436 vec2 c = vec2(0.5f * cos(0.6f * uniforms.iTime()), 0.5f * sin(0.5f * uniforms.iTime()));
437 vec2 d = vec2(1f, cos(0.3f * uniforms.iTime()));
438 pos = sub(pos, vec2(0.5f));
439 pos = cdiv(add(cmul(a, pos), b), add(cmul(c, pos), d));
440 pos = add(pos, vec2(0.5f));
441
442 // render the grid and stuff
443 fragColor = vec4(fragColor.x(), fragColor.y(), fragColor.z(), 1.0f);
444
445 renderGrid(pos, vec3(fragColor.x(),fragColor.y(),fragColor.z()));
446 fragColor = vec4(renderUnitSquare(pos, vec3(fragColor.x(),fragColor.y(),fragColor.z())), 1f);
447 fragColor = vec4(renderAxes(vec2(0f), pos, vec3(fragColor.x(),fragColor.y(),fragColor.z())), 1f);
448 return normalize(fragColor);
449 }
450
451 static Config controls = Config.of(
452 Boolean.getBoolean("hat") ? new Accelerator(MethodHandles.lookup(), Backend.FIRST) : null,
453 Integer.parseInt(System.getProperty("width", System.getProperty("size", "512"))),
454 Integer.parseInt(System.getProperty("height", System.getProperty("size", "512"))),
455 Integer.parseInt(System.getProperty("targetFps", "5")),
456 new MobiusShader()
457 );
458
459 static void main(String[] args) throws IOException {
460 new ShaderApp(controls);
461 }
462 }