PageRenderTime 50ms CodeModel.GetById 10ms app.highlight 33ms RepoModel.GetById 1ms app.codeStats 0ms

/Demo/canvas.js

http://github.com/mbebenita/Broadway
JavaScript | 452 lines | 342 code | 51 blank | 59 comment | 19 complexity | 50c748f72e7f63a3b7c8f6b6b82f0eac MD5 | raw file
  1/*
  2 * This file wraps several WebGL constructs and provides a simple, single texture based WebGLCanvas as well as a
  3 * specialized YUVWebGLCanvas that can handle YUV->RGB conversion. 
  4 */
  5
  6/**
  7 * Represents a WebGL shader script.
  8 */
  9var Script = (function script() {
 10  function constructor() {}
 11  
 12  constructor.createFromElementId = function(id) {
 13    var script = document.getElementById(id);
 14    
 15    // Didn't find an element with the specified ID, abort.
 16    assert(script , "Could not find shader with ID: " + id);
 17    
 18    // Walk through the source element's children, building the shader source string.
 19    var source = "";
 20    var currentChild = script .firstChild;
 21    while(currentChild) {
 22      if (currentChild.nodeType == 3) {
 23        source += currentChild.textContent;
 24      }
 25      currentChild = currentChild.nextSibling;
 26    }
 27    
 28    var res = new constructor();
 29    res.type = script.type;
 30    res.source = source;
 31    return res;
 32  };
 33  
 34  constructor.createFromSource = function(type, source) {
 35    var res = new constructor();
 36    res.type = type;
 37    res.source = source;
 38    return res;
 39  }
 40  return constructor;
 41})();
 42
 43/**
 44 * Represents a WebGL shader object and provides a mechanism to load shaders from HTML
 45 * script tags.
 46 */
 47var Shader = (function shader() {
 48  function constructor(gl, script) {
 49    
 50    // Now figure out what type of shader script we have, based on its MIME type.
 51    if (script.type == "x-shader/x-fragment") {
 52      this.shader = gl.createShader(gl.FRAGMENT_SHADER);
 53    } else if (script.type == "x-shader/x-vertex") {
 54      this.shader = gl.createShader(gl.VERTEX_SHADER);
 55    } else {
 56      error("Unknown shader type: " + script.type);
 57      return;
 58    }
 59    
 60    // Send the source to the shader object.
 61    gl.shaderSource(this.shader, script.source);
 62    
 63    // Compile the shader program.
 64    gl.compileShader(this.shader);
 65    
 66    // See if it compiled successfully.
 67    if (!gl.getShaderParameter(this.shader, gl.COMPILE_STATUS)) {
 68      error("An error occurred compiling the shaders: " + gl.getShaderInfoLog(this.shader));
 69      return;
 70    }
 71  }
 72  return constructor;
 73})();
 74
 75var Program = (function () {
 76  function constructor(gl) {
 77    this.gl = gl;
 78    this.program = this.gl.createProgram();
 79  }
 80  constructor.prototype = {
 81    attach: function (shader) {
 82      this.gl.attachShader(this.program, shader.shader);
 83    }, 
 84    link: function () {
 85      this.gl.linkProgram(this.program);
 86      // If creating the shader program failed, alert.
 87      assert(this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS),
 88             "Unable to initialize the shader program.");
 89    },
 90    use: function () {
 91      this.gl.useProgram(this.program);
 92    },
 93    getAttributeLocation: function(name) {
 94      return this.gl.getAttribLocation(this.program, name);
 95    },
 96    setMatrixUniform: function(name, array) {
 97      var uniform = this.gl.getUniformLocation(this.program, name);
 98      this.gl.uniformMatrix4fv(uniform, false, array);
 99    }
100  };
101  return constructor;
102})();
103
104/**
105 * Represents a WebGL texture object.
106 */
107var Texture = (function texture() {
108  function constructor(gl, size) {
109    this.gl = gl;
110    this.size = size;
111    this.texture = gl.createTexture();
112    gl.bindTexture(gl.TEXTURE_2D, this.texture);
113    gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, size.w, size.h, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, null);
114    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
115    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
116    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
117    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
118  }
119  var textureIDs = null;
120  constructor.prototype = {
121    fill: function(textureData) {
122      var gl = this.gl;
123      assert(textureData.length >= this.size.w * this.size.h, 
124             "Texture size mismatch, data:" + textureData.length + ", texture: " + this.size.w * this.size.h);
125      gl.bindTexture(gl.TEXTURE_2D, this.texture);
126      gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, this.size.w , this.size.h, gl.LUMINANCE, gl.UNSIGNED_BYTE, textureData);
127    },
128    bind: function(n, program, name) {
129      var gl = this.gl;
130      if (!textureIDs) {
131        textureIDs = [gl.TEXTURE0, gl.TEXTURE1, gl.TEXTURE2];
132      }
133      gl.activeTexture(textureIDs[n]);
134      gl.bindTexture(gl.TEXTURE_2D, this.texture);
135      gl.uniform1i(gl.getUniformLocation(program.program, name), n);
136    }
137  };
138  return constructor; 
139})();
140
141/**
142 * Generic WebGL backed canvas that sets up: a quad to paint a texture on, appropriate vertex/fragment shaders,
143 * scene parameters and other things. Specialized versions of this class can be created by overriding several 
144 * initialization methods.
145 * 
146 * <code>
147 * var canvas = new WebGLCanvas(document.getElementById('canvas'), new Size(512, 512);
148 * canvas.texture.fill(data);
149 * canvas.drawScene();
150 * </code>
151 */
152var WebGLCanvas = (function () {
153  
154  var vertexShaderScript = Script.createFromSource("x-shader/x-vertex", text([
155    "attribute vec3 aVertexPosition;",
156    "attribute vec2 aTextureCoord;",
157    "uniform mat4 uMVMatrix;",
158    "uniform mat4 uPMatrix;",
159    "varying highp vec2 vTextureCoord;",
160    "void main(void) {",
161    "  gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);",
162    "  vTextureCoord = aTextureCoord;",
163    "}"
164    ]));
165  
166  var fragmentShaderScript = Script.createFromSource("x-shader/x-fragment", text([
167    "precision highp float;",
168    "varying highp vec2 vTextureCoord;",
169    "uniform sampler2D texture;",
170    "void main(void) {",
171    "  gl_FragColor = texture2D(texture, vTextureCoord);",
172    "}"
173    ]));
174  
175  function constructor(canvas, size) {
176    this.canvas = canvas;
177    this.size = size;
178    this.canvas.width = size.w;
179    this.canvas.height = size.h;
180    
181    this.onInitWebGL();
182    this.onInitShaders();
183    initBuffers.call(this);
184    this.onInitTextures();
185    initScene.call(this);
186  }
187
188  /**
189   * Initialize vertex and texture coordinate buffers for a plane.
190   */
191  function initBuffers() {
192    var tmp;
193    var gl = this.gl;
194    
195    // Create vertex position buffer.
196    this.quadVPBuffer = gl.createBuffer();
197    gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVPBuffer);
198    tmp = [
199       1.0,  1.0, 0.0,
200      -1.0,  1.0, 0.0, 
201       1.0, -1.0, 0.0, 
202      -1.0, -1.0, 0.0];
203    
204    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(tmp), gl.STATIC_DRAW);
205    this.quadVPBuffer.itemSize = 3;
206    this.quadVPBuffer.numItems = 4;
207    
208    /*
209     +--------------------+ 
210     | -1,1 (1)           | 1,1 (0)
211     |                    |
212     |                    |
213     |                    |
214     |                    |
215     |                    |
216     | -1,-1 (3)          | 1,-1 (2)
217     +--------------------+
218     */
219    
220    var scaleX = 1.0;
221    var scaleY = 1.0;
222    
223    // Create vertex texture coordinate buffer.
224    this.quadVTCBuffer = gl.createBuffer();
225    gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVTCBuffer);
226    tmp = [
227      scaleX, 0.0,
228      0.0, 0.0,
229      scaleX, scaleY,
230      0.0, scaleY,
231    ];
232    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(tmp), gl.STATIC_DRAW);
233  }
234  
235  function mvIdentity() {
236    this.mvMatrix = Matrix.I(4);
237  }
238
239  function mvMultiply(m) {
240    this.mvMatrix = this.mvMatrix.x(m);
241  }
242
243  function mvTranslate(m) {
244    mvMultiply.call(this, Matrix.Translation($V([m[0], m[1], m[2]])).ensure4x4());
245  }
246  
247  function setMatrixUniforms() {
248    this.program.setMatrixUniform("uPMatrix", new Float32Array(this.perspectiveMatrix.flatten()));
249    this.program.setMatrixUniform("uMVMatrix", new Float32Array(this.mvMatrix.flatten()));
250  }
251
252  function initScene() {
253    var gl = this.gl;
254    
255    // Establish the perspective with which we want to view the
256    // scene. Our field of view is 45 degrees, with a width/height
257    // ratio of 640:480, and we only want to see objects between 0.1 units
258    // and 100 units away from the camera.
259    
260    this.perspectiveMatrix = makePerspective(45, 1, 0.1, 100.0);
261    
262    // Set the drawing position to the "identity" point, which is
263    // the center of the scene.
264    mvIdentity.call(this);
265    
266    // Now move the drawing position a bit to where we want to start
267    // drawing the square.
268    mvTranslate.call(this, [0.0, 0.0, -2.4]);
269
270    // Draw the cube by binding the array buffer to the cube's vertices
271    // array, setting attributes, and pushing it to GL.
272    gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVPBuffer);
273    gl.vertexAttribPointer(this.vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
274    
275    // Set the texture coordinates attribute for the vertices.
276    
277    gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVTCBuffer);
278    gl.vertexAttribPointer(this.textureCoordAttribute, 2, gl.FLOAT, false, 0, 0);  
279    
280    this.onInitSceneTextures();
281    
282    setMatrixUniforms.call(this);
283  }
284  
285  constructor.prototype = {
286    toString: function() {
287      return "WebGLCanvas Size: " + this.size;
288    },
289    checkLastError: function (operation) {
290      var err = this.gl.getError();
291      if (err != this.gl.NO_ERROR) {
292        var name = this.glNames[err];
293        name = (name !== undefined) ? name + "(" + err + ")":
294            ("Unknown WebGL ENUM (0x" + value.toString(16) + ")");
295        if (operation) {
296          console.log("WebGL Error: %s, %s", operation, name);
297        } else {
298          console.log("WebGL Error: %s", name);
299        }
300        console.trace();
301      }
302    },
303    onInitWebGL: function () {
304      try {
305        this.gl = this.canvas.getContext("experimental-webgl");
306      } catch(e) {}
307      
308      if (!this.gl) {
309        error("Unable to initialize WebGL. Your browser may not support it.");
310      }
311      if (this.glNames) {
312        return;
313      }
314      this.glNames = {};
315      for (var propertyName in this.gl) {
316        if (typeof this.gl[propertyName] == 'number') {
317          this.glNames[this.gl[propertyName]] = propertyName;
318        }
319      }
320    },
321    onInitShaders: function() {
322      this.program = new Program(this.gl);
323      this.program.attach(new Shader(this.gl, vertexShaderScript));
324      this.program.attach(new Shader(this.gl, fragmentShaderScript));
325      this.program.link();
326      this.program.use();
327      this.vertexPositionAttribute = this.program.getAttributeLocation("aVertexPosition");
328      this.gl.enableVertexAttribArray(this.vertexPositionAttribute);
329      this.textureCoordAttribute = this.program.getAttributeLocation("aTextureCoord");;
330      this.gl.enableVertexAttribArray(this.textureCoordAttribute);
331    },
332    onInitTextures: function () {
333      this.texture = new Texture(this.gl, this.size);
334    },
335    onInitSceneTextures: function () {
336      this.texture.bind(0, this.program, "texture");
337    },
338    drawScene: function() {
339      this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
340    }
341  };
342  return constructor;
343})();
344
345var YUVWebGLCanvas = (function () {
346  var vertexShaderScript = Script.createFromSource("x-shader/x-vertex", text([
347    "attribute vec3 aVertexPosition;",
348    "attribute vec2 aTextureCoord;",
349    "uniform mat4 uMVMatrix;",
350    "uniform mat4 uPMatrix;",
351    "varying highp vec2 vTextureCoord;",
352    "void main(void) {",
353    "  gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);",
354    "  vTextureCoord = aTextureCoord;",
355    "}"
356  ]));
357  
358  var fragmentShaderScriptOld = Script.createFromSource("x-shader/x-fragment", text([
359    "precision highp float;",
360    "varying highp vec2 vTextureCoord;",
361    "uniform sampler2D YTexture;",
362    "uniform sampler2D UTexture;",
363    "uniform sampler2D VTexture;",
364    
365    "void main(void) {",
366    "  vec3 YUV = vec3",
367    "  (",
368    "    texture2D(YTexture, vTextureCoord).x * 1.1643828125,   // premultiply Y",
369    "    texture2D(UTexture, vTextureCoord).x,",
370    "    texture2D(VTexture, vTextureCoord).x",
371    "  );",
372    "  gl_FragColor = vec4",
373    "  (",
374    "    YUV.x + 1.59602734375 * YUV.z - 0.87078515625,",
375    "    YUV.x - 0.39176171875 * YUV.y - 0.81296875 * YUV.z + 0.52959375,",
376    "    YUV.x + 2.017234375   * YUV.y - 1.081390625,",
377    "    1",
378    "  );",
379    "}"
380  ]));
381  
382  var fragmentShaderScriptSimple = Script.createFromSource("x-shader/x-fragment", text([
383   "precision highp float;",
384   "varying highp vec2 vTextureCoord;",
385   "uniform sampler2D YTexture;",
386   "uniform sampler2D UTexture;",
387   "uniform sampler2D VTexture;",
388   
389   "void main(void) {",
390   "  gl_FragColor = texture2D(YTexture, vTextureCoord);",
391   "}"
392   ]));
393
394  var fragmentShaderScript = Script.createFromSource("x-shader/x-fragment", text([
395    "precision highp float;",
396    "varying highp vec2 vTextureCoord;",
397    "uniform sampler2D YTexture;",
398    "uniform sampler2D UTexture;",
399    "uniform sampler2D VTexture;",
400    "const mat4 YUV2RGB = mat4",
401    "(",
402    " 1.1643828125, 0, 1.59602734375, -.87078515625,",
403    " 1.1643828125, -.39176171875, -.81296875, .52959375,",
404    " 1.1643828125, 2.017234375, 0, -1.081390625,",
405    " 0, 0, 0, 1",
406    ");",
407  
408    "void main(void) {",
409    " gl_FragColor = vec4( texture2D(YTexture,  vTextureCoord).x, texture2D(UTexture, vTextureCoord).x, texture2D(VTexture, vTextureCoord).x, 1) * YUV2RGB;",
410    "}"
411  ]));
412  
413  
414  function constructor(canvas, size) {
415    WebGLCanvas.call(this, canvas, size);
416  } 
417  
418  constructor.prototype = inherit(WebGLCanvas, {
419    onInitShaders: function() {
420      this.program = new Program(this.gl);
421      this.program.attach(new Shader(this.gl, vertexShaderScript));
422      this.program.attach(new Shader(this.gl, fragmentShaderScript));
423      this.program.link();
424      this.program.use();
425      this.vertexPositionAttribute = this.program.getAttributeLocation("aVertexPosition");
426      this.gl.enableVertexAttribArray(this.vertexPositionAttribute);
427      this.textureCoordAttribute = this.program.getAttributeLocation("aTextureCoord");;
428      this.gl.enableVertexAttribArray(this.textureCoordAttribute);
429    },
430    onInitTextures: function () {
431      console.log("creatingTextures: size: " + this.size);
432      this.YTexture = new Texture(this.gl, this.size);
433      this.UTexture = new Texture(this.gl, this.size.getHalfSize());
434      this.VTexture = new Texture(this.gl, this.size.getHalfSize());
435    },
436    onInitSceneTextures: function () {
437      this.YTexture.bind(0, this.program, "YTexture");
438      this.UTexture.bind(1, this.program, "UTexture");
439      this.VTexture.bind(2, this.program, "VTexture");
440    },
441    fillYUVTextures: function(y, u, v) {
442      this.YTexture.fill(y);
443      this.UTexture.fill(u);
444      this.VTexture.fill(v);
445    },
446    toString: function() {
447      return "YUVCanvas Size: " + this.size;
448    }
449  });
450  
451  return constructor;
452})();