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 @@ + + + + \ No newline at end of file