webgl.js 6.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. JSMpeg.Renderer.WebGL = (function(){ "use strict";
  2. var WebGLRenderer = function(options) {
  3. this.canvas = options.canvas || document.createElement('canvas');
  4. this.width = this.canvas.width;
  5. this.height = this.canvas.height;
  6. this.enabled = true;
  7. var contextCreateOptions = {
  8. preserveDrawingBuffer: !!options.preserveDrawingBuffer,
  9. alpha: false,
  10. depth: false,
  11. stencil: false,
  12. antialias: false
  13. };
  14. this.gl =
  15. this.canvas.getContext('webgl', contextCreateOptions) ||
  16. this.canvas.getContext('experimental-webgl', contextCreateOptions);
  17. if (!this.gl) {
  18. throw new Error('Failed to get WebGL Context');
  19. }
  20. var gl = this.gl;
  21. var vertexAttr = null;
  22. // Init buffers
  23. var vertexBuffer = gl.createBuffer();
  24. var vertexCoords = new Float32Array([0, 0, 0, 1, 1, 0, 1, 1]);
  25. gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
  26. gl.bufferData(gl.ARRAY_BUFFER, vertexCoords, gl.STATIC_DRAW);
  27. // Setup the main YCrCbToRGBA shader
  28. this.program = this.createProgram(
  29. WebGLRenderer.SHADER.VERTEX_IDENTITY,
  30. WebGLRenderer.SHADER.FRAGMENT_YCRCB_TO_RGBA
  31. );
  32. vertexAttr = gl.getAttribLocation(this.program, 'vertex');
  33. gl.enableVertexAttribArray(vertexAttr);
  34. gl.vertexAttribPointer(vertexAttr, 2, gl.FLOAT, false, 0, 0);
  35. this.textureY = this.createTexture(0, 'textureY');
  36. this.textureCb = this.createTexture(1, 'textureCb');
  37. this.textureCr = this.createTexture(2, 'textureCr');
  38. // Setup the loading animation shader
  39. this.loadingProgram = this.createProgram(
  40. WebGLRenderer.SHADER.VERTEX_IDENTITY,
  41. WebGLRenderer.SHADER.FRAGMENT_LOADING
  42. );
  43. vertexAttr = gl.getAttribLocation(this.loadingProgram, 'vertex');
  44. gl.enableVertexAttribArray(vertexAttr);
  45. gl.vertexAttribPointer(vertexAttr, 2, gl.FLOAT, false, 0, 0);
  46. this.shouldCreateUnclampedViews = !this.allowsClampedTextureData();
  47. };
  48. WebGLRenderer.prototype.resize = function(width, height) {
  49. this.width = width|0;
  50. this.height = height|0;
  51. this.canvas.width = this.width;
  52. this.canvas.height = this.height;
  53. this.gl.useProgram(this.program);
  54. this.gl.viewport(0, 0, this.width, this.height);
  55. };
  56. WebGLRenderer.prototype.createTexture = function(index, name) {
  57. var gl = this.gl;
  58. var texture = gl.createTexture();
  59. gl.bindTexture(gl.TEXTURE_2D, texture);
  60. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
  61. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  62. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  63. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  64. gl.uniform1i(gl.getUniformLocation(this.program, name), index);
  65. return texture;
  66. };
  67. WebGLRenderer.prototype.createProgram = function(vsh, fsh) {
  68. var gl = this.gl;
  69. var program = gl.createProgram();
  70. gl.attachShader(program, this.compileShader(gl.VERTEX_SHADER, vsh));
  71. gl.attachShader(program, this.compileShader(gl.FRAGMENT_SHADER, fsh));
  72. gl.linkProgram(program);
  73. gl.useProgram(program);
  74. return program;
  75. };
  76. WebGLRenderer.prototype.compileShader = function(type, source) {
  77. var gl = this.gl;
  78. var shader = gl.createShader(type);
  79. gl.shaderSource(shader, source);
  80. gl.compileShader(shader);
  81. if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
  82. throw new Error(gl.getShaderInfoLog(shader));
  83. }
  84. return shader;
  85. };
  86. WebGLRenderer.prototype.allowsClampedTextureData = function() {
  87. var gl = this.gl;
  88. var texture = gl.createTexture();
  89. gl.bindTexture(gl.TEXTURE_2D, texture);
  90. gl.texImage2D(
  91. gl.TEXTURE_2D, 0, gl.LUMINANCE, 1, 1, 0,
  92. gl.LUMINANCE, gl.UNSIGNED_BYTE, new Uint8ClampedArray([0])
  93. );
  94. return (gl.getError() === 0);
  95. };
  96. WebGLRenderer.prototype.renderProgress = function(progress) {
  97. var gl = this.gl;
  98. var loc = gl.getUniformLocation(this.loadingProgram, 'progress');
  99. gl.uniform1f(loc, progress);
  100. gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
  101. };
  102. WebGLRenderer.prototype.render = function(y, cb, cr) {
  103. if (!this.enabled) {
  104. return;
  105. }
  106. var gl = this.gl;
  107. var w = ((this.width + 15) >> 4) << 4,
  108. h = this.height,
  109. w2 = w >> 1,
  110. h2 = h >> 1;
  111. // In some browsers WebGL doesn't like Uint8ClampedArrays (this is a bug
  112. // and should be fixed soon-ish), so we have to create a Uint8Array view
  113. // for each plane.
  114. if (this.shouldCreateUnclampedViews) {
  115. y = new Uint8Array(y.buffer),
  116. cb = new Uint8Array(cb.buffer),
  117. cr = new Uint8Array(cr.buffer);
  118. }
  119. this.updateTexture(gl.TEXTURE0, this.textureY, w, h, y);
  120. this.updateTexture(gl.TEXTURE1, this.textureCb, w2, h2, cb);
  121. this.updateTexture(gl.TEXTURE2, this.textureCr, w2, h2, cr);
  122. gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
  123. };
  124. WebGLRenderer.prototype.updateTexture = function(unit, texture, w, h, data) {
  125. var gl = this.gl;
  126. gl.activeTexture(unit);
  127. gl.bindTexture(gl.TEXTURE_2D, texture);
  128. gl.texImage2D(
  129. gl.TEXTURE_2D, 0, gl.LUMINANCE, w, h, 0,
  130. gl.LUMINANCE, gl.UNSIGNED_BYTE, data
  131. );
  132. }
  133. WebGLRenderer.IsSupported = function() {
  134. try {
  135. if (!window.WebGLRenderingContext) {
  136. return false;
  137. }
  138. var canvas = document.createElement('canvas');
  139. return !!(
  140. canvas.getContext('webgl') ||
  141. canvas.getContext('experimental-webgl')
  142. );
  143. }
  144. catch (err) {
  145. return false;
  146. }
  147. };
  148. WebGLRenderer.SHADER = {
  149. FRAGMENT_YCRCB_TO_RGBA: [
  150. 'precision mediump float;',
  151. 'uniform sampler2D textureY;',
  152. 'uniform sampler2D textureCb;',
  153. 'uniform sampler2D textureCr;',
  154. 'varying vec2 texCoord;',
  155. 'mat4 rec601 = mat4(',
  156. '1.16438, 0.00000, 1.59603, -0.87079,',
  157. '1.16438, -0.39176, -0.81297, 0.52959,',
  158. '1.16438, 2.01723, 0.00000, -1.08139,',
  159. '0, 0, 0, 1',
  160. ');',
  161. 'void main() {',
  162. 'float y = texture2D(textureY, texCoord).r;',
  163. 'float cb = texture2D(textureCb, texCoord).r;',
  164. 'float cr = texture2D(textureCr, texCoord).r;',
  165. 'gl_FragColor = vec4(y, cr, cb, 1.0) * rec601;',
  166. '}'
  167. ].join('\n'),
  168. FRAGMENT_LOADING: [
  169. 'precision mediump float;',
  170. 'uniform float progress;',
  171. 'varying vec2 texCoord;',
  172. 'void main() {',
  173. 'float c = ceil(progress-(1.0-texCoord.y));',
  174. 'gl_FragColor = vec4(c,c,c,1);',
  175. '}'
  176. ].join('\n'),
  177. VERTEX_IDENTITY: [
  178. 'attribute vec2 vertex;',
  179. 'varying vec2 texCoord;',
  180. 'void main() {',
  181. 'texCoord = vertex;',
  182. 'gl_Position = vec4((vertex * 2.0 - 1.0) * vec2(1, -1), 0.0, 1.0);',
  183. '}'
  184. ].join('\n')
  185. };
  186. return WebGLRenderer;
  187. })();