documentation for pd/gem/lua version
[fractaloids] / webgl / fractaloids.html
1 <!DOCTYPE html>
2 <html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8">
3 <title>fractaloids</title>
4 <!--
5
6 fractaloids.html -- trippiness in your browser
7 Copyright (C) 2012 Claude Heiland-Allen
8
9 This program is free software: you can redistribute it and/or modify
10 it under the terms of the GNU Affero General Public License as
11 published by the Free Software Foundation, either version 3 of the
12 License, or (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU Affero General Public License for more details.
18
19 You should have received a copy of the GNU Affero General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
21
22 -->
23 <style type="text/css">* { border: 0; padding: 0; margin: 0; } #framerate { text-align: right; }</style>
24 <script type="text/javascript" src="webgl-utils.js"></script>
25 <script type="text/javascript" src="webgl-debug.js"></script>
26 <script type="text/javascript" src="J3DI.js"></script>
27 <script type="text/javascript" src="J3DIMath.js"></script>
28 <script id="vert" type="x-shader/x-vertex">//<![CDATA[
29
30 uniform mat4 vMatrix;
31 attribute vec2 vPosition;
32 varying vec2 v_texCoord;
33 void main() {
34 gl_Position = vec4(vPosition, 0.0, 1.0);
35 v_texCoord = vec2(vMatrix * vec4(vPosition, 0.0, 1.0));
36 }
37
38 //]]></script>
39 <script id="frag" type="x-shader/x-fragment">//<![CDATA[
40 precision mediump float;
41
42 uniform vec2 roots[8];
43 uniform float epsSquared;
44 uniform int maxIters;
45 uniform int activeRoots;
46
47 varying vec2 v_texCoord;
48
49 float cmag2(vec2 z) { return dot(z,z); }
50 vec2 crecip(vec2 z) { float d = cmag2(z); return z / vec2(d, -d); }
51
52 vec2 newtonStep(vec2 z) {
53 vec2 s = vec2(0.0);
54 for (int i = 0; i < 8; ++i) {
55 if (i < activeRoots) {
56 s += crecip(z - roots[i]);
57 }
58 }
59 return z - crecip(s);
60 }
61
62 vec2 newtonDelta(vec2 z00) {
63 vec2 z0 = z00;
64 bool done = false;
65 int count = -1;
66 int root = -1;
67 float d0 = 1000.0;
68 float d1 = 1000.0;
69 for (int n = 0; n < 64; ++n) {
70 d1 = d0;
71 z0 = newtonStep(z0);
72 d0 = 1000.0;
73 for (int i = 0; i < 8; ++i) {
74 if (i < activeRoots) {
75 float d = cmag2(z0 - roots[i]);
76 if (d < d0) { d0 = d; root = i; }
77 }
78 }
79 if (d0 < epsSquared) {
80 done = true;
81 count = n;
82 break;
83 }
84 }
85 float delta = -1.0;
86 if (done) {
87 delta = float(count);
88 if (d0 > 0.0) {
89 delta += clamp(log(epsSquared / d1) / log(d0 / d1), 0.0, 1.0);
90 }
91 }
92 return vec2(float(root),delta);
93 }
94
95 vec3 yuv2rgb(vec3 yuv) {
96 return yuv * mat3(1.0, 1.407, 0.0, 1.0, -0.677, -0.236, 1.0, 0.0, 1.848);
97 }
98
99 vec3 splat(vec3 rgb) {
100 float m = max(max(max(rgb.x, rgb.y), rgb.z), 1.0);
101 return clamp(rgb / m, 0.0, 1.0);
102 }
103
104 void main(void) {
105 vec2 z = v_texCoord.xy;
106 vec2 d = newtonDelta(z);
107 vec3 c = vec3(0.0);
108 if (d.x >= 0.0 && d.y >= 0.0) {
109 float y = clamp(1.0 / (1.0 + d.y), 0.0, 1.0);
110 float r = 0.7 * y;
111 float t = d.x * 3.883222077450933;
112 c = splat(yuv2rgb(vec3(y, r * cos(t), r * sin(t))));
113 }
114 gl_FragColor = vec4(c, 1.0);
115 }
116
117 //]]></script><script id="main" type="text/javascript">//<![CDATA[
118
119 // global state
120 var g = { };
121
122 function now() { return new Date().getTime(); }
123
124 function shader(gl) {
125 var vert = loadShader(gl, "vert");
126 var frag = loadShader(gl, "frag");
127 var prog = gl.createProgram();
128 gl.attachShader(prog, vert);
129 gl.attachShader(prog, frag);
130 gl.bindAttribLocation(prog, 0, "vPosition");
131 gl.linkProgram(prog);
132 var linked = gl.getProgramParameter(prog, gl.LINK_STATUS);
133 if (!linked && !gl.isContextLost()) {
134 gl.deleteProgram(prog);
135 gl.deleteProgram(frag);
136 gl.deleteProgram(vert);
137 return null;
138 }
139 gl.useProgram(prog);
140 return prog;
141 }
142
143 function init() {
144 var gl = initWebGL("fractaloids");
145 WebGLDebugUtils.makeDebugContext(gl);
146 if (gl) {
147 g.maxRoots = 8;
148 g.program = shader(gl);
149 gl.uniform1f(gl.getUniformLocation(g.program, "epsSquared"), 1.0);
150 gl.uniform1i(gl.getUniformLocation(g.program, "maxIters"), 64);
151 gl.uniform1i(gl.getUniformLocation(g.program, "activeRoots"), 0);
152 g.points = new Float32Array([1,1, -1,1, -1,-1, 1,-1]);
153 g.obj = gl.createBuffer()
154 gl.enableVertexAttribArray(0);
155 gl.bindBuffer(gl.ARRAY_BUFFER, g.obj);
156 gl.bufferData(gl.ARRAY_BUFFER, g.points, gl.STATIC_DRAW);
157 gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
158 g.vMatrix = new J3DIMatrix4();
159 return gl
160 }
161 return null;
162 }
163
164 function reshape(gl) {
165 var canvas = document.getElementById("fractaloids");
166 var winW = window.innerWidth;
167 var winH = window.innerHeight;
168 if (winW == g.width && winH == g.height) {
169 return;
170 }
171 g.width = winW;
172 g.height = winH - 60;
173 canvas.width = g.width;
174 canvas.height = g.height;
175 gl.viewport(0,0, g.width, g.height);
176 g.vMatrix.makeIdentity();
177 g.vMatrix.ortho(-1.0/g.width, 1.0/g.width, -1.0/g.height, 1.0/g.height, -1, 1);
178 g.vMatrix.setUniform(gl, gl.getUniformLocation(g.program, "vMatrix"), false);
179 }
180
181 function render(gl) {
182 reshape(gl);
183 var t = now();
184 var dt = t - g.frameTime;
185 g.frameTime = t;
186 rs = advance(dt);
187 gl.uniform1i(gl.getUniformLocation(g.program, "activeRoots"), Math.min(rs.length / 2, 8));
188 if (rs.length > 0) {
189 gl.uniform2fv(gl.getUniformLocation(g.program, "roots"), rs);
190 }
191 gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
192 g.framerate.snapshot();
193 }
194
195 function advance(dt) {
196 var rs = [];
197 for (var i = 0; i < g.loops.length; ++i) {
198 var l = g.loops[i];
199 if (l) {
200 l.offset = l.offset + dt;
201 while (l.offset > l.events[l.current].dt) {
202 l.offset -= l.events[l.current].dt;
203 l.current = (l.current + 1) % l.events.length;
204 }
205 var e0 = l.events[l.current];
206 var e1 = l.events[(l.current + 1) % l.events.length];
207 var t = l.offset / e0.dt;
208 var x = e0.x * (1 - t) + t * e1.x;
209 var y = e0.y * (1 - t) + t * e1.y;
210 rs[rs.length] = x;
211 rs[rs.length] = y;
212 }
213 }
214 return rs;
215 }
216
217 function handleContextLost(e) {
218 e.preventDefault();
219 if (g.requestId !== undefined) {
220 window.cancelAnimFrame(g.requestId);
221 g.requestId = undefined;
222 }
223 }
224
225 function handleContextRestored() {
226 init();
227 g.f();
228 }
229
230 function handleMouseDown(event) {
231 event.preventDefault();
232 g.mouseDown = true;
233 g.mouseTime = now();
234 g.mouseLoop = [];
235 }
236
237 function handleMouseUp(event) {
238 event.preventDefault();
239 g.mouseDown = false;
240 if (g.mouseLoop.length > 1) {
241 g.loops[g.nextloop] = { offset: 0, current: 0, events: g.mouseLoop };
242 g.nextloop = (g.nextloop + 1) % g.maxloops;
243 }
244 g.mouseLoop = [];
245 }
246
247 function handleMouseMove(event) {
248 event.preventDefault();
249 if (! g.mouseDown) { return; }
250 var t = now();
251 var dt = t - g.mouseTime;
252 g.mouseTime = t;
253 var x = g.width - 2 * event.clientX;
254 var y = g.height - 2 * event.clientY;
255 var e = { x: x, y: y, dt: dt };
256 g.mouseLoop[g.mouseLoop.length] = e;
257 }
258
259 function start() {
260 var canvas = document.getElementById("fractaloids");
261 var gl = init();
262 if (!gl) { return; }
263 g.mouseDown = false;
264 g.frameTime = now();
265 g.mouseloop = [];
266 g.loops = [];
267 g.maxloops = 8;
268 g.nextloop = 0;
269 canvas.addEventListener("webglcontextlost", handleContextLost, false);
270 canvas.addEventListener("webglcontextrestored", handleContextRestored, false);
271 canvas.addEventListener("mousedown", handleMouseDown, true);
272 canvas.addEventListener("mouseup", handleMouseUp, true);
273 canvas.addEventListener("mousemove", handleMouseMove, true);
274 g.framerate = new Framerate("framerate");
275 g.f = function() {
276 render(gl);
277 g.requestId = window.requestAnimFrame(g.f, gl);
278 };
279 g.f();
280 }
281
282 //]]></script></head><body onload="start()">
283 <div>fractaloids © 2012 Claude Heiland-Allen</div>
284 <canvas id="fractaloids"></canvas><div id="framerate"></div>
285 </body></html>