简体   繁体   English

WebGL:将8位灰度图像加载到纹理时图像无效

[英]WebGL: Invalid Image when loading 8-bit grayscale image to texture

I am trying to render an 8-bit grayscale JPG downloaded from my server into a texture to render with WebGL. 我试图将从服务器下载的8位灰度JPG渲染为纹理,以使用WebGL渲染。

// Pretend for the sake of example that I have passed in an argument 
// 'img' that is an Image object that I used to download the jpg.
// See the EDIT below for how this happens...

// ...also presume that I have set up my WebGL context and assigned it 
// to the variable 'gl':
// const gl = canvas.getContext('experimental-webgl');

const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);

// Set the parameters so we can render any size image.
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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

// Upload the image into the texture.
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, gl.LUMINANCE, gl.UNSIGNED_BYTE, img);

At this point, WebGL slaps me in the face and takes my lunch money: 此时,WebGL拍了我一巴掌,拿走了我的午餐钱:

WebGL: INVALID_VALUE: texImage2D: invalid image

and I am left to ask: WHAT MOAR U WANT WebGL?? 然后我问:WebGL到底有什么用? WHY NOT U JUST B HAPPY??? 为什么您不快乐?

I'm probably missing something basic, but the internets are a mess with WebGL stuff that doesn't answer my question. 我可能缺少一些基本的知识,但是Internet上杂乱无章的WebGL内容无法回答我的问题。 Can any WebGL gurus here advise? 这里有任何WebGL专家可以提出建议吗?

Here is the image: 这是图片:

8位灰度JPG 4LYFE!

EDIT: 编辑:

Here are some more details about how the image is obtained. 以下是有关如何获取图像的更多详细信息。 Note: the reason for doing it this way is that I need a download queue that I can shuffle, cancel, and restart, ad nauseam. 注意:采用这种方式的原因是,我需要一个下载队列,可以随机播放,取消和重新启动广告。 Hence the use of fetch, blobs, and object URLs. 因此,使用访存,blob和对象URL。 This whole thing is part of a deep-zoom image viewer that should efficiently load only the image tiles visible in the viewport (which often changes moment to moment). 这整个过程是深度缩放图像查看器的一部分,该视图应仅有效加载视口中可见的图像图块(该图块经常会随时变化)。 Anyway, here's how I get the image: 无论如何,这是我获取图像的方式:

// Take care of vendor prefixes:
window.URL = window.URL || window.webkitURL;

// This is where we'll assign our final product
const imageTag = new Image();

imageTag.onerror = (err) => {
  console.error('IMG ERROR:', err);
};
imageTag.onload = () => {
  // Once the image tag has loaded the blob data, clean up our object url:
  window.URL.revokeObjectURL(imageTag.src);

  // imageTag is now passed into the code above as 'img'
};

// Grab the image data as a blob
fetch(`<url_of_image>`)
  .then(res => res.blob())
  .then(blob => {
    // When we get the image data, wrap it in an object url and assign
    // it to src. The onload event won't fire until the next tick.
    imageTag.src = window.URL.createObjectURL(blob);
  });

I should point out that I was originally rendering using the canvas2D API, and that it was able to draw the images just fine using the drawImage() method. 我应该指出,我最初是使用canvas2D API进行渲染的,并且能够使用drawImage()方法很好地绘制图像。 So I'm pretty sure the images objects are loading ok. 所以我很确定图像对象可以正常加载。 My reason for implementing a WebGL renderer is to open the door to greater performance enhancements. 实现WebGL渲染器的原因是为提高性能提供了方便。

Thanks for looking! 感谢您的光临!

From the search through Chromium codebase for the error message string and looking at revokeObjectURL reference it seems that it is the culprit. 从Chromium代码库中搜索错误消息字符串并查看revokeObjectURL参考,看来这是罪魁祸首。 The image content represented by image URL simply gets "freed" to early. 由图像URL表示的图像内容只是“释放”到早期。 The solution would be to call revokeObjectURL after calling texImage2D . 解决方案是在调用revokeObjectURL 之后调用texImage2D

Alright party people, I figured this out (one way to do it at least). 好的聚会人士,我想出了这一点(至少是一种方法)。

The original flow, which I could never get to work, was: 我无法上班的原始流程是:

  1. Fetch image data as blob 提取图像数据为Blob
  2. Use ObjectURL to be able to pass blob to image tag on src attribute 使用ObjectURL能够将blob传递给src属性上的图片标签
  3. Pass image tag into texImage2D (old method signature) 将图像标签传递到texImage2D (旧方法签名)

The new flow that worked for me is: 对我有用的新流程是:

  1. Fetch image data as array buffer 获取图像数据作为数组缓冲区
  2. Decode jpg data into Uint8Array using jpeg-js 使用jpeg-js将 jpg数据解码为Uint8Array
  3. Pass Uint8Array into texImage2D (new method signature) texImage2D传递到texImage2D (新方法签名)

It looks like this: 看起来像这样:

// I am using the decode() method from jpeg-js. Passing true as a second parameter 
// causes the data to be returned wrapped in a Uint8Array. Even though the data
// was one channel going in, it's going to come out with all four (RGBA) channels.

import { decode } from 'jpeg-js';

// Like before, please presume a global gl variable to be my WebGL context.
const gl = '<my-webgl-context-that-I-set-up-elsewhere>';

fetch(`<url_of_image>`)
  .then(res => res.arrayBuffer())
  .then(buffer => {
    // When we get the image data, it's still encoded. We need to decode it
    // before we've got pixel data. We want it as a Uint8Array which will make
    // texImage2D() happy.
    const pixels = decode(buffer, true);

    // Set up a new texture where we'll capture and keep the image data
    const texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, texture);

    // Set the parameters so we can render any size image.
    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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

    // Upload the image into the texture:

    // Here we are telling WebGL how many bytes each pixel will be (I think 
    // think the default is 4, but I thought that this step was important to show).
    gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4);

    // jpeg-js is kind enough to provide the dimensions of the image data in its
    // returned value. Since jpeg-js returns the data with all 4 channels, we use
    // gl.RGBA here.
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, pixels.width, pixels.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels.data);

    // Ready to draw!
  });

Hopefully this is of use to someone in the future. 希望这对将来的某人有用。 Thanks to everyone who contributed thoughts and ideas, and thanks to the collaborators on jpeg-js! 感谢所有贡献思想和想法的人,也感谢jpeg-js上的合作者!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM