Webgl 每像素 1 位纹理

Posted

技术标签:

【中文标题】Webgl 每像素 1 位纹理【英文标题】:Webgl 1 bit per pixel texture 【发布时间】:2015-05-29 07:12:34 【问题描述】:

有没有办法在 WebGL 中使用每像素 1 位的纹理数据(例如 32 字节数据中的纹理 16×16px)或者是否需要先将数据从 1bpp 解包到 8bpp?

我找到了类似的 OpenGL 相关问题和答案https://***.com/a/15706596/4540236,但在我看来,WebGL 没有 GL_BITMAP 数据类型常量。

【问题讨论】:

您可以将 8 个位图存储在一个 8 位纹理中,但是您必须手动进行插值。 谢谢你的建议,但大多数时候只有一个1bpp的纹理(用于医学图像叠加) 【参考方案1】:

WebGL 本身没有每像素一位的格式。您必须自己解压缩数据。

您可以使用gl.ALPHAgl.LUMINANCE 格式将其解压缩为每像素1 个字节

您可以尝试创建一个片段着色器来解包,不知道精度问题是否会杀死您。类似的东西

precision mediump float;

varying vec2 v_texcoord;
uniform vec2 u_textureSize;
uniform sampler2D u_texture;

void main() 
  float texelCoord = floor(v_texcoord.x * u_textureSize.x); 
  float bit  = mod(texelCoord, 8.0);
  float byte = texelCoord / 8.0;
  vec2 uv = vec2(byte / u_textureSize.x, v_texcoord.y);
  float eightPixels = texture2D(u_texture, uv).r * 255.0;
  float pixel = mod(floor(eightPixels / pow(2.0, bit)), 2.0);
  gl_FragColor = vec4(pixel, pixel, pixel, 1.0);

嗯,我想我们应该测试一下......

// Using TWGL.js because it's easier and I'm lazy
var gl = twgl.getWebGLContext(document.getElementById("c"));
var programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);

var arrays = 
  position: [-1, -1, 0, 1, -1, 0, -1, 1, 0, -1, 1, 0, 1, -1, 0, 1, 1, 0],
;
var bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

function render(time) 
  twgl.resizeCanvasToDisplaySize(gl.canvas);
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

  var uniforms = 
    u_texture: twgl.createTexture(gl, 
      format: gl.LUMINANCE,
      min: gl.NEAREST,
      mag: gl.NEAREST,
      width: 1,
      src: [ 0x3C, 0x42, 0xBD, 0x81, 0xA5, 0x81, 0x42, 0x3C, ],
    ),
    u_textureSize: [8, 8],
  ;

  gl.useProgram(programInfo.program);
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
  twgl.setUniforms(programInfo, uniforms);
  console.log("foo");
  twgl.drawBufferInfo(gl, gl.TRIANGLES, bufferInfo);

render();
canvas  border: 1px solid red; 
<script src="//twgljs.org/dist/twgl-full.min.js"></script>
<canvas id="c"></canvas>
  <script id="vs" type="notjs">
attribute vec4 position;
varying vec2 v_texcoord;
void main() 
  gl_Position = position;
  v_texcoord = position.xy * 0.5 + 0.5;

  </script>
  <script id="fs" type="notjs">
    precision mediump float;

    varying vec2 v_texcoord;
    uniform vec2 u_textureSize;
    uniform sampler2D u_texture;
    
    void main() 
      float texelCoord = floor(v_texcoord.x * u_textureSize.x); 
      float bit  = mod(texelCoord, 8.0);
      float byte = texelCoord / 8.0;
      vec2 uv = vec2(byte / u_textureSize.x, v_texcoord.y);
      float eightPixels = texture2D(u_texture, uv).r * 255.0;
      float pixel = mod(floor(eightPixels / pow(2.0, bit)), 2.0);
      gl_FragColor = vec4(pixel, pixel, pixel, 1.0);
    
</script>

【讨论】:

感谢您的解决方案。我已经通过将数据解包到每个像素 1 个字节来解决它。片段着色器解决方案听起来不错,但它只适用于 NEAREST 过滤,不适用于我需要的 LINEAR。 嗯,你可以在着色器中进行双线性过滤。拉出4个像素。但是你不会得到 mips :(【参考方案2】:

正如 gman 回答的 (https://***.com/a/30529136/4540236):WebGL 不支持每像素一位格式。

所以我使用了 LUMINANCE 格式并手动解压数据:

int off = 0;
for (int i=0;i<packedData.length;i++,off+=8) 
  int val = packedData[i];
  for (int j=0;j<8;j++) 
    unpackedData[off+j] = ((val&(1<<j))!=0)?255:0;        
  

【讨论】:

以上是关于Webgl 每像素 1 位纹理的主要内容,如果未能解决你的问题,请参考以下文章

在 webgl 的大纹理中更改 20 到 200 个单独像素的最佳方法?

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

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

像素和颜色 - WebGL

计算机图形学基于WebGL的纹理贴图

webgl 纹理