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 }