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 }