Created
May 13, 2018 09:31
-
-
Save Flexi23/0fa1d2c405af308c0b254a304c246a2b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<title>Lemniscate Panorama Configurator</title> | |
<script type="text/javascript" src="dat.gui.min.js"></script> | |
<script id="shader-vs" type="x-shader/x-vertex"> | |
attribute vec3 aPos; | |
attribute vec2 aTexCoord; | |
varying vec2 uv; | |
void main(void) { | |
gl_Position = vec4(aPos, 1.); | |
uv = aTexCoord; | |
} | |
</script> | |
<script id="shader-fs-pano" type="x-shader/x-fragment"> | |
#ifdef GL_ES | |
precision mediump float; | |
#endif | |
varying vec2 uv; | |
uniform sampler2D sampler_pano; | |
uniform vec2 aspect; | |
uniform float mirrorSize; | |
uniform vec2 rotation; | |
uniform float zoom; | |
uniform float offsetX; | |
uniform vec2 flip; | |
uniform float reverse; | |
vec2 factorA, factorB, product; | |
#define pi 3.141592653589793238462643383279 | |
#define pi_inv 0.318309886183790671537767526745 | |
#define pi2_inv 0.159154943091895335768883763372 | |
float atan2(float y, float x){ | |
if(x>0.) return atan(y/x); | |
if(y>=0. && x<0.) return atan(y/x) + pi; | |
if(y<0. && x<0.) return atan(y/x) - pi; | |
if(y>0. && x==0.) return pi/2.; | |
if(y<0. && x==0.) return -pi/2.; | |
if(y==0. && x==0.) return pi/2.; // undefined usually | |
return pi/2.; | |
} | |
vec2 applyMirror(vec2 uv){ | |
uv.y = 1.- uv.y; // flipud | |
uv.y = mix( uv.y / (1. - mirrorSize), (1.-uv.y) / mirrorSize, float(uv.y > 1.- mirrorSize)); | |
uv.y = 1.- uv.y; // flipud | |
return uv; | |
} | |
void main(void) { | |
vec2 uv = (uv-0.5)*aspect * zoom; | |
uv = 0.5 + vec2( uv.x*rotation.x - uv.y*rotation.y, uv.x*rotation.y + uv.y*rotation.x); | |
// polar coordinates for the left-hand side | |
vec2 c1 = vec2(0.25 - mirrorSize*0.25, 0.5); | |
float a1 = atan2(uv.y - c1.y, uv.x - c1.x) + offsetX; // angle | |
float d1 = distance(uv, c1) / 0.5; // dist | |
float m1 = float(d1 < 1.); // mask | |
vec2 uv1 = applyMirror(vec2(a1*pi2_inv,d1)); | |
float mm = float(d1 < mirrorSize && flip.y == -1.); // mirror mask | |
uv1 = mix(uv1, 1.-applyMirror(1.-uv1), mm); | |
// polar coordinates for the right-hand side | |
vec2 c2 = vec2(0.75 + mirrorSize*0.25, 0.5); | |
float a2 = -atan2(uv.y - c2.y, uv.x - c2.x) + offsetX + pi; // angle | |
float d2 = distance(uv, c2) / 0.5; // dist | |
float m2 = float(d2 < 1.); // mask | |
vec2 uv2 = applyMirror(vec2(a2*pi2_inv,d2)); uv2.y = 1.-uv2.y; | |
mm = float(d2 < mirrorSize && flip.y == 1.); // mirror mask | |
uv2 = mix(uv2, applyMirror(uv2), mm); | |
vec4 leftBall = texture2D(sampler_pano, uv1) * m1; | |
vec4 rightBall = texture2D(sampler_pano, uv2) * m2; | |
float mh = float(0.5 + (uv.y-0.5) * reverse < 0.5); // mask horizontal half | |
vec2 mixUv = mix(uv1, uv2, m2); | |
mixUv = mix(mixUv, uv1, mh * m1); | |
mixUv = 0.5 + (mixUv-0.5)*flip; | |
float mixMask = max(m1, m2); | |
vec4 mixBall = texture2D(sampler_pano, mixUv) * mixMask; | |
gl_FragColor = mixBall; | |
gl_FragColor.a = 1.; | |
} | |
</script> | |
<script type="text/javascript"> | |
function getShader(gl, id) { | |
var shaderScript = document.getElementById(id); | |
var str = ""; | |
var k = shaderScript.firstChild; | |
while (k) { | |
if (k.nodeType == 3) | |
str += k.textContent; | |
k = k.nextSibling; | |
} | |
var shader; | |
if (shaderScript.type == "x-shader/x-fragment") { | |
shader = gl.createShader(gl.FRAGMENT_SHADER); | |
} else if (shaderScript.type == "x-shader/x-vertex") | |
shader = gl.createShader(gl.VERTEX_SHADER); | |
else | |
return null; | |
gl.shaderSource(shader, str); | |
gl.compileShader(shader); | |
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS) == 0) | |
alert("error compiling shader '" + id + "'\n\n" + gl.getShaderInfoLog(shader)); | |
return shader; | |
} | |
window.requestAnimFrame = (function () { | |
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame | |
|| window.msRequestAnimationFrame || function (callback) { | |
window.setTimeout(callback, 10); // don't really need 100fps anyway | |
}; | |
})(); | |
var $ = function (d) { | |
return document.getElementById(d); | |
}; | |
var gl; | |
var ext; | |
var prog_pano; | |
var texture_pano; | |
var texture_pano2; | |
var texture_pano3; | |
var sizeX = 1024; // must be powers of 2 | |
var sizeY = 512; | |
var frame = 0; // frame counter to be resetted every 1000ms | |
var framecount = 0; // not resetted | |
var fps, fpsDisplayUpdateTimer; | |
var time, starttime = new Date().getTime(); | |
var pointerX = 0.5; | |
var pointerY = 0.5; | |
// geometry | |
var squareBuffer; | |
function load() { | |
clearInterval(fpsDisplayUpdateTimer); | |
var c = document.getElementById("c"); | |
try { | |
gl = c.getContext("experimental-webgl", { | |
depth: false | |
}); | |
} catch (e) { | |
} | |
if (!gl) { | |
alert("Meh! Y u no support experimental WebGL !?!"); | |
return; | |
} | |
["OES_texture_float", "OES_standard_derivatives", "OES_texture_float_linear", "OES_texture_float_linear"].forEach(function (name) { | |
console.log("check " + name); | |
try { | |
ext = gl.getExtension(name); | |
} catch (e) { | |
alert(e); | |
} | |
if (!ext) { | |
alert("Meh! Y u no support " + name + " !?!\n(Chrome 29 or Firefox 24 will do fine)"); | |
return; | |
} | |
ext = false; | |
}); | |
document.onmousemove = function (evt) { | |
pointerX = evt.pageX / sizeX; | |
pointerY = 1 - evt.pageY / sizeY; | |
}; | |
window.onresize = function (a) { | |
c.style.width = innerWidth + 'px'; | |
c.style.height = innerHeight + 'px'; | |
}; | |
sizeX = window.innerWidth; | |
sizeY = window.innerHeight; | |
c.width = sizeX; | |
c.height = sizeY; | |
prog_pano = createAndLinkProgram("shader-fs-pano"); | |
triangleStripGeometry = { | |
vertices: new Float32Array([-1, -1, 0, 1, -1, 0, -1, 1, 0, 1, 1, 0]), | |
texCoords: new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]), | |
vertexSize: 3, | |
vertexCount: 4, | |
type: gl.TRIANGLE_STRIP | |
}; | |
createTexturedGeometryBuffer(triangleStripGeometry); | |
squareBuffer = gl.createBuffer(); | |
gl.bindBuffer(gl.ARRAY_BUFFER, squareBuffer); | |
var aPosLoc = gl.getAttribLocation(prog_pano, "aPos"); | |
var aTexLoc = gl.getAttribLocation(prog_pano, "aTexCoord"); | |
gl.enableVertexAttribArray(aPosLoc); | |
gl.enableVertexAttribArray(aTexLoc); | |
var verticesAndTexCoords = new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1, // one square of a quad! | |
0, 0, 1, 0, 0, 1, 1, 1] // hello texture, you be full | |
); | |
gl.bufferData(gl.ARRAY_BUFFER, verticesAndTexCoords, gl.STATIC_DRAW); | |
gl.vertexAttribPointer(aPosLoc, 2, gl.FLOAT, gl.FALSE, 8, 0); | |
gl.vertexAttribPointer(aTexLoc, 2, gl.FLOAT, gl.FALSE, 8, 32); | |
FBO_pano = gl.createFramebuffer(); | |
texture_pano = createAndBindImageTexture($("pano"), FBO_pano); | |
FBO_pano2 = gl.createFramebuffer(); | |
//texture_pano2 = createAndBindImageTexture($("pano2"), FBO_pano2); | |
FBO_pano3 = gl.createFramebuffer(); | |
//texture_pano3 = createAndBindImageTexture($("pano3"), FBO_pano3); | |
gl.activeTexture(gl.TEXTURE1); | |
gl.bindTexture(gl.TEXTURE_2D, texture_pano); | |
gl.activeTexture(gl.TEXTURE2); | |
gl.bindTexture(gl.TEXTURE_2D, texture_pano2); | |
gl.activeTexture(gl.TEXTURE3); | |
gl.bindTexture(gl.TEXTURE_2D, texture_pano3); | |
time = new Date().getTime() - starttime; | |
gl.blendFunc(gl.SRC_ALPHA, gl.ONE); | |
gl.clearColor(0, 0, 0, 1); | |
var gui = new dat.GUI(); | |
gui.add(configuration, 'zoom', 0.4, 2.5); | |
gui.add(configuration, 'mirrorSize', 0.001, 1); | |
gui.add(configuration, 'rotation', -180, 180); | |
gui.add(configuration, 'offsetX', -180, 180); | |
gui.add(configuration, 'flipX'); | |
gui.add(configuration, 'flipY'); | |
gui.add(configuration, 'reverse'); | |
anim(); | |
} | |
function createTexturedGeometryBuffer(geometry) { | |
geometry.buffer = gl.createBuffer(); | |
gl.bindBuffer(gl.ARRAY_BUFFER, geometry.buffer); | |
geometry.aPosLoc = gl.getAttribLocation(prog_pano, "aPos"); | |
gl.enableVertexAttribArray(geometry.aPosLoc); | |
geometry.aTexLoc = gl.getAttribLocation(prog_pano, "aTexCoord"); | |
gl.enableVertexAttribArray(geometry.aTexLoc); | |
geometry.texCoordOffset = geometry.vertices.byteLength; | |
gl.bufferData(gl.ARRAY_BUFFER, geometry.texCoordOffset + geometry.texCoords.byteLength, gl.STATIC_DRAW); | |
gl.bufferSubData(gl.ARRAY_BUFFER, 0, geometry.vertices); | |
gl.bufferSubData(gl.ARRAY_BUFFER, geometry.texCoordOffset, geometry.texCoords); | |
setGeometryVertexAttribPointers(geometry); | |
} | |
function setGeometryVertexAttribPointers(geometry) { | |
gl.vertexAttribPointer(geometry.aPosLoc, geometry.vertexSize, gl.FLOAT, gl.FALSE, 0, 0); | |
gl.vertexAttribPointer(geometry.aTexLoc, 2, gl.FLOAT, gl.FALSE, 0, geometry.texCoordOffset); | |
} | |
function createAndLinkProgram(fsId) { | |
var program = gl.createProgram(); | |
gl.attachShader(program, getShader(gl, "shader-vs")); | |
gl.attachShader(program, getShader(gl, fsId)); | |
gl.linkProgram(program); | |
return program; | |
} | |
function createAndBindImageTexture(img, fbo) { | |
var texture = gl.createTexture(); | |
gl.bindTexture(gl.TEXTURE_2D, texture); | |
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); | |
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img); | |
//gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2048, 1024, 0, gl.RGBA, gl.UNSIGNED_BYTE, img); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); | |
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); | |
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); | |
return texture; | |
} | |
function setUniforms(program) { | |
gl.uniform2f(gl.getUniformLocation(program, "aspect"), Math.max(1, innerWidth / innerHeight), Math.max(1, innerHeight / innerWidth)); | |
gl.uniform1i(gl.getUniformLocation(program, "sampler_pano"), 1); | |
gl.uniform1f(gl.getUniformLocation(program, "mirrorSize"), configuration.mirrorSize); | |
gl.uniform1f(gl.getUniformLocation(program, "offsetX"), configuration.offsetX / 180 * Math.PI); | |
gl.uniform2f(gl.getUniformLocation(program, "rotation"), Math.cos(configuration.rotation / 180 * Math.PI), Math.sin(configuration.rotation / 180 * Math.PI)); | |
gl.uniform1f(gl.getUniformLocation(program, "zoom"), 1 / configuration.zoom); | |
gl.uniform2f(gl.getUniformLocation(program, "flip"), configuration.flipX ? -1 : 1, configuration.flipY ? -1 : 1); | |
gl.uniform1f(gl.getUniformLocation(program, "reverse"), configuration.reverse ? -1 : 1); | |
} | |
function useGeometry(geometry) { | |
gl.bindBuffer(gl.ARRAY_BUFFER, geometry.buffer); | |
setGeometryVertexAttribPointers(geometry); | |
} | |
function renderGeometry(geometry, targetFBO) { | |
useGeometry(geometry); | |
gl.bindFramebuffer(gl.FRAMEBUFFER, targetFBO); | |
gl.drawArrays(geometry.type, 0, geometry.vertexCount); | |
gl.flush(); | |
} | |
function renderAsTriangleStrip(targetFBO) { | |
renderGeometry(triangleStripGeometry, targetFBO); | |
} | |
function render() { | |
gl.viewport(0, 0, sizeX, sizeY); | |
gl.useProgram(prog_pano); | |
setUniforms(prog_pano); | |
renderAsTriangleStrip(null); | |
} | |
function anim() { | |
requestAnimationFrame(anim); | |
render(); | |
} | |
var Configuration = function () { | |
this.zoom = 1.; | |
this.mirrorSize = 0.001; | |
this.rotation = 0; | |
this.offsetX = 110; | |
this.flipX = true; | |
this.flipY = false; | |
this.reverse = true; | |
}; | |
var configuration = new Configuration(); | |
</script> | |
<style type="text/css"> | |
body { | |
background-color: #000000; | |
color: #FFFFFF; | |
overflow: hidden; | |
} | |
#imgs { | |
position: absolute; | |
top: 2048; | |
left: 0; | |
z-index: -1; | |
} | |
#c { | |
position: absolute; | |
top: 0; | |
left: 0; | |
z-index: -1; | |
} | |
a { | |
color: #FFFFFF; | |
font-weight: bold; | |
} | |
#desc { | |
background-color: rgba(0, 0, 0, 0.2); | |
width: 4096; | |
} | |
</style> | |
</head> | |
<body onload="load()" ondblclick="hide()"> | |
<canvas id="c" width="4096" height="4096"></canvas> | |
<br> | |
<div id="imgs"> | |
<img id="pano" src="your_equirectangular_image_here.jpg"><br> | |
</div> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment