gl.texImage2D работает с Image, но не с ImageData - PullRequest
0 голосов
/ 03 июня 2018

У меня есть текстура, которая корректно отображается, когда я использую gl.texImage2D с объектом Image javascript для указания данных пикселей.Но если я рендую Image на холст за пределами экрана, затем вытаскиваю из него объект ImageData, а затем вместо этого передаю его gl.texImage2D, текстура отрисовывается неправильно.Когда я проверяю объект ImageData, данные о пикселях верны.Что может gl.texImage2D делать с объектом Image иначе, чем с объектом ImageData?

Изображение просто 32-битное, 8-битное на компонент, RGBA.Мой фрагментный шейдер получает и ожидает эти компоненты как значения в [0,1].Может ли быть так, что когда я передаю объект ImageData, мой шейдер теперь получает их в диапазоне [0,255]?Я должен проверить эту гипотезу.В любом случае, я в полной растерянности.

РЕДАКТИРОВАТЬ: После долгих трудов, я пришел к выводу, что нет ничего плохого в OpenGL и шейдерных конвейерах.Проблема, с которой я сталкиваюсь, связана с преобразованием из Image в ImageData.Вот некоторый код ...

let perm_image = new Image();
...
let canvas = document.createElement('canvas');
let context = canvas.getContext('2d');
canvas.width = perm_image.width;
canvas.height = perm_image.height;
context.drawImage(perm_image, 0, 0);
let perm_image_data = context.getImageData(0, 0, perm_image.width, perm_image.height);

Я сравнил возвращенные данные изображения с фактическими данными изображения, и хотя первая часть буфера выглядела правильно, остальная часть была неправильной.Я делал эту проверку раньше, но остановился слишком рано.

Во всяком случае, я собираюсь осмотреться и посмотреть, как я могу изменить / исправить вышеуказанный код.

ЗАКЛЮЧИТЕЛЬНОЕ РЕДАКТИРОВАНИЕ: Я 'мы решили сдаться.Я не знаю ни одного пуленепробиваемого способа получения необработанных, неизмененных данных RGBA из объекта JS Image, который, я думаю, является полным провалом со стороны тех, кто изобрел этот проклятый объект.То, что я собираюсь сделать, это просто не использовать файлы PNG, а мой собственный двоичный формат.

1 Ответ

0 голосов
/ 03 июня 2018

Кажется, работает на меня.Может быть, сравните и посмотрите, что вы делаете по-другому?

const img = new Image();
img.addEventListener('load', render);
img.crossOrigin = "";
img.src = "https://i.imgur.com/ZKMnXce.png";

function render() {
  const vs = `
  attribute vec4 position;
  void main() {
    gl_PointSize = 80.0;
    gl_Position = position;
  }
  `;

  const fs = `
  precision mediump float;
  uniform sampler2D tex;
  void main() {
    gl_FragColor = texture2D(tex, gl_PointCoord);
  }
  `;

  const gl = document.querySelector("canvas").getContext("webgl");
  const program = twgl.createProgram(gl, [vs, fs]);
  const positionLoc = gl.getAttribLocation(program, "position");
  
  const ctx = document.createElement("canvas").getContext("2d");
  ctx.canvas.width = img.width;
  ctx.canvas.height = img.height;
  ctx.fillStyle = '#FFF'
  ctx.drawImage(img, 0, 0);
  ctx.font = "bold 48px sans-serif";
  ctx.fillText("imageData", 10, 200);

  const imgData = ctx.getImageData(0, 0, img.width, img.height);

  ctx.drawImage(img, 0, 0);
  ctx.fillText("canvas", 10, 200);

  gl.useProgram(program);

  createTextureAndDraw(img, -.6);
  createTextureAndDraw(ctx.canvas, 0);
  createTextureAndDraw(imgData, .6);
  
  function createTextureAndDraw(src, x) {
  
    const tex = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, tex);
    {
      const level = 0;
      const internalFormat = gl.RGBA;
      const width = 1;
      const height = 1;
      const border = 0;
      const format = gl.RGBA;
      const type = gl.UNSIGNED_BYTE;
      gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
                    format, type, src);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    }
    
    gl.vertexAttrib1f(positionLoc, x);

    const primitiveType = gl.POINTS;
    const offset = 0;
    const count = 1;
    gl.drawArrays(primitiveType, offset, count);
  }
}
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>

Читая обновления вашего вопроса, вы, вероятно, должны открыть новый вопрос, поскольку ваша реальная проблема вовсе не связана с заданным вами вопросом.

Во-первых, Canvas ВСЕГДА ПРЕОБРАЗОВАННЫЙ .Это означает, что это с потерями.

Это подробно описано в этом ответе: https://stackoverflow.com/a/50566789/128511

Так как вы собираетесь использовать Image (png) -> Canvas -> ImageData, тогда, если ваш PNG имеет не 255альфа, вы потеряете данные.

Во-вторых, вы упомянули гамма-коррекцию.Да, браузер будет корректировать гамму и / или применять другие преобразования цветов к изображениям.Это делается для того, чтобы изображения, нарисованные в WebGL, соответствовали изображениям, нарисованным в Canvas, и изображениям, отображаемым на странице.

Чтобы избежать преобразования цвета, вам нужно установить gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE), который говорит WebGL делать все, что нужно, чтобы получить исходные данные из изображения (что может включать в себя повторное декодирование изображения с нуля, так как изображение по умолчанию)Данные - это любой формат, который необходим браузеру для рисования тегов изображений в HTML).

Вот пример неприменения преобразования цвета.Создает 2 текстуры из одного изображения.Один раз с преобразованием цветов по умолчанию, один раз без.

const img = new Image();
img.addEventListener('load', render);
img.crossOrigin = "";
img.src = "https://cdn.rawgit.com/KhronosGroup/WebGL/8ea92581/sdk/tests/resources/small-square-with-colorspin-profile.png";

function render() {
  const vs = `
  attribute vec4 position;
  void main() {
    gl_PointSize = 130.0;
    gl_Position = position;
  }
  `;

  const fs = `
  precision mediump float;
  uniform sampler2D tex;
  void main() {
    gl_FragColor = texture2D(tex, gl_PointCoord);
  }
  `;

  const gl = document.querySelector("canvas").getContext("webgl");
  const program = twgl.createProgram(gl, [vs, fs]);
  const positionLoc = gl.getAttribLocation(program, "position");
  
  gl.useProgram(program);

  createTextureAndDraw(img, -.5, true);   // default (with COLOR CONVERSION)
  createTextureAndDraw(img,  .5, false);  // color conversion OFF
  
  function createTextureAndDraw(src, x, colorConversion) {
  
    const tex = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, tex);
    const mode = colorConversion ? gl.BROWSER_DEFAULT_WEBGL : gl.NONE;
    gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, mode);
    {
      const level = 0;
      const internalFormat = gl.RGBA;
      const width = 1;
      const height = 1;
      const border = 0;
      const format = gl.RGBA;
      const type = gl.UNSIGNED_BYTE;
      gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
                    format, type, src);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    }
    
    gl.vertexAttrib1f(positionLoc, x);

    const primitiveType = gl.POINTS;
    const offset = 0;
    const count = 1;
    gl.drawArrays(primitiveType, offset, count);
  }
}
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>

Примечание: это крайний пример, так как текстура красная с преобразованием цвета, а синяя без.Если вы откроете файл в фотошопе, вы увидите, что необработанные данные отображаются синим цветом, но в зависимости от цветового профиля они отображаются красным цветом.

enter image description here

В тесте на соответствие WebGL для этой функции есть несколько крайних примеров , чтобы проверить, что вы можете получить необработанные данные из PNG в WebGL.

...