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