diff --git a/img/uvgrid.png b/img/uvgrid.png
new file mode 100644
index 0000000..2401acd
Binary files /dev/null and b/img/uvgrid.png differ
diff --git a/js/fragshader.glsl b/js/fragshader.glsl
index 41bb128..e7d1594 100644
--- a/js/fragshader.glsl
+++ b/js/fragshader.glsl
@@ -18,7 +18,7 @@ vec2 invMobius(vec2 a, vec2 b, vec2 c, vec2 d, vec2 z){
}
-void mainImage( out vec4 fragColor, in vec2 fragCoord )
+vec4 mainImage( vec2 fragCoord )
{
vec2 uv = fragCoord.xy / iResolution.xy;
uv = uv- vec2(0.5,0.5);
@@ -30,3 +30,7 @@ void mainImage( out vec4 fragColor, in vec2 fragCoord )
vec2 transCoord = invMobius(a,b,c,d,uv);
fragColor = length(transCoord)>1.0?vec4(1,1,1,1):texture(iChannel0,transCoord);
}
+
+void main(){
+ gl_FragColor = mainImage(gl_FragCoord);
+}
\ No newline at end of file
diff --git a/js/mobius.js b/js/mobius.js
index d646ab2..009b59b 100644
--- a/js/mobius.js
+++ b/js/mobius.js
@@ -1,4 +1,12 @@
+var buffer;
+var gl;
+var canvas;
+var glCanvas;
+var program;
+var tex;
+
+
function add_2d_ctrl(parent,variable,label,min_x,max_x,min_y,max_y) {
var container = document.createElement('div'); //Create the slider container
@@ -72,8 +80,8 @@ function add_vector_field(){
var canvas = document.createElement('canvas');
canvas.style.border = "1px solid";
canvas.id = 'canvas'
- canvas.height = 600;
- canvas.width = 600
+ canvas.height = 500;
+ canvas.width = 500;
ctx = canvas.getContext('2d');
height = canvas.height;
@@ -116,6 +124,14 @@ function update_vector_field(a,b,c,d){
ctx.stroke();
}
}
+ fp = fixedpts(a,b,c,d);
+ ctx.beginPath();
+ ctx.arc(remap(fp[0].re,-2,2,0,600),remap(fp[0].im,-2,2,600,0),5,0,Math.PI*2);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.arc(remap(fp[1].re,-2,2,0,600),remap(fp[1].im,-2,2,600,0),5,0,Math.PI*2)
+ ctx.stroke();
}
function render(a,b,c,d){
@@ -124,8 +140,81 @@ function render(a,b,c,d){
c = $(document.getElementById('c')).data('complex');
d = $(document.getElementById('d')).data('complex');
update_vector_field(a,b,c,d)
+}
+
+
+function renderGL(texture) {
+
+
+ window.requestAnimationFrame(renderGL, canvas);
+
+ gl.clearColor(1.0, 0.0, 0.0, 1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ positionLocation = gl.getAttribLocation(program, "a_position");
+ gl.enableVertexAttribArray(positionLocation);
+ gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
+
+ gl.uniform2f(gl.getUniformLocation(program, "a"),a.re,a.im);
+ gl.uniform2f(gl.getUniformLocation(program, "b"),b.re,b.im);
+ gl.uniform2f(gl.getUniformLocation(program, "c"),c.re,c.im);
+ gl.uniform2f(gl.getUniformLocation(program, "d"),d.re,d.im);
+
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
}
+//Load Texture function from MDN
+function loadTexture(gl, url) {
+ const texture = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+
+ // Because images have to be download over the internet
+ // they might take a moment until they are ready.
+ // Until then put a single pixel in the texture so we can
+ // use it immediately. When the image has finished downloading
+ // we'll update the texture with the contents of the image.
+ const level = 0;
+ const internalFormat = gl.RGBA;
+ const width = 1;
+ const height = 1;
+ const border = 0;
+ const srcFormat = gl.RGBA;
+ const srcType = gl.UNSIGNED_BYTE;
+ const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue
+ gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
+ width, height, border, srcFormat, srcType,
+ pixel);
+
+ const image = new Image();
+ image.src = url;
+ image.onload = function() {
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
+ srcFormat, srcType, image);
+
+ // WebGL1 has different requirements for power of 2 images
+ // vs non power of 2 images so check if the image is a
+ // power of 2 in both dimensions.
+ if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
+ // Yes, it's a power of 2. Generate mips.
+ gl.generateMipmap(gl.TEXTURE_2D);
+ } else {
+ // No, it's not a power of 2. Turn of mips and set
+ // wrapping to clamp to edge
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+ }
+ };
+
+
+ return texture;
+}
+
+function isPowerOf2(value) {
+ return (value & (value - 1)) == 0;
+}
+
var Complex = function(_re,_im){
this.im = _im;
@@ -154,6 +243,25 @@ var mobius_inv = function(a,b,c,d,z){
return c.multiply(Complex(-1,0)).multiply(z).add(a).reciprocal().multiply(b.multiply(Complex(-1,0)).add(z.multiply(d)));
}
+function cpxSqrt(z) {
+ x = z.re;
+ y = z.im;
+ factor = Math.pow(x*x+y*y,0.25);
+ re = Math.cos(0.5*Math.atan2(y,x))*factor;
+ im = Math.sin(0.5*Math.atan2(y,x))*factor;
+ return new Complex(re,im);
+}
+
+var fixedpts = function(a,b,c,d){
+ fp = [1000,1000];
+ ad = a.add(new Complex(-1,0).multiply(d));
+ discr = ad.multiply(ad).add(new Complex(4,0).multiply(b).multiply(c));
+ den = new Complex(2,0).multiply(c).reciprocal();
+ fp[0] = ad.add(cpxSqrt(discr)).multiply(den);
+ fp[1] = ad.add(cpxSqrt(discr).multiply(new Complex(-1,0))).multiply(den);
+ return fp;
+}
+
remap = function(x,a,b,c,d){
//maps x from [a,b] to [c,d]
return (x - a) * (d - c) / (b - a) + c;
@@ -182,6 +290,7 @@ $(function () {//document is ready, setup everything
title.append(authors);
document.body.appendChild(title);
+
//Create GUI div
document.body.appendChild(title)
guibox = document.createElement('div');
@@ -195,7 +304,62 @@ $(function () {//document is ready, setup everything
add_2d_ctrl($(guibox),d,"d",-1,1,-1,1);
- canvas = add_vector_field()
- document.body.appendChild(canvas);
+ glCanvas = document.createElement("canvas");
+ glCanvas.id = 'glcanvas';
+ glCanvas.width = 500;
+ glCanvas.height = 500;
+ document.body.appendChild(glCanvas);
+
+ gl = glCanvas.getContext('experimental-webgl');
+
+ gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
+
+ buffer = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
+ gl.bufferData(
+ gl.ARRAY_BUFFER,
+ new Float32Array([
+ -1.0, -1.0,
+ 1.0, -1.0,
+ -1.0, 1.0,
+ -1.0, 1.0,
+ 1.0, -1.0,
+ 1.0, 1.0]),
+ gl.STATIC_DRAW
+ );
+
+ tex = loadTexture(gl,"img/uvgrid.png");
+
+ var shaderScript;
+ var shaderSource;
+ var vertexShader;
+ var fragmentShader;
+
+ shaderScript = document.getElementById("2d-vertex-shader");
+ shaderSource = shaderScript.text;
+ vertexShader = gl.createShader(gl.VERTEX_SHADER);
+ gl.shaderSource(vertexShader, shaderSource);
+ gl.compileShader(vertexShader);
+
+ shaderScript = document.getElementById("2d-fragment-shader");
+ shaderSource = shaderScript.text;
+ fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
+ gl.shaderSource(fragmentShader, shaderSource);
+ gl.compileShader(fragmentShader);
+
+ console.log(gl.getShaderInfoLog(fragmentShader));
+
+ program = gl.createProgram();
+ gl.attachShader(program, vertexShader);
+ gl.attachShader(program, fragmentShader);
+ gl.linkProgram(program);
+ gl.useProgram(program);
+
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+ gl.uniform1i(gl.getUniformLocation(program, 'sampler'), 0);
+
+ renderGL(program,tex);
});
+
diff --git a/mobius.html b/mobius.html
index 7817d37..6d68495 100644
--- a/mobius.html
+++ b/mobius.html
@@ -15,5 +15,76 @@
+
+
+
+