如何在ArrayGLuffer中渲染WebGL中的图像

时间:2016-02-10 07:34:32

标签: javascript glsl webgl babylonjs

我正在使用服务器端读取的图像,并通过AJAX调用推送到Web浏览器。我有一个要求,我必须使用WebGL逐行渲染它们。

例如:图像是640X480,其中640是宽度,480是高度。现在像素的总数将是640 * 480 = 307200像素。因此,我想使用WebGL在循环中以640(总宽度)间隔渲染整个图像。

现在我在webgl中有texture2D(据我所知)这样做,但不知道从哪里开始。我也有ArrayBuffer和我,只有使用Texture2D我想慢慢地逐行渲染它。

如果满足要求,我准备去任何js库。 因此,要逐行编写图像,我们可以这样做。

顶点着色器

  • attribute vec2 a_position;?
    attribute vec2 a_texCoord;?
    
    void main() {
       ???
    }
    
  • 片段着色器

     #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用于逐像素渲染

      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 ?
    

    任何人都可以完成代码,我不知道如何编写代码来完成此任务。

2 个答案:

答案 0 :(得分:1)

OpenGL不是为了逐行绘制图像而设计的。&#34;但是,您可以通过写入数组,将其作为纹理上载并在绘制全屏多边形时在着色器中对其进行采样来在软件中实现此效果。

要解决此问题,您应该创建一个无符号字节数组。对于图像中的每个像素,您可以使用红色,绿色,蓝色和Alpha通道的某种组合。最简单的情况是RGB,每个像素有3个无符号字节。最终数组的大小应根据组件大小(3),宽度(640)乘以你的高度(480)。您应该根据您想要的背景颜色初始化数组中的值,然后使用texImage2D将其上传到gpu。

逐行绘制&#39;将更新宽度&#39;给定一行的像素。每次更改图像数据时,都应将图像重新上传到gpu,然后绘制全屏多边形。

全屏多边形只是两个三角形,覆盖了整个屏幕的剪辑空间。屏幕在x和y维度上从-1变为1,因此相应地创建一个数组缓冲区,使用两个三角形上传它,并在更新纹理时调用drawArrays。多边形的UV应该从0到1,所以在你的顶点着色器中你应该有一个变化的&#39;输出变量为0.5 *位置+ 0.5。这在片段着色器中用于从纹理中进行采样。

官方文档是最好的学习场所之一。 openGL ES或openGL 3的官方参考页面包含相关信息,而参考卡https://www.khronos.org/files/webgl/webgl-reference-card-1_0.pdf显示WebGL中可用的功能,大致对应于相同的api。

答案 1 :(得分:1)

根本不清楚你想要完成什么以及为什么要使用WebGL。您是否一次发送一行数据并且想要在收到数据时呈现一行数据?您是否正在发送所有数据,而您只是想在水平时间显示一条线?

如果你有整个图像可用,那么你可以使用canvas2d渲染它的越来越大的部分。 drawImage函数采用可选的源矩形和目标矩形。

// at init time
var x = 0;

// at render time
while (x < img.width) {
  var srcX = x;
  var srcY = 0;
  var srcWidth = 1;  // one pixel per frame
  var srcHeight = img.height;
  var dstX = x;
  var dstY = 0;
  var dstWidth = 1;
  var dstHeight = img.height;
  ctx.drawImage(img, srcX, srcY, srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight);
  ++x;
}

如果您一次向他们发送1行数据,则可以使用ImageData制作1x高度图像并使用putImageData进行绘制。

// at init time or whenever you know the height
var imageData = ctx.createImageData(1, height);
var x = 0;

// on received next line of data
for (var ii = 0; ii < imageData.length; ++ii) {
  imageData.data[ii] = receivedColumnOfPixels[ii];
}

ctx.putImageData(imageData, x, 0);
++x;

如果你想缩放ImageData将它放在第二个画布中,并使用第一种技术将该画布用作drawImage的输入。

您可以在WebGL中执行相同的操作。如果你在内存中有整个纹理,那么每个帧都会调整你的位置和纹理坐标以绘制它的不同部分。如果您一次只接收1列数据,那么只需使用1 x高度的纹理并在适当的位置绘制。或者,使用gl.texSubImage2D将1 x高度数据复制到fullsize纹理中,然后相应地调整位置和纹理坐标,以将要绘制的纹理部分绘制到要绘制它的画布部分。

在WebGL中实现的

drawImage看起来像这样。我使用twgl.js因为WebGL太冗长了。

&#13;
&#13;
var m4 = twgl.m4;
var gl = document.getElementById("c").getContext("webgl");
// compiles shader, links and looks up locations
var programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);

// a unit quad
var arrays = {
  position: { 
    numComponents: 2, 
    data: [
      0, 0,  
      1, 0, 
      0, 1, 
      0, 1, 
      1, 0,  
      1, 1,
    ],
  },
};
// calls gl.createBuffer, gl.bindBuffer, gl.bufferData for each array
var bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

