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.ComputeContext;
 29 import hat.Accelerator.Compute;
 30 import hat.ComputeContext.Kernel;
 31 import hat.KernelContext;
 32 import hat.NDRange;
 33 import hat.backend.Backend;
 34 import hat.buffer.F32Array;
 35 import hat.types.vec2;
 36 import hat.types.vec3;
 37 import hat.types.vec4;
 38 import jdk.incubator.code.Reflect;
 39 import optkl.ifacemapper.MappableIface;
 40 import hat.buffer.Uniforms;
 41 import shade.ShaderViewer;
 42 import java.lang.invoke.MethodHandles;
 43 
 44 import static hat.types.F32.abs;
 45 import static hat.types.F32.cos;
 46 import static hat.types.F32.max;
 47 import static hat.types.F32.min;
 48 import static hat.types.F32.sin;
 49 import static hat.types.F32.smoothstep;
 50 import static hat.types.vec2.add;
 51 import static hat.types.vec2.div;
 52 import static hat.types.vec2.dot;
 53 import static hat.types.vec2.mul;
 54 import static hat.types.vec2.round;
 55 import static hat.types.vec2.sub;
 56 import static hat.types.vec2.vec2;
 57 import static hat.types.vec3.mix;
 58 import static hat.types.vec3.vec3;
 59 import static hat.types.vec4.normalize;
 60 import static hat.types.vec4.vec4;
 61 
 62 //https://www.shadertoy.com/view/4tXyWs
 63 public class MobiusShader{
 64 
 65 
 66     /*
 67      vec2 ortho(vec2 v)
 68             {
 69                 return vec2(v.y, -v.x);
 70             }
 71      */
 72     @Reflect
 73     public static  vec2 ortho(vec2 v) {
 74         return vec2(v.y(), -v.x());
 75     }
 76 
 77     /*
 78     void stroke(float dist, vec3 color, inout vec3 fragColor, float thickness, float aa)
 79             {
 80                 float alpha = smoothstep(0.5 * (thickness + aa), 0.5 * (thickness - aa), abs(dist));
 81                 fragColor = mix(fragColor, color, alpha);
 82             }
 83      */
 84     @Reflect public static  vec3 stroke(float dist, vec3 color, vec3 fragColor, float thickness, float aa) {
 85         float alpha = smoothstep(0.5f * (thickness + aa), 0.5f * (thickness - aa), abs(dist));
 86         return mix(fragColor, color, alpha);
 87     }
 88     /*
 89 
 90             void fill(float dist, vec3 color, inout vec3 fragColor, float aa)
 91             {
 92                 float alpha = smoothstep(0.5*aa, -0.5*aa, dist);
 93                 fragColor = mix(fragColor, color, alpha);
 94             }
 95 
 96      */
 97 
 98     @Reflect public static vec3 fill(float dist, vec3 color, vec3 fragColor, float aa) {
 99         float alpha = smoothstep(0.5f * aa, -0.5f * aa, dist);
100         return mix(fragColor, color, alpha);
101     }
102 
103     /*
104     void renderGrid(vec2 pos, out vec3 fragColor)
105             {
106                 vec3 background = vec3(1.0);
107                 vec3 axes = vec3(0.4);
108                 vec3 lines = vec3(0.7);
109                 vec3 sublines = vec3(0.95);
110                 float subdiv = 10.0;
111 
112                 float thickness = 0.003;
113                 float aa = length(fwidth(pos));
114 
115                 fragColor = background;
116 
117                 vec2 toSubGrid = pos - round(pos*subdiv)/subdiv;
118                 stroke(min(abs(toSubGrid.x), abs(toSubGrid.y)), sublines, fragColor, thickness, aa);
119 
120                 vec2 toGrid = pos - round(pos);
121                 stroke(min(abs(toGrid.x), abs(toGrid.y)), lines, fragColor, thickness, aa);
122 
123                 stroke(min(abs(pos.x), abs(pos.y)), axes, fragColor, thickness, aa);
124             }
125      */
126     @Reflect public static void renderGrid(vec2 pos, vec3 fragColor) {
127         vec3 background = vec3(1.0f);
128         vec3 axes = vec3(0.4f);
129         vec3 lines = vec3(0.7f);
130         vec3 sublines = vec3(0.95f);
131         float subdiv = 10.0f;
132 
133         float thickness = 0.003f;
134         float fwidthPos = 0.01f;
135         float aa = fwidthPos;//?length(fwidthPos);
136 
137         fragColor = background;
138 
139         vec2 toSubGrid = sub(pos, div(vec2.round(mul(pos, subdiv)), subdiv));
140         stroke(min(abs(toSubGrid.x()), abs(toSubGrid.y())), sublines, fragColor, thickness, aa);
141 
142         vec2 toGrid = sub(pos, round(pos));
143         stroke(min(abs(toGrid.x()), abs(toGrid.y())), lines, fragColor, thickness, aa);
144 
145         stroke(min(abs(pos.x()), abs(pos.y())), axes, fragColor, thickness, aa);
146     }
147 
148     /*
149     float sdistLine(vec2 a, vec2 b, vec2 pos)
150                 {
151                     return dot(pos - a, normalize(ortho(b - a)));
152                 }
153     */
154     @Reflect public static float sdistLine(vec2 a, vec2 b, vec2 pos) {
155         return dot(sub(pos, a), vec2.normalize(ortho(sub(b, a))));
156     }
157     /*
158             float sdistTri(vec2 a, vec2 b, vec2 c, vec2 pos)
159             {
160                 return max( sdistLine(a, b, pos),
161                         max(sdistLine(b, c, pos),
162                             sdistLine(c, a, pos)));
163             }
164  */
165 
166     @Reflect public static  float sdistTri(vec2 a, vec2 b, vec2 c, vec2 pos) {
167         return max(sdistLine(a, b, pos),
168                 max(sdistLine(b, c, pos),
169                         sdistLine(c, a, pos)));
170     }
171 
172     /*
173     float sdistQuadConvex(vec2 a, vec2 b, vec2 c, vec2 d, vec2 pos)
174             {
175                 return max(  sdistLine(a, b, pos),
176                         max( sdistLine(b, c, pos),
177                          max(sdistLine(c, d, pos),
178                              sdistLine(d, a, pos))));
179             }
180      */
181     @Reflect public static  float sdistQuadConvex(vec2 a, vec2 b, vec2 c, vec2 d, vec2 pos) {
182         return max(sdistLine(a, b, pos),
183                 max(sdistLine(b, c, pos),
184                         max(sdistLine(c, d, pos),
185                                 sdistLine(d, a, pos))));
186     }
187 
188     /*
189     void renderUnitSquare(vec2 pos, inout vec3 fragColor)
190             {
191             #if 0
192                 // Put a texture in there
193                 if (pos.x >= 0.0 && pos.y >= 0.0 && pos.x <= 1.0 && pos.y <= 1.0)
194                 {
195                     fragColor.rgb = texture(iChannel0, pos).rgb;
196                 }
197             #endif
198 
199                 float dist = sdistQuadConvex(vec2(0, 0),
200                                              vec2(1, 0),
201                                              vec2(1, 1),
202                                              vec2(0, 1), pos);
203                 stroke(dist, vec3(0, 0, 1), fragColor, 0.007, length(fwidth(pos)));
204             }
205      */
206     @Reflect public static  vec3 renderUnitSquare(vec2 pos, vec3 fragColor) {
207 
208         float dist = sdistQuadConvex(vec2(0, 0),
209                 vec2(1, 0),
210                 vec2(1, 1),
211                 vec2(0, 1), pos);
212         float fwidthPos = .0f;
213         return stroke(dist, vec3(0, 0, 1), fragColor, 0.007f, fwidthPos/*length(fwidth(pos)*/);
214     }
215 
216     /*
217     void renderAxes(vec2 origin, vec2 pos, inout vec3 fragColor)
218             {
219                 float len = 0.1;
220                 float thickness = 0.0075;
221                 float aa = length(fwidth(pos));
222 
223                 float xshaft = sdistQuadConvex(origin + vec2(0.5*thickness),
224                                                origin - vec2(0.5*thickness),
225                                                origin + vec2(len, -0.5*thickness),
226                                                origin + vec2(len, 0.5*thickness), pos);
227                 float xhead = sdistTri(origin + vec2(len, -2.0*thickness),
228                                        origin + vec2(len + 6.0*thickness, 0),
229                                        origin + vec2(len, 2.0*thickness), pos);
230 
231                 fill(min(xshaft, xhead), vec3(1, 0, 0), fragColor, aa);
232 
233                 float yshaft = sdistQuadConvex(origin - vec2(0.5*thickness),
234                                                origin + vec2(0.5*thickness),
235                                                origin + vec2(0.5*thickness, len),
236                                                origin + vec2(-0.5*thickness, len), pos);
237                 float yhead = sdistTri(origin + vec2(2.0*thickness, len),
238                                        origin + vec2(0, len + 6.0*thickness),
239                                        origin + vec2(-2.0*thickness, len), pos);
240 
241                 fill(min(yshaft, yhead), vec3(0, 0.75, 0), fragColor, aa);
242 
243             }
244      */
245     @Reflect public static   vec3 renderAxes(vec2 origin, vec2 pos, vec3 fragColor) {
246         float len = 0.1f;
247         float thickness = 0.0075f;
248         float fwidthPos = 0.01f;
249         float aa = fwidthPos;//length(fwidth(pos));
250 
251         float xshaft = sdistQuadConvex(add(origin, vec2(0.5f * thickness)),
252                 sub(origin, vec2(0.5f * thickness)),
253                 add(origin, vec2(len, -0.5f * thickness)),
254                 add(origin, vec2(len, 0.5f * thickness)), pos);
255 
256         float xhead = sdistTri(add(origin, vec2(len, -2.0f * thickness)),
257                 add(origin, vec2(len + 6.0f * thickness, 0f)),
258                 add(origin, vec2(len, 2.0f * thickness)), pos);
259 
260         fragColor = fill(min(xshaft, xhead), vec3(1f, 0f, 0f), fragColor, aa);
261 
262         float yshaft = sdistQuadConvex(add(origin, vec2(0.5f * thickness)),
263                 add(origin, vec2(0.5f * thickness)),
264                 add(origin, vec2(0.5f * thickness, len)),
265                 add(origin, vec2(-0.5f * thickness, len)), pos);
266 
267         float yhead = sdistTri(add(origin, vec2(2.0f * thickness, len)),
268                 add(origin, vec2(0, len + 6.0f * thickness)),
269                 add(origin, vec2(-2.0f * thickness, len)), pos);
270 
271         fragColor = fill(min(yshaft, yhead), vec3(0f, 0.75f, 0f), fragColor, aa);
272 
273         return fragColor;
274     }
275 
276     /*
277     vec2 cmul(vec2 a, vec2 b)
278             {
279                 return vec2(a.x*b.x - a.y*b.y, a.x*b.y + a.y*b.x);
280             }
281 */
282     @Reflect public static  vec2 cmul(vec2 a, vec2 b) {
283         return vec2(a.x() * b.x() - a.y() * b.y(), a.x() * b.y() + a.y() * b.x());
284     }
285     /*
286             vec2 cdiv(vec2 a, vec2 b)
287             {
288                 return cmul(a, vec2(b.x, -b.y)) / dot(b, b);
289             }
290      */
291 
292     @Reflect public static vec2 cdiv(vec2 a, vec2 b) {
293         return div(cmul(a, vec2(b.x(), -b.y())), dot(b, b));
294     }
295     @Reflect public static vec4 createPixel(vec2 fres, float ftime, vec2 fmouse,vec2 fragCoord){
296         vec4 fragColor = vec4(1f, 1f, 1f, 1f);
297         float aspect =fres.x() / fres.y();
298         vec2 pos = sub(mul(div(fragCoord,fres.y()), 1.5f), vec2((1.5f * aspect - 1.0f) / 2.0f, 0.25f));
299 
300         // apply a Möbius transformation to the plane
301         vec2 a = vec2(1f, sin(0.4f * ftime));
302         vec2 b = vec2(0f);
303         vec2 c = vec2(0.5f * cos(0.6f * ftime), 0.5f * sin(0.5f * ftime));
304         vec2 d = vec2(1f, cos(0.3f * ftime));
305         pos = sub(pos, vec2(0.5f));
306         pos = cdiv(add(cmul(a, pos), b), add(cmul(c, pos), d));
307         pos = add(pos, vec2(0.5f));
308 
309         // render the grid and stuff
310         fragColor = vec4(fragColor.x(), fragColor.y(), fragColor.z(), 1.0f);
311 
312         renderGrid(pos, vec3(fragColor.x(),fragColor.y(),fragColor.z()));
313         fragColor = vec4(renderUnitSquare(pos, vec3(fragColor.x(),fragColor.y(),fragColor.z())), 1f);
314         fragColor = vec4(renderAxes(vec2(0f), pos, vec3(fragColor.x(),fragColor.y(),fragColor.z())), 1f);
315         return normalize(fragColor);
316     }
317     @Reflect public static vec4 mainImage(Uniforms uniforms, vec4 fragColor, vec2 fragCoord) {
318         return createPixel(vec2.vec2(uniforms.iResolution().x(),uniforms.iResolution().y()),uniforms.iTime(),vec2.vec2(uniforms.iMouse().x(),uniforms.iMouse().y()),fragCoord);
319     }
320 
321 
322     @Reflect
323     public static void penumbra(@MappableIface.RO KernelContext kc, @MappableIface.RO Uniforms uniforms, @MappableIface.RW F32Array f32Array) {
324         int width = (int) uniforms.iResolution().x();
325         int height = (int) uniforms.iResolution().y();
326         var fragColor = mainImage(uniforms, vec4.vec4(0f), vec2.vec2((float)(kc.gix % width), (float)(height-(kc.gix / width))));
327         f32Array.array(kc.gix * 3, fragColor.x());
328         f32Array.array(kc.gix * 3+1, fragColor.y());
329         f32Array.array(kc.gix * 3+2, fragColor.z());
330     }
331 
332     @Reflect
333     static public void compute(final ComputeContext computeContext, @MappableIface.RO Uniforms uniforms, @MappableIface.RO F32Array image, int width, int height) {
334         computeContext.dispatchKernel(NDRange.of1D(width * height), (@Reflect Kernel) kc -> penumbra(kc, uniforms, image));
335     }
336 
337     public static void update(  Accelerator acc, Uniforms uniforms, F32Array f32Array, int width, int height) {
338         acc.compute((@Reflect Compute) cc -> compute(cc, uniforms, f32Array, width, height));
339     }
340 
341     static void main(String[] args) {
342         var acc = new Accelerator(MethodHandles.lookup(), Backend.FIRST);
343         var shader = ShaderViewer.of(acc, MobiusShader.class,1024, 1024);
344         shader.startLoop((uniforms, f32Array) -> update( acc, uniforms, f32Array, shader.view.getWidth(), shader.view.getHeight()));
345     }
346 }