2

我有一张正在服务器端阅读并通过 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 ?
    

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

4

2 回答 2

2

OpenGL 并非旨在“逐行”绘制图像。但是,您可以在软件中通过写入数组、将其作为纹理上传并在绘制全屏多边形时在着色器中对其进行采样来实现此效果。

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

“逐行绘制”将是在给定一行的时间一次更新“宽度”像素。每次更改图像数据时,您都应该将图像重新上传到 gpu,然后绘制全屏多边形。

全屏多边形只是两个覆盖屏幕整个剪辑空间的三角形。屏幕在 x 和 y 维度上从 -1 变为 1,因此相应地制作一个数组缓冲区,将其与两个三角形一起上传,并在更新纹理时调用 drawArrays。多边形的 UV 应该从 0 到 1,因此在您的顶点着色器中,您应该有一个“变化”的输出变量,它将是 0.5 * 位置 + 0.5。这在片段着色器中用于从纹理中采样。

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

于 2016-02-10T11:57:07.273 回答
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用来制作 1xheight 图像并用于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 高度的纹理并将其绘制在适当的位置。或者,将 1 x 高度数据复制到全尺寸纹理中gl.texSubImage2D,然后适当调整位置和纹理坐标,以将要绘制的纹理部分绘制到要绘制的画布部分。

drawImage在 WebGL 中实现看起来像这样。我使用的是twgl.js,因为 WebGL 太冗长了。

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);
canvas { border: 1px solid black; }
<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>

要了解它们的矩阵数学,请参阅这些文章并在这些文章中向后或向前工作。

于 2016-02-10T11:59:58.633 回答