// create a texture using a canvas so we don't have to download one
var ctx = document.createElement("canvas").getContext("2d");
ctx.fillStyle = "blue";
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.lineWidth = 20;
["red", "orange", "yellow"].forEach(function(color, ndx, array) {
  ctx.strokeStyle = color;
  ctx.beginPath();
  ctx.arc((ndx + 1) / (array.length + 1) * ctx.canvas.width, ctx.canvas.height / 2, ctx.canvas.height * 0.4, 0, Math.PI * 2, false);
  ctx.stroke();
});
ctx.fillStyle = "white";
ctx.font = "40px sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("DrawImage", ctx.canvas.width / 2, ctx.canvas.height / 2);

// calls gl.createTexture, gl.bindTexture, gl.texImage2D, gl.texParameteri
var tex = twgl.createTexture(gl, { src: ctx.canvas });
var texWidth  = ctx.canvas.width;
var texHeight = ctx.canvas.height;

// we pass in texWidth and texHeight because unlike images
// we can't look up the width and height of a texture

// we pass in targetWidth and targetHeight to tell it
// the size of the thing we're drawing too. We could look 
// up the size of the canvas with gl.canvas.width and
// gl.canvas.height but maybe we want to draw to a framebuffer
// etc.. so might as well pass those in.

// srcX, srcY, srcWidth, srcHeight are in pixels 
// computed from texWidth and texHeight

// dstX, dstY, dstWidth, dstHeight are in pixels
// computed from targetWidth and targetHeight
function drawImage(
    tex, texWidth, texHeight,
    srcX, srcY, srcWidth, srcHeight,
    dstX, dstY, dstWidth, dstHeight,
    targetWidth, targetHeight) {
  var mat  = m4.identity();
  var tmat = m4.identity();
  
  var uniforms = {
    matrix: mat,
    textureMatrix: tmat,
    texture: tex,
  };

  // these adjust the unit quad to generate texture coordinates
  // to select part of the src texture

  // NOTE: no check is done that srcX + srcWidth go outside of the
  // texture or are in range in any way. Same for srcY + srcHeight

  m4.translate(tmat, [srcX / texWidth, srcY / texHeight, 0], tmat);
  m4.scale(tmat, [srcWidth / texWidth, srcHeight / texHeight, 1], tmat);

  // these convert from pixels to clip space
  m4.translate(mat, [-1, 1, 0], mat);
  m4.scale(mat, [2 / targetWidth, -2 / targetHeight, 1], mat); 

  // these move and scale the unit quad into the size we want
  // in the target as pixels
  m4.translate(mat, [dstX, dstY, 0], mat);
  m4.scale(mat, [dstWidth, dstHeight, 1], mat);

  gl.useProgram(programInfo.program);
  // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
  // calls gl.uniformXXX, gl.activeTexture, gl.bindTexture
  twgl.setUniforms(programInfo, uniforms);
  // calls gl.drawArray or gl.drawElements
  twgl.drawBufferInfo(gl, gl.TRIANGLES, bufferInfo);
  
}
  
function render(time) {
  time *= 0.001;
  var targetWidth  = gl.canvas.width;
  var targetHeight = gl.canvas.height;

  // pick some various src rects and dst rects
  var srcX = Math.abs(Math.sin(time * 1   )) * texWidth;
  var srcY = Math.abs(Math.sin(time * 1.81)) * texHeight;
  var srcWidth  = (texWidth  - srcX) * Math.abs(Math.sin(time * 2.12));
  var srcHeight = (texHeight - srcY) * Math.abs(Math.sin(time * 1.53));

  var dstX = Math.abs(Math.sin(time * 0.34)) * targetWidth;
  var dstY = Math.abs(Math.sin(time * 2.75)) * targetHeight;
  var dstWidth  = (targetWidth  - dstX) * Math.abs(Math.sin(time * 1.16));
  var dstHeight = (targetHeight - dstY) * Math.abs(Math.sin(time * 1.17));
  
  drawImage(
    tex, texWidth, texHeight,  
    srcX, srcY, srcWidth, srcHeight,
    dstX, dstY, dstWidth, dstHeight,
    targetWidth, targetHeight);

  requestAnimationFrame(render);
}
requestAnimationFrame(render);
&#13;
canvas { border: 1px solid black; }
&#13;
<script src="https://twgljs.org/dist/twgl-full.min.js"></script>
<script id="vs" type="not-js">
// we will always pass a 0 to 1 unit quad
// and then use matrices to manipulate it
attribute vec4 position;   

uniform mat4 matrix;
uniform mat4 textureMatrix;

varying vec2 texcoord;

void main () {
  gl_Position = matrix * position;
  
  texcoord = (textureMatrix * position).xy;
}
</script>
<script id="fs" type="not-js">
precision mediump float;

varying vec2 texcoord;
uniform sampler2D texture;

void main() {
  gl_FragColor = texture2D(texture, texcoord);
}
</script>
<canvas id="c"></canvas>
&#13;
&#13;
&#13;

要理解矩阵数学see these articles,并在这些文章中向前或向前工作。