WebGL - 当我从帧缓冲区读取像素数据时,图标失去透明度

Posted

技术标签:

【中文标题】WebGL - 当我从帧缓冲区读取像素数据时,图标失去透明度【英文标题】:WebGL - When I read pixel data from frame buffer, icon loses its transparency 【发布时间】:2021-01-14 17:28:38 【问题描述】:

我有一个透明的图标图。 256x256

我使用这张图片创建了一个纹理。

    AddIconMap(imageUrl) 
        const image = new Image()
        const  gl  = this
        image.onload = () => 
            const iconMapTexture = gl.createTexture()
            gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false)
            gl.bindTexture(gl.TEXTURE_2D, iconMapTexture)
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image)
            gl.generateMipmap(gl.TEXTURE_2D)

            gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
            gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
            gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
            gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
            gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true)
            this.mapTexture.push(iconMapTexture) 
            window.URL.revokeObjectURL(image.src)
        
        image.crossOrigin = ''
        image.src = imageUrl
    

然后,我想在此纹理中获取最后一个小图标并将其绘制在画布上。画布的高度和宽度为 256px。每个small的高宽都是32px。

     var frameBuffer = gl.createFramebuffer();
     gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer)
     gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.Sphere.mapTexture[0], 0)
     const pixel = new Uint8Array(4 * 32 * 32)
     gl.readPixels(224, 224, 32, 32, gl.RGBA, gl.UNSIGNED_BYTE, pixel)
     var imageData = this.ctx.createImageData(32, 32)
     imageData.data.set(pixel)
     this.ctx.putImageData(imageData, offset.x, offset.y, 0, 0, 32, 32)

我在画布上绘制了图标,但图标不透明。我想,也获取背景图片的像素,但我不想要这个。

有什么想法吗?我该怎么办?

【问题讨论】:

【参考方案1】:

检查

const gl = document.createElement('canvas').getContext('webgl');
const r = max => Math.random() * max;

class Foo 
  constructor(gl) 
    this.mapTexture = [];
    this.gl = gl;
  
  AddIconMap(imageUrl) 
    const image = new Image()
    const 
      gl
     = this
    image.onload = () => 
      const iconMapTexture = gl.createTexture()
      gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false)
      gl.bindTexture(gl.TEXTURE_2D, iconMapTexture)
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image)
      gl.generateMipmap(gl.TEXTURE_2D)

      gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
      gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
      gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
      gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
      gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true)
      this.mapTexture.push(iconMapTexture)
      window.URL.revokeObjectURL(image.src)
    
    image.crossOrigin = ''
    image.src = imageUrl
  


class Bar 
  constructor() 
    this.ctx = document.querySelector('canvas').getContext('2d');
    this.Sphere = new Foo(gl);
    this.Sphere.AddIconMap('https://i.imgur.com/1zS9of5.png');

  
  drawInReallyStrangeWay(offset) 
    var frameBuffer = gl.createFramebuffer();
    gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer)
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.Sphere.mapTexture[0], 0)
    const pixel = new Uint8Array(4 * 32 * 32)
    gl.readPixels(224, 224, 32, 32, gl.RGBA, gl.UNSIGNED_BYTE, pixel)
    var imageData = this.ctx.createImageData(32, 32)
    imageData.data.set(pixel)
    this.ctx.putImageData(imageData, offset.x, offset.y, 0, 0, 32, 32)
  



const b = new Bar();
setInterval(() => 
  if (b.Sphere.mapTexture.length) 
    b.drawInReallyStrangeWay(
      x: r(300 - 32),
      y: r(150 - 32)
    );
  
, 200);
canvas 
  background-color: #C0C0C0;
  background-image: linear-gradient(45deg, #808080 25%, transparent 25%), linear-gradient(-45deg, #808080 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #808080 75%), linear-gradient(-45deg, transparent 75%, #808080 75%);
  background-size: 20px 20px;
  background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
<canvas></canvas>

它对我有用。目前尚不清楚为什么要使用 WebGL 加载纹理,然后使用 readPixels 将数据从纹理中提取出来,并将其放入 2D 画布中而不是 drawing it with WebGL。但是,putImageData 替换了画布中的数据。它不混合。如果你想混合,那么你需要使用drawImage 在这种情况下使用 WebGL 根本没有意义,只需加载图像并绘制你想要的部分

const r = max => Math.random() * max;

class Foo 
  constructor() 
    this.mapTexture = [];
  
  AddIconMap(imageUrl) 
    const image = new Image()
    image.onload = () => 
      this.mapTexture.push(image)
    
    image.crossOrigin = ''
    image.src = imageUrl
  


class Bar 
  constructor() 
    this.ctx = document.querySelector('canvas').getContext('2d');
    this.Sphere = new Foo();
    this.Sphere.AddIconMap('https://i.imgur.com/1zS9of5.png');

  
  draw(offset) 
    const image = b.Sphere.mapTexture[0];
    this.ctx.drawImage(
        image, 
        224, 224, 32, 32,            // src rect
        offset.x, offset.y, 32, 32,  // dst rect
    );
  


const b = new Bar();
setInterval(() => 
  if (b.Sphere.mapTexture.length) 
    b.draw(
      x: r(300 - 32),
      y: r(150 - 32)
    );
  
, 200);
canvas 
  background-color: #C0C0C0;
  background-image: linear-gradient(45deg, #808080 25%, transparent 25%), linear-gradient(-45deg, #808080 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #808080 75%), linear-gradient(-45deg, transparent 75%, #808080 75%);
  background-size: 20px 20px;
  background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
<canvas></canvas>

【讨论】:

谢谢,但我不想使用该图像。如果我不撤销图像,我就会有记忆问题。我需要在创建纹理后删除图像,所以我必须从帧缓冲区读取像素数据并在画布上绘制。

以上是关于WebGL - 当我从帧缓冲区读取像素数据时,图标失去透明度的主要内容,如果未能解决你的问题,请参考以下文章

WebGL - 从渲染缓冲区读取像素数据

从帧缓冲区对象读取时,OpenGL 纹理坐标是相反的

Framebuffer 和 Renderbuffer 的 Webgl 绑定

WebGL 帧缓冲区 ClearColor 仅影响 (0,0) 像素

将 WebGL 2 中的像素读取为浮点值

读取 WebGLTexture 中的像素(将 WebGL 渲染到纹理)