How can I flip the result of WebGLRenderingContext.readPixels()?

前端 未结 3 682
无人及你
无人及你 2021-01-14 02:55

Why is the imageData that I get from WebGLRenderingContext.readPixels() upside down?

I try to do the folowing:

var gl = renderer.domElem         


        
相关标签:
3条回答
  • 2021-01-14 03:31

    You can flip pixels of ImadaData directly like this.

    const imageData = new ImageData(Uint8ClampedArray.from(pixels),gl.drawingBufferWidth,gl.drawingBufferHeight);
    
    const w = imageData.width, h = imageData.height;
    const data = imageData.data;
    Array.from({length: h}, (val, i) => data.slice(i * w * 4, (i + 1) * w * 4))
            .forEach((val, i) => data.set(val, (h - i - 1) * w * 4));
    
    0 讨论(0)
  • If you're going to copy to a 2d canvas to flip you might as well skip the readPixels. Just use drawImage

    var dstX = 0;
    var dstY = 0;
    var dstWidth = ctx.canvas.width;
    var dstHeight = ctx.canvas.height;    
    ctx.drawImage(gl.canvas, dstX, dstY, dstWidth, dstHeight);
    

    The browser will do the right thing and the result will be right side up.

    Example:

    var gl = document.querySelector("#webgl").getContext("webgl");
    var ctx = document.querySelector("#two_d").getContext("2d");
    
    // fill webgl canvas with red on top and blue on bottom
    gl.enable(gl.SCISSOR_TEST);
    for (var y = 0; y < 15; ++y) {
      var v = y / 14;
      gl.scissor(0, y * 10, 300, 10);
      gl.clearColor(v, 0, 1 - v, 1);
      gl.clear(gl.COLOR_BUFFER_BIT);
    }
    
    // copy it to 2d canvas
    var dstX = 0;
    var dstY = 0;
    var dstWidth = ctx.canvas.width;
    var dstHeight = ctx.canvas.height;
    ctx.drawImage(gl.canvas, dstX, dstY, dstWidth, dstHeight);
    canvas { 
      margin: 1em;
      border: 1px solid black;
    }
    <canvas id="webgl"></canvas>
    <canvas id="two_d"></canvas>

    If you really did want to call gl.readPixels for some reason (you had no intent of every putting them in a 2d canvas, then you can just flip the bytes

    var width = gl.drawingBufferWidth;
    var height = gl.drawingBufferHeight
    var pixels = new Uint8Array(width * height * 4);
    gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
    
    var halfHeight = height / 2 | 0;  // the | 0 keeps the result an int
    var bytesPerRow = width * 4;
    
    // make a temp buffer to hold one row
    var temp = new Uint8Array(width * 4);
    for (var y = 0; y < halfHeight; ++y) {
      var topOffset = y * bytesPerRow;
      var bottomOffset = (height - y - 1) * bytesPerRow;
    
      // make copy of a row on the top half
      temp.set(pixels.subarray(topOffset, topOffset + bytesPerRow));
    
      // copy a row from the bottom half to the top
      pixels.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow);
    
      // copy the copy of the top half row to the bottom half 
      pixels.set(temp, bottomOffset);
    }
    

    Example:

    var gl = document.querySelector("#webgl").getContext("webgl");
    var ctx = document.querySelector("#two_d").getContext("2d");
    
    // fill webgl canvas with red on top and blue on bottom
    gl.enable(gl.SCISSOR_TEST);
    for (var y = 0; y < 15; ++y) {
      var v = y / 14;
      gl.scissor(0, y * 10, 300, 10);
      gl.clearColor(v, 0, 1 - v, 1);
      gl.clear(gl.COLOR_BUFFER_BIT);
    }
    
    
    var width = gl.drawingBufferWidth;
    var height = gl.drawingBufferHeight
    var pixels = new Uint8Array(width * height * 4);
    gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
    
    var halfHeight = height / 2 | 0;  // the | 0 keeps the result an int
    var bytesPerRow = width * 4;
    
    // make a temp buffer to hold one row
    var temp = new Uint8Array(width * 4);
    for (var y = 0; y < halfHeight; ++y) {
      var topOffset = y * bytesPerRow;
      var bottomOffset = (height - y - 1) * bytesPerRow;
    
      // make copy of a row on the top half
      temp.set(pixels.subarray(topOffset, topOffset + bytesPerRow));
    
      // copy a row from the bottom half to the top
      pixels.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow);
    
      // copy the copy of the top half row to the bottom half 
      pixels.set(temp, bottomOffset);
    }
    
    // This part is not part of the answer. It's only here
    // to show the code above worked
    // copy the pixels in a 2d canvas to show it worked
    var imgdata = new ImageData(with, height);
    imgdata.data.set(pixels);
    ctx.putImageData(imgdata, 0, 0);
    canvas { 
      margin: 1em;
      border: 1px solid black;
    }
    <canvas id="webgl"></canvas>
    <canvas id="two_d"></canvas>

    0 讨论(0)
  • 2021-01-14 03:54

    I don't know a webgl way to flip readPixels, and I suspect there is indeed a way to "avoid the problem altogether", but since you seem to draw it on a 2DContext anyway, here is a way to flip your putImageData.

    Since putImageData is not affected by context's transforms, simply doing ctx.scale(1,-1); ctx.putImageData() won't work.

    You'll need to putImageData, then flip its transforms, before drawing the canvas on itself.

    Use globalCompositeOperation = 'copy' if you have transparency.

    function flip(imageData){
      var ctx = flipped.getContext('2d');
      flipped.width = imageData.width;
      flipped.height = imageData.height;
      // first put the imageData
      ctx.putImageData(imageData, 0,0);
      // because we've got transparency
      ctx.globalCompositeOperation = 'copy';
      ctx.scale(1,-1); // Y flip
      ctx.translate(0, -imageData.height); // so we can draw at 0,0
      ctx.drawImage(flipped, 0,0);
      // now we can restore the context to defaults if needed
      ctx.setTransform(1,0,0,1,0,0);
      ctx.globalCompositeOperation = 'source-over';
      }
    
    /* remaining is just for the demo */
    var ctx = normal.getContext('2d');
    var img = new Image();
    img.crossOrigin = 'anonymous';
    img.onload = getImageData;
    img.src = "https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png";
    
    function getImageData(){
      normal.width = this.width;
      normal.height = this.height;
      ctx.drawImage(this, 0,0);
      imageData = ctx.getImageData(0,0,this.width, this.height);
      flip(imageData);
      }
    canvas{border: 1px solid}
    body{background: ivory;}
    <canvas id="normal"></canvas>
    <canvas id="flipped"></canvas>

    0 讨论(0)
提交回复
热议问题