-->

How to create a Texture from a ArrayBuffer in html

2019-07-11 01:19发布

问题:

I am having a image that I am reading in server side and pushing to web browser via AJAX call. I have a requirement where I have to render them line by line using WebGL.

For Example : Image is 640X480 where 640 is width and 480 is height. Now the total number of pixels will be 640*480 = 307200 pixels. So, I want to render the whole image in 640(total width) intervals in a loop using WebGL.

Now I have texture2D(as per my knowledge) in webgl to do so, but not getting any idea of where to start . I also having the ArrayBuffer with me , only thing is using Texture2D I want to render it slowly ,line by line.

I am ready to go for any js libraries ,if they are satisfying the requirements.

Tagging Babylon.js and Three.js ,incase those guys have a answer to my question with what they already have.

Adding some code to explain my requirement :

//Recieve data
Code : 
var imageDataFromServer = single line data from server;
var imageLineData = new ArrayBuffer(imageDataFromServer.length);
var imageUintArray = new Uint8Array(imageLineData);
for (var i = 0; i < width(i.e.640) ;i++) //because I know one line length =640.
{
   //how to put data in texture2D using Three.js or Babylon.js or just plain WebGL
}

回答1:

Ok, below is the working code 1) put it into .html file 2) load image (see comment in initTextures function)

In this code is also added vertex and fragment shaders

<canvas width="400" height="400"></canvas>

<script>
function initShaders(gl, vshader, fshader) {
  var program = createProgram(gl, vshader, fshader);    
  gl.useProgram(program);
  gl.program = program;

  return true;
}

function createProgram(gl, vshader, fshader) {
  var vertexShader = loadShader(gl, gl.VERTEX_SHADER, vshader);
  var fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fshader);
  var program = gl.createProgram();

  gl.attachShader(program, vertexShader);
  gl.attachShader(program, fragmentShader);

  gl.linkProgram(program);
  return program;
}

function loadShader(gl, type, source) {
  var shader = gl.createShader(type);
  gl.shaderSource(shader, source);
  gl.compileShader(shader);
  return shader;
}

function getWebGLContext(canvas, opt_debug) {
  return canvas.getContext('webgl');
}

var VSHADER_SOURCE =
  'attribute vec4 a_Position;\n' +
  'attribute vec2 a_TexCoord;\n' +
  'varying vec2 uv;\n' +
  'void main() {\n' +
  '  gl_Position = a_Position;\n' +
  '  uv = a_TexCoord;\n' +
  '}\n';

var FSHADER_SOURCE =
  '#ifdef GL_ES\n' +
  'precision mediump float;\n' +
  '#endif\n' +
  'uniform sampler2D uImage0;\n' +
  'uniform vec2 resolution;\n' +
  'uniform float time;\n' +
  'varying vec2 uv;\n' +
  'void main() {\n' +
      'vec2 position = 1.0 - gl_FragCoord.xy / resolution;\n' +
      'vec3 color = vec3(1.0);\n' +

      'if (time > position.y * 10.0) {\n' +
          'color = vec3(texture2D(uImage0, uv));\n' +
      '}\n' +
  '  gl_FragColor = vec4(color, 1.0);\n' +
  '}\n';

function main() {
  var canvas = document.querySelector('canvas');
  var gl = getWebGLContext(canvas);
  initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE);

  var n = initVertexBuffers(gl);
  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  initTextures(gl, n)

  function render (dt) {
    gl.uniform1f(time, dt / 1000);
    draw();
    requestAnimationFrame(render);
  }

  render();
}

function initVertexBuffers(gl) {
  var verticesTexCoords = new Float32Array([
    -0.5,  0.5,   0.0, 1.0,
    -0.5, -0.5,   0.0, 0.0,
     0.5,  0.5,   1.0, 1.0,
     0.5, -0.5,   1.0, 0.0,
  ]);
  var n = 4;
  var vertexTexCoordBuffer = gl.createBuffer();

  gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexCoordBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW);

  var FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;
  var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 4, 0);
  gl.enableVertexAttribArray(a_Position);

  var a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord');
  gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, FSIZE * 4, FSIZE * 2);
  gl.enableVertexAttribArray(a_TexCoord);

  return n;
}

function initTextures(gl, n) {
  var texture = gl.createTexture();
  resolution = gl.getUniformLocation(gl.program, 'resolution');
  gl.uniform2f(resolution, 256, 256);
  time = gl.getUniformLocation(gl.program, 'time');

  var uImage0 = gl.getUniformLocation(gl.program, 'uImage0');

  var image = new Image();
  image.onload = function(){ loadTexture(gl, n, texture, uImage0, image); };

  // load this file: http://www.html5rocks.com/static/images/tutorials/easy-hidpi/chrome1x.png
  // to the same folder as this .html file
  image.src = 'chrome1x.png';

  return true;
}

function loadTexture(gl, n, texture, uImage0, image) {
  count = n;
  GL = gl;

  gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
  gl.activeTexture(gl.TEXTURE0);
  gl.bindTexture(gl.TEXTURE_2D, texture);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
  gl.uniform1i(uImage0, 0);

  draw();
}

function draw () {
  if (!window.GL) {
    return;
  }
  GL.clear(GL.COLOR_BUFFER_BIT);
  GL.drawArrays(GL.TRIANGLE_STRIP, 0, count);
}

main();
</script>


回答2:

So, to write a image line by line we can do something like this.

Vertex Shader

  • attribute vec2 a_position;?
    attribute vec2 a_texCoord;?
    
    void main() {
       ???
    }
    
  • Fragment Shader

     #ifdef GL_ES
     precision mediump float;
     #endif
    
    uniform float time;
    uniform vec2 mouse;
    uniform vec2 resolution;
    
    void main( void ) {
      vec2 position = 1.0 - gl_FragCoord.xy / resolution;
      vec3 color = vec3(1.0);
    
      if (time > position.y * 10.0) {
          color = texture2D(uImage0, uv);
      }
    
     gl_FragColor = vec4(color, 1.0);
    
    }
    
  • Javascript For rendering pixel by pixel

      function createTextureFromArray(gl, dataArray, type, width, height) {
            var data = new Uint8Array(dataArray);
            var texture = gl.createTexture();
            gl.bindTexture(gl.TEXTURE_2D, texture);
            gl.texImage2D(gl.TEXTURE_2D, 0, type, width, height, 0, type, gl.UNSIGNED_BYTE, data);
            return texture;
     }
    
       var arrayBuffer = new ArrayBuffer(640*480);
       for (var i=0; i < 640; i++) {
           for (var j=0; j < 480; j++) {
               arrayBuffer[i] = Math.floor(Math.random() * 255) + 0;     //filling buffer with random data between 0 and 255 which will be further filled to the texture 
               //NOTE : above data is just dummy data , I will get this data from server pixel by pixel.
           }
       }
       var gl = canvas.getContext('webgl');
       // setup GLSL program
       var program = createProgramFromScripts(gl, ["2d-vertex-shader", "2d-fragment-shader"]);
       gl.useProgram(program);
       //what should I add after this ?