为什么我从WebGLRenderingContext.readPixels()获得的imageData颠倒了?

我尝试做以下事情:

var gl = renderer.domElement.getContext("webgl")

var pixels = new Uint8Array(gl.drawingBufferWidth * gl.drawingBufferHeight * 4);
gl.readPixels(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels);

var imageData = new ImageData(Uint8ClampedArray.from(pixels), gl.drawingBufferWidth, gl.drawingBufferHeight);

ctx.putImageData(imageData, 0, 0);

但结果是沿x轴镜像的图像(即:上下翻转)。

我还尝试过在ctx.putImageData之后使用scale,如下所示:
ctx.scale(1, -1);

但是没有结果。反转像素也不起作用。

到现在为止,我了解到putImageData()使用从左上角开始的坐标,而readPixels()从左下角开始。

是否有人对如何翻转图像或完全避免问题有任何建议?

最佳答案

如果要复制到2D Canvas 上进行翻转,则不妨跳过readPixels。只需使用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);

浏览器将执行正确的操作,结果将正确显示。

例子:

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>


如果您确实确实出于某种原因想要调用gl.readPixels(您无意将它们放入2d Canvas 中,那么您只需翻转字节即可
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);
}

例子:

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>

10-08 04:31