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 }