/Demo/canvas.js
JavaScript | 452 lines | 342 code | 51 blank | 59 comment | 19 complexity | 50c748f72e7f63a3b7c8f6b6b82f0eac MD5 | raw file
Possible License(s): BSD-3-Clause
- /*
- * This file wraps several WebGL constructs and provides a simple, single texture based WebGLCanvas as well as a
- * specialized YUVWebGLCanvas that can handle YUV->RGB conversion.
- */
- /**
- * Represents a WebGL shader script.
- */
- var Script = (function script() {
- function constructor() {}
-
- constructor.createFromElementId = function(id) {
- var script = document.getElementById(id);
-
- // Didn't find an element with the specified ID, abort.
- assert(script , "Could not find shader with ID: " + id);
-
- // Walk through the source element's children, building the shader source string.
- var source = "";
- var currentChild = script .firstChild;
- while(currentChild) {
- if (currentChild.nodeType == 3) {
- source += currentChild.textContent;
- }
- currentChild = currentChild.nextSibling;
- }
-
- var res = new constructor();
- res.type = script.type;
- res.source = source;
- return res;
- };
-
- constructor.createFromSource = function(type, source) {
- var res = new constructor();
- res.type = type;
- res.source = source;
- return res;
- }
- return constructor;
- })();
- /**
- * Represents a WebGL shader object and provides a mechanism to load shaders from HTML
- * script tags.
- */
- var Shader = (function shader() {
- function constructor(gl, script) {
-
- // Now figure out what type of shader script we have, based on its MIME type.
- if (script.type == "x-shader/x-fragment") {
- this.shader = gl.createShader(gl.FRAGMENT_SHADER);
- } else if (script.type == "x-shader/x-vertex") {
- this.shader = gl.createShader(gl.VERTEX_SHADER);
- } else {
- error("Unknown shader type: " + script.type);
- return;
- }
-
- // Send the source to the shader object.
- gl.shaderSource(this.shader, script.source);
-
- // Compile the shader program.
- gl.compileShader(this.shader);
-
- // See if it compiled successfully.
- if (!gl.getShaderParameter(this.shader, gl.COMPILE_STATUS)) {
- error("An error occurred compiling the shaders: " + gl.getShaderInfoLog(this.shader));
- return;
- }
- }
- return constructor;
- })();
- var Program = (function () {
- function constructor(gl) {
- this.gl = gl;
- this.program = this.gl.createProgram();
- }
- constructor.prototype = {
- attach: function (shader) {
- this.gl.attachShader(this.program, shader.shader);
- },
- link: function () {
- this.gl.linkProgram(this.program);
- // If creating the shader program failed, alert.
- assert(this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS),
- "Unable to initialize the shader program.");
- },
- use: function () {
- this.gl.useProgram(this.program);
- },
- getAttributeLocation: function(name) {
- return this.gl.getAttribLocation(this.program, name);
- },
- setMatrixUniform: function(name, array) {
- var uniform = this.gl.getUniformLocation(this.program, name);
- this.gl.uniformMatrix4fv(uniform, false, array);
- }
- };
- return constructor;
- })();
- /**
- * Represents a WebGL texture object.
- */
- var Texture = (function texture() {
- function constructor(gl, size) {
- this.gl = gl;
- this.size = size;
- this.texture = gl.createTexture();
- gl.bindTexture(gl.TEXTURE_2D, this.texture);
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, size.w, size.h, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, null);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
- 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);
- }
- var textureIDs = null;
- constructor.prototype = {
- fill: function(textureData) {
- var gl = this.gl;
- assert(textureData.length >= this.size.w * this.size.h,
- "Texture size mismatch, data:" + textureData.length + ", texture: " + this.size.w * this.size.h);
- gl.bindTexture(gl.TEXTURE_2D, this.texture);
- gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, this.size.w , this.size.h, gl.LUMINANCE, gl.UNSIGNED_BYTE, textureData);
- },
- bind: function(n, program, name) {
- var gl = this.gl;
- if (!textureIDs) {
- textureIDs = [gl.TEXTURE0, gl.TEXTURE1, gl.TEXTURE2];
- }
- gl.activeTexture(textureIDs[n]);
- gl.bindTexture(gl.TEXTURE_2D, this.texture);
- gl.uniform1i(gl.getUniformLocation(program.program, name), n);
- }
- };
- return constructor;
- })();
- /**
- * Generic WebGL backed canvas that sets up: a quad to paint a texture on, appropriate vertex/fragment shaders,
- * scene parameters and other things. Specialized versions of this class can be created by overriding several
- * initialization methods.
- *
- * <code>
- * var canvas = new WebGLCanvas(document.getElementById('canvas'), new Size(512, 512);
- * canvas.texture.fill(data);
- * canvas.drawScene();
- * </code>
- */
- var WebGLCanvas = (function () {
-
- var vertexShaderScript = Script.createFromSource("x-shader/x-vertex", text([
- "attribute vec3 aVertexPosition;",
- "attribute vec2 aTextureCoord;",
- "uniform mat4 uMVMatrix;",
- "uniform mat4 uPMatrix;",
- "varying highp vec2 vTextureCoord;",
- "void main(void) {",
- " gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);",
- " vTextureCoord = aTextureCoord;",
- "}"
- ]));
-
- var fragmentShaderScript = Script.createFromSource("x-shader/x-fragment", text([
- "precision highp float;",
- "varying highp vec2 vTextureCoord;",
- "uniform sampler2D texture;",
- "void main(void) {",
- " gl_FragColor = texture2D(texture, vTextureCoord);",
- "}"
- ]));
-
- function constructor(canvas, size) {
- this.canvas = canvas;
- this.size = size;
- this.canvas.width = size.w;
- this.canvas.height = size.h;
-
- this.onInitWebGL();
- this.onInitShaders();
- initBuffers.call(this);
- this.onInitTextures();
- initScene.call(this);
- }
- /**
- * Initialize vertex and texture coordinate buffers for a plane.
- */
- function initBuffers() {
- var tmp;
- var gl = this.gl;
-
- // Create vertex position buffer.
- this.quadVPBuffer = gl.createBuffer();
- gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVPBuffer);
- tmp = [
- 1.0, 1.0, 0.0,
- -1.0, 1.0, 0.0,
- 1.0, -1.0, 0.0,
- -1.0, -1.0, 0.0];
-
- gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(tmp), gl.STATIC_DRAW);
- this.quadVPBuffer.itemSize = 3;
- this.quadVPBuffer.numItems = 4;
-
- /*
- +--------------------+
- | -1,1 (1) | 1,1 (0)
- | |
- | |
- | |
- | |
- | |
- | -1,-1 (3) | 1,-1 (2)
- +--------------------+
- */
-
- var scaleX = 1.0;
- var scaleY = 1.0;
-
- // Create vertex texture coordinate buffer.
- this.quadVTCBuffer = gl.createBuffer();
- gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVTCBuffer);
- tmp = [
- scaleX, 0.0,
- 0.0, 0.0,
- scaleX, scaleY,
- 0.0, scaleY,
- ];
- gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(tmp), gl.STATIC_DRAW);
- }
-
- function mvIdentity() {
- this.mvMatrix = Matrix.I(4);
- }
- function mvMultiply(m) {
- this.mvMatrix = this.mvMatrix.x(m);
- }
- function mvTranslate(m) {
- mvMultiply.call(this, Matrix.Translation($V([m[0], m[1], m[2]])).ensure4x4());
- }
-
- function setMatrixUniforms() {
- this.program.setMatrixUniform("uPMatrix", new Float32Array(this.perspectiveMatrix.flatten()));
- this.program.setMatrixUniform("uMVMatrix", new Float32Array(this.mvMatrix.flatten()));
- }
- function initScene() {
- var gl = this.gl;
-
- // Establish the perspective with which we want to view the
- // scene. Our field of view is 45 degrees, with a width/height
- // ratio of 640:480, and we only want to see objects between 0.1 units
- // and 100 units away from the camera.
-
- this.perspectiveMatrix = makePerspective(45, 1, 0.1, 100.0);
-
- // Set the drawing position to the "identity" point, which is
- // the center of the scene.
- mvIdentity.call(this);
-
- // Now move the drawing position a bit to where we want to start
- // drawing the square.
- mvTranslate.call(this, [0.0, 0.0, -2.4]);
- // Draw the cube by binding the array buffer to the cube's vertices
- // array, setting attributes, and pushing it to GL.
- gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVPBuffer);
- gl.vertexAttribPointer(this.vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
-
- // Set the texture coordinates attribute for the vertices.
-
- gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVTCBuffer);
- gl.vertexAttribPointer(this.textureCoordAttribute, 2, gl.FLOAT, false, 0, 0);
-
- this.onInitSceneTextures();
-
- setMatrixUniforms.call(this);
- }
-
- constructor.prototype = {
- toString: function() {
- return "WebGLCanvas Size: " + this.size;
- },
- checkLastError: function (operation) {
- var err = this.gl.getError();
- if (err != this.gl.NO_ERROR) {
- var name = this.glNames[err];
- name = (name !== undefined) ? name + "(" + err + ")":
- ("Unknown WebGL ENUM (0x" + value.toString(16) + ")");
- if (operation) {
- console.log("WebGL Error: %s, %s", operation, name);
- } else {
- console.log("WebGL Error: %s", name);
- }
- console.trace();
- }
- },
- onInitWebGL: function () {
- try {
- this.gl = this.canvas.getContext("experimental-webgl");
- } catch(e) {}
-
- if (!this.gl) {
- error("Unable to initialize WebGL. Your browser may not support it.");
- }
- if (this.glNames) {
- return;
- }
- this.glNames = {};
- for (var propertyName in this.gl) {
- if (typeof this.gl[propertyName] == 'number') {
- this.glNames[this.gl[propertyName]] = propertyName;
- }
- }
- },
- onInitShaders: function() {
- this.program = new Program(this.gl);
- this.program.attach(new Shader(this.gl, vertexShaderScript));
- this.program.attach(new Shader(this.gl, fragmentShaderScript));
- this.program.link();
- this.program.use();
- this.vertexPositionAttribute = this.program.getAttributeLocation("aVertexPosition");
- this.gl.enableVertexAttribArray(this.vertexPositionAttribute);
- this.textureCoordAttribute = this.program.getAttributeLocation("aTextureCoord");;
- this.gl.enableVertexAttribArray(this.textureCoordAttribute);
- },
- onInitTextures: function () {
- this.texture = new Texture(this.gl, this.size);
- },
- onInitSceneTextures: function () {
- this.texture.bind(0, this.program, "texture");
- },
- drawScene: function() {
- this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
- }
- };
- return constructor;
- })();
- var YUVWebGLCanvas = (function () {
- var vertexShaderScript = Script.createFromSource("x-shader/x-vertex", text([
- "attribute vec3 aVertexPosition;",
- "attribute vec2 aTextureCoord;",
- "uniform mat4 uMVMatrix;",
- "uniform mat4 uPMatrix;",
- "varying highp vec2 vTextureCoord;",
- "void main(void) {",
- " gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);",
- " vTextureCoord = aTextureCoord;",
- "}"
- ]));
-
- var fragmentShaderScriptOld = Script.createFromSource("x-shader/x-fragment", text([
- "precision highp float;",
- "varying highp vec2 vTextureCoord;",
- "uniform sampler2D YTexture;",
- "uniform sampler2D UTexture;",
- "uniform sampler2D VTexture;",
-
- "void main(void) {",
- " vec3 YUV = vec3",
- " (",
- " texture2D(YTexture, vTextureCoord).x * 1.1643828125, // premultiply Y",
- " texture2D(UTexture, vTextureCoord).x,",
- " texture2D(VTexture, vTextureCoord).x",
- " );",
- " gl_FragColor = vec4",
- " (",
- " YUV.x + 1.59602734375 * YUV.z - 0.87078515625,",
- " YUV.x - 0.39176171875 * YUV.y - 0.81296875 * YUV.z + 0.52959375,",
- " YUV.x + 2.017234375 * YUV.y - 1.081390625,",
- " 1",
- " );",
- "}"
- ]));
-
- var fragmentShaderScriptSimple = Script.createFromSource("x-shader/x-fragment", text([
- "precision highp float;",
- "varying highp vec2 vTextureCoord;",
- "uniform sampler2D YTexture;",
- "uniform sampler2D UTexture;",
- "uniform sampler2D VTexture;",
-
- "void main(void) {",
- " gl_FragColor = texture2D(YTexture, vTextureCoord);",
- "}"
- ]));
- var fragmentShaderScript = Script.createFromSource("x-shader/x-fragment", text([
- "precision highp float;",
- "varying highp vec2 vTextureCoord;",
- "uniform sampler2D YTexture;",
- "uniform sampler2D UTexture;",
- "uniform sampler2D VTexture;",
- "const mat4 YUV2RGB = mat4",
- "(",
- " 1.1643828125, 0, 1.59602734375, -.87078515625,",
- " 1.1643828125, -.39176171875, -.81296875, .52959375,",
- " 1.1643828125, 2.017234375, 0, -1.081390625,",
- " 0, 0, 0, 1",
- ");",
-
- "void main(void) {",
- " gl_FragColor = vec4( texture2D(YTexture, vTextureCoord).x, texture2D(UTexture, vTextureCoord).x, texture2D(VTexture, vTextureCoord).x, 1) * YUV2RGB;",
- "}"
- ]));
-
-
- function constructor(canvas, size) {
- WebGLCanvas.call(this, canvas, size);
- }
-
- constructor.prototype = inherit(WebGLCanvas, {
- onInitShaders: function() {
- this.program = new Program(this.gl);
- this.program.attach(new Shader(this.gl, vertexShaderScript));
- this.program.attach(new Shader(this.gl, fragmentShaderScript));
- this.program.link();
- this.program.use();
- this.vertexPositionAttribute = this.program.getAttributeLocation("aVertexPosition");
- this.gl.enableVertexAttribArray(this.vertexPositionAttribute);
- this.textureCoordAttribute = this.program.getAttributeLocation("aTextureCoord");;
- this.gl.enableVertexAttribArray(this.textureCoordAttribute);
- },
- onInitTextures: function () {
- console.log("creatingTextures: size: " + this.size);
- this.YTexture = new Texture(this.gl, this.size);
- this.UTexture = new Texture(this.gl, this.size.getHalfSize());
- this.VTexture = new Texture(this.gl, this.size.getHalfSize());
- },
- onInitSceneTextures: function () {
- this.YTexture.bind(0, this.program, "YTexture");
- this.UTexture.bind(1, this.program, "UTexture");
- this.VTexture.bind(2, this.program, "VTexture");
- },
- fillYUVTextures: function(y, u, v) {
- this.YTexture.fill(y);
- this.UTexture.fill(u);
- this.VTexture.fill(v);
- },
- toString: function() {
- return "YUVCanvas Size: " + this.size;
- }
- });
-
- return constructor;
- })();