Last active
August 28, 2023 20:31
-
-
Save mathdoodle/ff65b6e53f980bdbc14a to your computer and use it in GitHub Desktop.
Warping with WebGL
This file contains hidden or 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
{ | |
"uuid": "f27b5235-e517-4fc9-8467-1270bbac7bbf", | |
"description": "Warping with WebGL", | |
"dependencies": {}, | |
"operatorOverloading": false | |
} |
This file contains hidden or 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
<!doctype html> | |
<html> | |
<head> | |
<!-- Copyright � Microsoft Corporation. All Rights Reserved. --> | |
<!-- Demo Author: Frank Olivier, Microsoft Corporation --> | |
<!-- Updates by Jay Munro --> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> | |
<title>Photo warping with WebGL</title> | |
<style> | |
/* STYLE-MARKER */ | |
</style> | |
<!-- SCRIPTS-MARKER --> | |
</head> | |
<body> | |
<h1 id="DemoTitle"> | |
Photo warping with WebGL | |
</h1> | |
<h2>click and drag</h2> | |
<div id="DemoContent"> | |
<div style='width:600px; height:600px'> | |
<div style='position: relative'> | |
<!-- WebGL canvas --> | |
<canvas style='position: absolute' id='webglcanvas' width='600' height='600'></canvas> | |
<!-- notation cavas --> | |
<canvas style='position: absolute' id='2dcanvas' width='600' height='600'></canvas> | |
</div> | |
</div> | |
<div style="text-align:center"> | |
<button id="openphoto1" onclick="OpenPhoto1()">Open a photo</button> | |
<span id="openphoto2" style="display:none">Pick a photo: <input type="file" id="files" /></span> <br /> | |
<button onclick="undo()">Undo</button> | |
<button onclick="reset()">Start over</button> | |
<button onclick="save()">Save</button> | |
</div> | |
</div> | |
<div id="ErrorMessage" style="display:none"> | |
<div class="heading">Sorry!</div> | |
This demonstation requires a browser with WebGL support. | |
</div> | |
<div id="log"></div> | |
<div id="Details"> | |
<div class="heading">WebGL 101</div> | |
To render the warped photo above, a mesh of 400 triangle coordinates, a photo, a vertex & | |
fragment shader program and uniform points are uploaded to the GPU using WebGL.<br /> | |
<br /> | |
<!-- The show uniform points checkbox --> | |
<label><input type="checkbox" name="showUniforms" id="showUniforms" onchange="renderer.changeMode();renderer.render()" />Show uniform points</label> | |
<div> | |
<label><input type="radio" name="rendertype" id="renderLines" onclick="renderer.render()" />Show triangle mesh</label> | |
<label><input type="radio" name="rendertype" id="renderTriangles" onclick="renderer.render()" checked />Show rendered photo</label> | |
</div> | |
<br /> | |
When you click and drag on the photo, new uniform points are set on the GPU...<br /><br /> | |
<div class="heading">Vertex shader</div> | |
<img src="vertex.png"/><br /> | |
...The GPU runs the vertex shader below to distort the mesh using the uniform points...<br /><br /> | |
<pre id="vertexshadersource"></pre> | |
<br /> | |
<div class="heading">Fragment shader</div> | |
<img src="fragment.png"/><br /> | |
...and the fragment shader paints photo pixels using the distorted mesh.<br /><br /> | |
<pre id="fragmentshadersource"></pre> | |
<br /> | |
<!-- For more information on WebGL, see <a href="http://docs.webplatform.org/wiki/webgl">webplatform.org</a>.</div> --> | |
</div> | |
<script> | |
// CODE-MARKER | |
</script> | |
<script id="2d-vertex-shader" type="x-shader/x-vertex"> | |
// outgoing coordinate | |
varying vec2 v_texCoord; | |
// incoming coordinate (point) | |
attribute vec2 a_texCoord; | |
// maximum number of changes to grid | |
#define MAXPOINTS 10 | |
uniform vec2 p1[MAXPOINTS]; // Where the drag started | |
uniform vec2 p2[MAXPOINTS]; // Where the drag ended | |
void main() { | |
v_texCoord = a_texCoord; | |
// Set up position variable with current coordinate normalized from 0 - 1 to -1 to 1 range | |
vec2 position = a_texCoord * 2.0 - 1.0; | |
for (int i = 0; i < MAXPOINTS; i++) // loop through | |
{ | |
float dragdistance = distance(p1[i], p2[i]); // Calculate the distance between two start and end of mouse drag for each of the drags | |
float mydistance = distance(p1[i], position); // Calculate the distance between the start of the mouse drag and the last position | |
if (mydistance < dragdistance) | |
{ | |
vec2 maxdistort = (p2[i] - p1[i]) / 4.0; // only affect vertices within 4 x the drag distance ( | |
float normalizeddistance = mydistance / dragdistance; | |
float normalizedimpact = (cos(normalizeddistance*3.14159265359)+1.0)/2.0; | |
position += (maxdistort * normalizedimpact); | |
} | |
} | |
// gl_Position always specifies where to render this vector | |
gl_Position = vec4(position, 0.0, 1.0); // x,y,z, | |
} | |
</script> | |
<!-- fragment shader --> | |
<script id="2d-fragment-shader" type="x-shader/x-fragment"> | |
precision mediump float; | |
// uniform to use for texture | |
uniform sampler2D u_image; | |
// Output of the vertex shader | |
varying vec2 v_texCoord; | |
void main() { | |
// Specify the color to make the current pixel. | |
gl_FragColor = texture2D(u_image, v_texCoord); | |
} | |
</script> | |
<!-- fragment shader --> | |
<script id="red" type="x-shader/x-fragment"> | |
precision mediump float; | |
varying vec2 v_texCoord; | |
// Set a solid color for the grid | |
void main() { | |
gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0); | |
} | |
</script> | |
</body> | |
</html> |
This file contains hidden or 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
'use strict'; | |
window.onload = main; // Startup | |
/** | |
* Point - converts incoming values to a -1 to 1 range. | |
*/ | |
class Point { | |
public x: number; | |
public y: number; | |
constructor(x: number, y: number) { | |
if (x<-1) x = -1; | |
if (y<-1) y = -1; | |
if (x>1) x = 1; | |
if (y>1) y = 1; | |
this.x = x; | |
this.y = y; | |
} | |
} | |
/** | |
* A new mouse manipulation. | |
*/ | |
class Move { | |
public point1: Point; | |
public point2: Point; | |
constructor(point: Point) { | |
this.point1 = new Point(point.x, point.y); | |
this.point2 = new Point(point.x, point.y); | |
} | |
move(point: Point) { | |
this.point2.x = point.x; | |
this.point2.y = point.y; | |
} | |
} | |
var renderer = new function () { | |
var gl: WebGLRenderingContext; // Handle to the context. | |
var lineprogram: WebGLProgram; // Handle to GLSL program that draws lines. | |
var pictureprogram: WebGLProgram; // Handle to GLSL program that draws a picture. | |
this.texCoordLocation; // Location of the texture for the picture fragment shader. | |
this.texCoordLocation2; // Location of the texture for the line fragment shader. | |
this.texCoordBuffer; // The buffer for the texture for the picture fragment shader. | |
this.texCoordBuffer2; // The buffer for the texture for the line fragment shader. | |
var moves = new Array<Move>(); | |
var MAXMOVES = 10; | |
var currentMove = 0; | |
var resolution = 20; // Resolution of the mesh. | |
// First init called by main(). | |
// Initialize the gl context variable. | |
this.init = function () { | |
// Get a context from our canvas object with id = "webglcanvas". | |
var canvas = <HTMLCanvasElement>document.getElementById("webglcanvas"); | |
try { | |
// Get the context into a local gl and and a public gl. | |
// Use preserveDrawingBuffer:true to keep the drawing buffer after presentation | |
var gl: WebGLRenderingContext = this.gl = canvas.getContext("experimental-webgl", { preserveDrawingBuffer: true }); | |
} | |
catch (e) { | |
// Fail quietly | |
} | |
// If we can't get a WebGL context (no WebGL support), then display an error message. | |
if (!this.gl) { | |
document.getElementById("ErrorMessage").style.display = "block"; | |
return; | |
} | |
try { | |
// Load the GLSL source written in the HTML file. | |
// Create a program with the two shaders | |
this.lineprogram = loadProgram(gl, getShader(gl, "2d-vertex-shader"), getShader(gl, "red")); | |
// Tell webGL to use this program | |
gl.useProgram(this.lineprogram); | |
// Look up where the vertex data needs to go. | |
this.texCoordLocation2 = gl.getAttribLocation(this.lineprogram, "a_texCoord"); | |
// Provide texture coordinates for the rectangle. | |
this.texCoordBuffer2 = gl.createBuffer(); | |
gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer2); | |
// Create a buffer and set it use the array set up above. | |
// Set it to be modified once, use many. | |
// createRedGrid sets up the vector array itself. | |
gl.bufferData(gl.ARRAY_BUFFER, createRedGrid(), gl.STATIC_DRAW); // Fill buffer data | |
// Turns on the vertex attributes in the GPU program. | |
gl.enableVertexAttribArray(this.texCoordLocation2); | |
// Set up the data format for the vertex array - set to points (x/y). | |
// Use floats. | |
gl.vertexAttribPointer(this.texCoordLocation2, 2, gl.FLOAT, false, 0, 0); | |
} | |
catch (e) { | |
// Display the fail on the screen if the shaders/program fail. | |
log('shader fail'); | |
return; | |
} | |
try { | |
var vertexshader = getShader(gl, "2d-vertex-shader"); | |
var fragmentshader = getShader(gl, "2d-fragment-shader"); | |
this.pictureprogram = loadProgram(gl, vertexshader, fragmentshader); | |
gl.useProgram(this.pictureprogram); | |
// Put the shader source into the <divs>. | |
document.getElementById("vertexshadersource").innerText = gl.getShaderSource(vertexshader); | |
document.getElementById("fragmentshadersource").innerText = gl.getShaderSource(fragmentshader); | |
// Look up where the vertex data needs to go. | |
this.texCoordLocation = gl.getAttribLocation(this.pictureprogram, "a_texCoord"); | |
// Provide texture coordinates for the rectangle. | |
this.texCoordBuffer = gl.createBuffer(); | |
gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer); | |
// createImageGrid sets up the vector array itself | |
gl.bufferData(gl.ARRAY_BUFFER, createImageGrid(), gl.STATIC_DRAW); // Fill buffer data | |
gl.vertexAttribPointer(this.texCoordLocation, 2, gl.FLOAT, false, 0, 0); | |
gl.enableVertexAttribArray(this.texCoordLocation); | |
// Set up uniforms variables (image). | |
this.pictureprogram.u_image = gl.getUniformLocation(this.pictureprogram, "u_image"); | |
// Set the texture to use. | |
gl.uniform1i(this.pictureprogram.u_image, 0); | |
} | |
catch (e) { | |
log('shader fail'); | |
return; | |
} | |
this.loadImage(); | |
} | |
// Load a default image. | |
this.loadImage = function () { | |
var image = new Image(); | |
image.onload = function () { | |
renderer.loadImage2(image); | |
} | |
image.src = "photos/demophoto.jpg"; // Default image | |
} | |
// load a the user's image. | |
this.loadImageX = function (dataURL) { | |
var image = new Image(); | |
image.onload = function () { | |
renderer.loadImage2(image); | |
} | |
image.src = dataURL; | |
} | |
// This function does the heavy lifting of creating the texture from the image. | |
this.loadImage2 = function (image) { | |
// Convert the image to a square image via the temporary 2d canvas. | |
var canvas = <HTMLCanvasElement>document.getElementById("2dcanvas"); | |
var ctx = canvas.getContext("2d"); | |
var canvHeight = (<HTMLCanvasElement>document.getElementById("2dcanvas")).height; | |
var x = 0; | |
var y = 0; | |
var xx = canvHeight; | |
var yy = canvHeight; | |
ctx.clearRect(0, 0, canvHeight, canvHeight); | |
// If image isn't square, adjust width, height, and origin so it's centered. | |
if (image.width < image.height) { | |
// Change origin and dimensions if the image isn't square. | |
// Change x, xx | |
xx = image.width / image.height * canvHeight; | |
x = (canvHeight - xx) / 2; | |
} | |
if (image.width > image.height) { | |
// Change y, yy | |
yy = image.height / image.width * canvHeight; | |
y = (canvHeight - yy) / 2; | |
} | |
// Put the image on the canvas, scaled using xx & yy. | |
ctx.drawImage(image, 0, 0, image.width, image.height, x, y, xx, yy); | |
var gl: WebGLRenderingContext = this.gl; | |
// Create a texture object that will contain the image. | |
var texture = gl.createTexture(); | |
// Bind the texture the target (TEXTURE_2D) of the active texture unit. | |
gl.bindTexture(gl.TEXTURE_2D, texture); | |
// Flip the image's Y axis to match the WebGL texture coordinate space. | |
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, <any>true); | |
// Set the parameters so we can render any size image. | |
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); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); | |
// Upload the resized canvas image into the texture. | |
// Note: a canvas is used here but can be replaced by an image object. | |
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas); | |
ctx.clearRect(0, 0, canvHeight, canvHeight); | |
this.reset(); | |
} | |
// This code checks the Show uniform points checkbox and changes the mode and initializes the canvas. | |
this.modeOff = 0; | |
this.modeHint = 1; | |
this.modeHint2 = 2; | |
this.modeUniform = 3; | |
// Modes tell the app to show uniforms or not | |
this.canvasMode = this.modeHint; | |
this.changeMode = function () { | |
if ((<HTMLInputElement>document.getElementById("showUniforms")).checked) { | |
this.canvasMode = this.modeUniform; | |
} | |
else { | |
this.canvasMode = this.modeOff; | |
var canvas = <HTMLCanvasElement>document.getElementById("2dcanvas"); | |
var ctx = canvas.getContext("2d"); | |
ctx.clearRect(0, 0, canvas.width, canvas.height); | |
} | |
} | |
this.render = function () { | |
var gl: WebGLRenderingContext = this.gl; | |
// Create two arrays to hold start and end point uniforms | |
var p1 = new Float32Array(MAXMOVES * 2); //x and y | |
var p2 = new Float32Array(MAXMOVES * 2); //x and y | |
// Set up the arrays of points | |
{ | |
var index = 0; | |
for (var i = 0; i < MAXMOVES; i++) { | |
// Working values | |
var x1: number, y1:number, x2: number, y2: number; | |
if (moves[i]) { | |
x1 = moves[i].point1.x; | |
y1 = moves[i].point1.y; | |
x2 = moves[i].point2.x; | |
y2 = moves[i].point2.y; | |
} | |
else { | |
x1 = 1; | |
y1 = 1; | |
x2 = 0.9999999; | |
y2 = 0.9999999; | |
} | |
p1[index] = x1; | |
p1[index + 1] = y1; | |
p2[index] = x2; | |
p2[index + 1] = y2; | |
index += 2; | |
} | |
} | |
// Clear color buffer and set it to light gray | |
gl.clearColor(1.0, 1.0, 1.0, 0.5); | |
gl.clear(this.gl.COLOR_BUFFER_BIT); | |
// This draws either the grid or the photo for stretching | |
if ((<HTMLInputElement>document.getElementById("renderLines")).checked) | |
{ | |
gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer2); | |
gl.useProgram(this.lineprogram); | |
gl.uniform2fv(gl.getUniformLocation(this.lineprogram, "p1"), p1); | |
gl.uniform2fv(gl.getUniformLocation(this.lineprogram, "p2"), p2); | |
gl.vertexAttribPointer(this.texCoordLocation2, 2, gl.FLOAT, false, 0, 0); | |
gl.enableVertexAttribArray(this.texCoordLocation2); | |
gl.drawArrays(gl.LINES, 0, resolution * resolution * 10); | |
} | |
else | |
{ | |
gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer); | |
gl.useProgram(this.pictureprogram); | |
gl.uniform2fv(gl.getUniformLocation(this.pictureprogram, "p1"), p1); | |
gl.uniform2fv(gl.getUniformLocation(this.pictureprogram, "p2"), p2); | |
gl.vertexAttribPointer(this.texCoordLocation, 2, gl.FLOAT, false, 0, 0); | |
gl.enableVertexAttribArray(this.texCoordLocation); | |
gl.drawArrays(gl.TRIANGLES, 0, resolution * resolution * 2 * 3); | |
} | |
// Draw uniform points | |
if (this.canvasMode == this.modeUniform) { | |
var canvas = <HTMLCanvasElement>document.getElementById("2dcanvas"); | |
var ctx = canvas.getContext("2d"); | |
ctx.clearRect(0, 0, canvas.width, canvas.height); | |
for (var i = 0; i < MAXMOVES; i++) { | |
if (moves[i]) { | |
var x1 = (moves[i].point1.x + 1) * canvas.width / 2; | |
var y1 = (-moves[i].point1.y + 1) * canvas.height / 2; | |
var x2 = (moves[i].point2.x + 1) * canvas.width / 2; | |
var y2 = (-moves[i].point2.y + 1) * canvas.height / 2; | |
// The raio is used here to show where the pixel started and ended | |
var ratio = 0.3; | |
x2 = x1 + (x2 - x1) * ratio; | |
y2 = y1 + (y2 - y1) * ratio; | |
var radius = 6; | |
ctx.beginPath(); // Start a fresh path | |
// Create a 2D gradient | |
var grd = ctx.createLinearGradient(x1, y1, x2, y2); | |
grd.addColorStop(0, 'pink'); // Set one side to pink | |
grd.addColorStop(1, 'red'); // The other side to red | |
ctx.setLineDash([5, 5]); // Use a dotted line | |
ctx.lineWidth = radius / 2; | |
ctx.moveTo(x1, y1); // Create a line from start to end poing | |
ctx.lineTo(x2, y2); | |
ctx.strokeStyle = grd; | |
ctx.stroke(); | |
ctx.beginPath(); // Start a new path for pink dot | |
ctx.arc(x1, y1, radius, 0, 2 * Math.PI, false); // full circle (2*pi) | |
ctx.fillStyle = 'pink'; | |
ctx.fill(); | |
ctx.beginPath(); // Start a new path for red dot | |
ctx.arc(x2, y2, radius, 0, 2 * Math.PI, false); | |
ctx.fillStyle = 'red'; | |
ctx.fill(); | |
} | |
} | |
} | |
} | |
// (point) is where the mouse was clicked | |
this.newMove = function(point) // Where the warp starts (-1 to 1 range) | |
{ | |
var move = new Move(point); | |
// Adds move to beginning of moves array (pushes onto array) | |
moves.unshift(move); | |
return move; | |
} | |
this.reset = function () { | |
moves = []; | |
this.render(); | |
} | |
this.undo = function () { | |
// Removes the first element in moves array (pops off array) | |
moves.shift(); | |
this.render(); | |
} | |
this.save = function () { | |
// First create a dataURL string from the canvas in jpeg format. | |
var dataURL = (<HTMLCanvasElement>document.getElementById("webglcanvas")).toDataURL("image/png"); | |
// Split the dataURL and decode it from ASCII to base-64 binary. | |
var binArray = atob(dataURL.split(',')[1]); | |
// Create an 8-bit unsigned array | |
var array = []; | |
// Add the unicode numeric value of each element to the new array. | |
for (var i = 0; i < binArray.length; i++) { | |
array.push(binArray.charCodeAt(i)); | |
} | |
var blobObject = new Blob([new Uint8Array(array)], { type: 'image/png' }); | |
if (window.navigator.msSaveBlob) { | |
window.navigator.msSaveBlob(blobObject, 'warpedphoto.png'); | |
}else if (window.navigator['saveBlob']) { | |
window.navigator['saveBlob'](blobObject, 'warpedphoto.png'); | |
} | |
else { | |
dataURL = dataURL.replace("image/png", "image/octet-stream"); | |
window.location.href = dataURL; | |
// alert("Sorry, your browser does not support navigator.saveBlob"); | |
} | |
} | |
// Grid making section | |
function createRedGrid() { | |
// Make a 0,0 to 1,1 triangle mesh, using n = resolution steps. | |
var q = 0.001; // A fudge factor to ensure that the wireframe lines are rendered inside the canvas boundary. | |
var r = (1 - q * 2) / resolution; | |
//2 numbers per coord; three coords per triangle; 2 triagles per square; resolution * resolution squares. | |
var c = new Float32Array(resolution * resolution * 20); | |
// Array index. | |
var i = 0; | |
// Build the mesh top to bottom, left to right. | |
for (var xs = 0; xs < resolution; xs++) { | |
for (var ys = 0; ys < resolution; ys++) { | |
var x = r * xs + q; | |
var y = r * ys + q; | |
// Top of square - first triangle. | |
c[i++] = x; | |
c[i++] = y; | |
c[i++] = x + r; | |
c[i++] = y; | |
// Center line - hypotonose of triangles. | |
c[i++] = x; | |
c[i++] = y + r; | |
c[i++] = x + r; | |
c[i++] = y; | |
// Bottom line of 2nd triangle. | |
c[i++] = x; | |
c[i++] = y + r; | |
c[i++] = x + r; | |
c[i++] = y + r; | |
// First triangle, left side. | |
c[i++] = x; | |
c[i++] = y; | |
c[i++] = x; | |
c[i++] = y + r; | |
// Right side of 2nd triangle. | |
c[i++] = x + r; | |
c[i++] = y; | |
c[i++] = x + r; | |
c[i++] = y + r; | |
} | |
} | |
return c; | |
} | |
function createImageGrid() { | |
var q = 0.001; | |
var r = (1 - q * 2) / resolution; | |
var c = new Float32Array(resolution * resolution * 12); //2 numbers per coord; three coords per triangle; 2 triagles per square; resolution * resolution squares. | |
var i = 0; | |
for (var xs = 0; xs < resolution; xs++) { | |
for (var ys = 0; ys < resolution; ys++) { | |
var x = r * xs + q; | |
var y = r * ys + q; | |
c[i++] = x; | |
c[i++] = y; | |
c[i++] = x + r; | |
c[i++] = y; | |
c[i++] = x; | |
c[i++] = y + r; | |
c[i++] = x + r; | |
c[i++] = y; | |
c[i++] = x; | |
c[i++] = y + r; | |
c[i++] = x + r; | |
c[i++] = y + r; | |
} | |
} | |
return c; | |
} | |
} | |
// getMousePoint | |
// input - mouse event e | |
function getMousePoint(e: MouseEvent) { | |
var x; | |
var y; | |
// The standard way to get mouse coordinates | |
if (e.offsetX) { | |
x = e.offsetX; | |
y = e.offsetY; | |
} | |
// LayerX and layerY are provided for cross-browser compatibility | |
else if (e.layerX) { | |
x = e.layerX; | |
y = e.layerY; | |
} | |
else { | |
return undefined; //Work around Chrome | |
} | |
return normalizedPoint(x, y); // Converts pixels to -1 to 1 | |
} | |
var inputHandler = new function() { | |
var move; // Pointer to a uniform variable in the renderer object | |
this.init = function () { | |
var canvas = document.getElementById("2dcanvas"); | |
// Set up mouse events on the canvas object. | |
canvas.onmousedown = function (e) { | |
console.log("onmousedown"); | |
this.move = renderer.newMove(getMousePoint(e)); | |
} | |
canvas.onmouseup = function (e) { | |
console.log("onmouseup"); | |
this.move = undefined; | |
renderer.render(); | |
} | |
canvas.onmouseout = function (e) { | |
console.log("onmouseout"); | |
this.move = undefined; | |
renderer.render(); | |
}; | |
canvas.onmousemove = function (e) { | |
console.log("onmousemove"); | |
var point = getMousePoint(e); | |
if (typeof this.move != 'undefined') | |
{ | |
if (typeof point != 'undefined') | |
{ | |
this.move.move(point); | |
} | |
renderer.render(); | |
} | |
}; | |
canvas.ondragstart = function (e) { //Workaround for Chrome | |
console.log("ondragstart"); | |
e.preventDefault(); | |
}; | |
} | |
} | |
/** | |
* Program starts here. | |
*/ | |
function main() { | |
renderer.init(); // Initialize WebGL shapes and image | |
inputHandler.init(); // Initialize mouse and UI handler | |
} | |
function undo() { | |
renderer.undo(); | |
} | |
function reset() { | |
renderer.reset(); | |
} | |
function save() { | |
renderer.save(); | |
} | |
function normalizedPoint(x: number, y: number): Point | |
{ | |
// converts screen coordinates to -1 to 1 | |
var canvas = <HTMLCanvasElement>document.getElementById("2dcanvas"); | |
x = (x / canvas.width) * 2 - 1; | |
y = (1 - (y / canvas.height)) * 2 - 1; | |
return new Point(x, y); | |
} | |
// Adds a string to the log in the web page | |
function log(result: string) { | |
var resultDiv = document.getElementById("log"); | |
resultDiv.innerHTML += result + "<br />"; | |
} | |
// Adds a string to the log in the web page; overwrites everything in the log with the new string | |
function logi(result: string) { | |
var resultDiv = document.getElementById('log'); | |
resultDiv.innerHTML = result; | |
} | |
// Loads a shader from a script tag | |
// Parameters: | |
// WebGL context | |
// id of script element containing the shader to load | |
function getShader(gl: WebGLRenderingContext, id: string): WebGLShader { | |
var shaderScript = <HTMLScriptElement>document.getElementById(id); | |
// error - element with supplied id couldn't be retrieved | |
if (!shaderScript) { | |
return null; | |
} | |
// If successful, build a string representing the shader source | |
var str = ""; | |
var k = shaderScript.firstChild; | |
while (k) { | |
if (k.nodeType == 3) { | |
str += k.textContent; | |
} | |
k = k.nextSibling; | |
} | |
var shader: WebGLShader; | |
// Create shaders based on the type we set | |
// note: these types are commonly used, but not required | |
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); | |
// Check the compile status, return an error if failed | |
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { | |
console.log(gl.getShaderInfoLog(shader)); | |
return null; | |
} | |
return shader; | |
} | |
function loadProgram(gl: WebGLRenderingContext, vertexShader: WebGLShader, fragmentShader: WebGLShader): WebGLProgram | |
{ | |
// create a progam object | |
var program = gl.createProgram(); | |
// attach the two shaders | |
gl.attachShader(program, vertexShader); | |
gl.attachShader(program, fragmentShader); | |
// link everything | |
gl.linkProgram(program); | |
// Check the link status | |
var linked = gl.getProgramParameter(program, gl.LINK_STATUS); | |
if (!linked) { | |
// An error occurred while linking | |
var lastError = gl.getProgramInfoLog(program); | |
console.warn("Error in program linking:" + lastError); | |
gl.deleteProgram(program); | |
return null; | |
} | |
// if all is well, return the program object | |
return program; | |
}; | |
function handleFileSelect(evt) { | |
document.getElementById("openphoto1").style.display = "inline"; | |
document.getElementById("openphoto2").style.display = "none"; | |
var files = evt.target.files; // FileList object | |
// files is a FileList of File objects. List some properties. | |
var file = files[0]; | |
var reader = new FileReader(); | |
// Closure to capture the file information. | |
reader.onload = function (e) { | |
renderer.loadImageX(this.result); | |
}; | |
// Read in the image file as a data URL. | |
reader.readAsDataURL(file); | |
} | |
document.getElementById('files').addEventListener('change', handleFileSelect, false); | |
function OpenPhoto1() { | |
document.getElementById("openphoto1").style.display = "none"; | |
document.getElementById("openphoto2").style.display = "inline"; | |
} |
This file contains hidden or 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
/* Prevent text selection from being displayed when the user drags out of the canvas */ | |
*::selection { | |
background:transparent; | |
} | |
#DemoContent { | |
margin-top:10px; | |
} | |
#DemoContent { | |
width:600px; | |
margin-left:auto; | |
margin-right:auto; | |
margin-top:10px; | |
} | |
#Details { | |
margin-top:10px; | |
margin-bottom:10px; | |
padding:20px; | |
border-radius:4px; | |
} | |
body { | |
-ms-user-select: none; /* turn off user selection */ | |
font-size: 12pt; | |
} | |
canvas { | |
-ms-touch-action: none; /* turn off panning and zooming */ | |
} | |
.heading | |
{ | |
font-size: 20pt; | |
font-weight: normal; | |
} | |
#ErrorMessage { | |
position:absolute; | |
top:100px; | |
left:20%; | |
right:20%; | |
background-color:lightcoral; | |
padding:20px; | |
border-radius:4px; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment