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