是否可以进行RGBA浮动和来回往返并读取WebGL GLSL ES 1.0中的像素? [英] Is it possible to do a RGBA to float and back round trip and read the pixels in WebGL GLSL ES 1.0?

查看:47
本文介绍了是否可以进行RGBA浮动和来回往返并读取WebGL GLSL ES 1.0中的像素?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在尝试每种可能的算法大约一周之后,我可能会在stackoverflow上使用每个先前的答案-没有结果,然后问这个问题.因此,请客气并帮助GLSL学徒.

I'm asking this question after about a week of trying every possible algorithm, and using probably every prior answer on stackoverflow - with no results. So, please be kind and help a GLSL apprentice.

我正在尝试

  1. 将RGBA转换为浮动
  2. 执行一些计算
  3. 将浮点转换为RGBA

在GPU内-最初的RGBA是通过javascript通过Float32到Uint8转换定义的.

inside the GPU - the initial RGBA is defined in javascript via Float32 to Uint8 conversion.

关键是能够在RGB分辨率过低的GPU中进行计算(GIS距离和类似距离)之后进行往返并读取浮点数.

The key thing is being able to do the round trip and read back the floats after doing calculations (GIS distances and similar) in the GPU, for which RGB has too low a resolution.

我想通过 gl.readPixels 将结果读回到javascript中.

I want to read the results back into javascript via gl.readPixels.

我开始怀疑它是否可能的原因是:

The reasons I start to doubt that it's even possible is:

  1. 如果我不执行RGBA =>float =>RGBA转换一切正常-我放入GPU的浮点数也是我浮出的浮点数-没有损失
  2. 如果我执行RGB =>float =>RGB转换,它也可以正常工作,所有麻烦都在于第四个组件的重要性最低或最高

请注意-我怀疑这在更高版本的GLSL上可以正常使用,但这是WebGL GLSL ES 1.0,它具有更多的约束和更少的功能,并且我怀疑写入纹理将对iOS产生约束-我需要.

Please be aware - I suspect this works ok with later versions of GLSL, but this is WebGL GLSL ES 1.0 with a lot more constraints and a lot fewer functionality, and I suspect writing to texture will be a constraint on iOS - which I need.

我已经考虑列出我在这里尝试过的所有代码-必要时可以这样做,但这似乎有些过分.

I have considered listing all the code I tried here - can do if necessary but it seemed like overkill.

很快就知道不可能已经足够了-我至少不会再把头撞在墙上了.

Knowing quickly it is not possible would be enough for now - I'd stop banging my head against the wall at least.

推荐答案

好吧,除非我重新编写代码,否则由于我无权将代码重新许可为CC-,因此无法发布完整答案堆栈溢出所要求的BY-SA-4.0

Well, unless I re-write the code I can't post the full answer because I don't have permission to re-license the code as CC-BY-SA-4.0 as required by Stack Overflow

但是,这里有一个MIT许可的GLSL代码段

But, there's an MIT licensed GLSL snippet here

https://github.com/mikolalysenko/glsl-read-float/blob/master/index.glsl

似乎可以在着色器中浮动并转换RGBA8颜色.

Which seems to work for taking a float in a shader and converting an RGBA8 color.

采用另一种方法,您可以使用多种方法将浮点数添加到着色器,均匀,变化,甚至从浮点纹理读取,包括iPhone AFAIK在内的大多数手机都支持.通常不支持仅过滤它们(不需要)并写入它们.

Going the other way you can use any number of ways to get floats in to a shader, uniforms, varyings, even reading from floating point textures is supported by most phones including iPhone AFAIK. Only filtering them (which you don't need) and writing to them is usually not supported.

这是一个测试

async function main() {
  const gl = document.createElement('canvas').getContext('webgl');
  gl.canvas.width = 1;
  gl.canvas.height = 1;
  gl.viewport(0, 0, 1, 1);

  const req = await fetch('https://cdn.jsdelivr.net/npm/glsl-read-float@1.1.0/index.glsl')
  const glslFn = await req.text();

  const vs = `
  void main() {
    gl_PointSize = 1.0;
    gl_Position = vec4(vec3(0), 1);
  }
  `;

  const fs = `
  precision highp float;
  uniform float value;

  ${glslFn}

  void main() {
    gl_FragColor = encode_float(value).abgr;
  }
  `;

  const prg = twgl.createProgram(gl, [vs, fs]);
  const vLoc = gl.getUniformLocation(prg, 'value');
  gl.useProgram(prg);

  function test(v) {
    gl.uniform1f(vLoc, v);
    gl.drawArrays(gl.POINTS, 0, 1);
    const f = new Float32Array(1);
    const u = new Uint8Array(f.buffer);
    gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, u);

    console.log(`in: ${(new Float32Array([v]))[0]}, out: ${f[0]}`);
  }

  test(123.456);
  test(0.00913);
  test(1000000.1);
  test(0.0000001);

}
main();

<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>

我要做的一件事是重新编写该函数,以便它以相反的顺序输出值,因此不需要麻烦.

One thing I'd do is re-write the function so it outputs the values in the opposite order so the swizzle is not needed.

ps:这是Safari的一个版本,因为Safari的WebGL支持被多种方式破坏

ps: here's a version for Safari because Safari's WebGL support is broken in multiple ways

async function main() {
  const gl = document.createElement('canvas').getContext('webgl');
  gl.canvas.width = 1;
  gl.canvas.height = 1;
  gl.viewport(0, 0, 1, 1);

  const req = await fetch('https://cdn.jsdelivr.net/npm/glsl-read-float@1.1.0/index.glsl')
  const glslFn = await req.text();
  
  const vs = `
  attribute vec4 position;
  void main() {
    gl_PointSize = 1.0;
    gl_Position = position;
  }
  `;

  const fs = `
  precision highp float;
  uniform float value;

  ${glslFn}

  void main() {
    gl_FragColor = encode_float(value).abgr;
  }
  `;

  const prg = twgl.createProgram(gl, [vs, fs]);
  const vLoc = gl.getUniformLocation(prg, 'value');
  gl.useProgram(prg);
  
  // if safari  
  {
    // Safari fails if you don't have at least one attribute.
    // meaning it fails the WebGL Conformance Tests and has for > 4 years
    // in one of many ways.
    gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
    gl.bufferData(gl.ARRAY_BUFFER, 1, gl.STATIC_DRAW);
    const pLoc = gl.getAttribLocation(prg, "position");
    gl.enableVertexAttribArray(pLoc);
    gl.vertexAttribPointer(pLoc, 1, gl.UNSIGNED_BYTE, false, 0, 0);
  }

  function test(v) {
    gl.uniform1f(vLoc, v);
    gl.drawArrays(gl.POINTS, 0, 1);
    const f = new Float32Array(1);
    const u = new Uint8Array(f.buffer);
    gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, u);

    console.log(`in: ${(new Float32Array([v]))[0]}, out: ${f[0]}`);
  }

  test(123.456);
  test(0.00913);
  test(1000000.1);
  test(0.0000001);

}
main();

<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>

我想我可以看到您可能希望如何将编码的RGBA纹理传递给另一个着色器,这样就无法传递FLOAT纹理,因此我尝试制作RGAB->浮点解码器.这似乎有效.不知道它是完全正确的还是存在什么精度问题

I guess I can see how you might want to pass your encoded RGBA texture to another shader so that passing in FLOAT textures isn't a solution so I tried to make a RGAB -> Float decoder. This seems to work. No idea if it's entirely correct or what precision issues there are

async function main() {
  const gl = document.createElement('canvas').getContext('webgl');
  gl.canvas.width = 1;
  gl.canvas.height = 1;
  gl.viewport(0, 0, 1, 1);

  const req = await fetch('https://cdn.jsdelivr.net/npm/glsl-read-float@1.1.0/index.glsl')
  const glslFn = await req.text();

  const vs = `
    attribute vec4 position;
    void main() {
     gl_PointSize = 1.0;
     // gl_Position = vec4(vec3(0), 1); 
     gl_Position = position; 
  }
  `;

  const fs = `
    precision highp float;
    uniform sampler2D tex;

   ${glslFn}
   
   // note: the 0.1s here an there are voodoo related to precision
   float decode_float(vec4 v) {
     vec4 bits = v * 255.0;
     float sign = mix(-1.0, 1.0, step(bits[3], 128.0));
     float expo = floor(mod(bits[3] + 0.1, 128.0)) * 2.0 +
                  floor((bits[2] + 0.1) / 128.0) - 127.0;
     float sig = bits[0] +
                 bits[1] * 256.0 +
                 floor(mod(bits[2] + 0.1, 128.0)) * 256.0 * 256.0;
     return sign * (1.0 + sig / 8388607.0) * pow(2.0, expo);
   }

   void main() {
     float value = decode_float(texture2D(tex, vec2(0)));
     gl_FragColor = encode_float(value).abgr;
   }
`;

  const prg = twgl.createProgram(gl, [vs, fs]);
  const vLoc = gl.getUniformLocation(prg, 'value');
  gl.useProgram(prg);

  // if safari  
  {
    // Safari fails if you don't have at least one attribute.
    // meaning it fails the WebGL Conformance Tests and has for > 4 years
    // in one of many ways.
    gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
    gl.bufferData(gl.ARRAY_BUFFER, 1, gl.STATIC_DRAW);
    const pLoc = gl.getAttribLocation(prg, "position");
    gl.enableVertexAttribArray(pLoc);
    gl.vertexAttribPointer(pLoc, 1, gl.UNSIGNED_BYTE, false, 0, 0);
  }

  gl.bindTexture(gl.TEXTURE_2D, gl.createTexture());

  function test(v) {
    const float = new Float32Array([v]);
    const data = new Uint8Array(float.buffer);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
    gl.drawArrays(gl.POINTS, 0, 1);
    const f = new Float32Array(1);
    const u = new Uint8Array(f.buffer);
    gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, u);

    log(`in: ${(new Float32Array([v]))[0]}, out: ${f[0]}`);
  }

  test(123.456);
  test(0.00913);
  test(1000000.1);
  test(0.0000001);
  test(-123.456);
  test(-0.00913);
  test(-1000000.1);
  test(-0.0000001);
  test(0.0);
  test(1/0);
  test(-1/0);
  test(1.175494351E-38),
  test(3.402823465E37),
  test(1+'foo');  // NaN
}

function log(...args) {
  const elem = document.createElement('pre');
  elem.textContent = args.join(' ');
  document.body.appendChild(elem);
}

main();

pre { margin: 0; }

<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>

注意:尽管ATM没有我所知道的常见的大型endian系统,但我没有处理任何endian问题.

note: I didn't deal with any endian issues though ATM there are no common big endian systems I know of.

这篇关于是否可以进行RGBA浮动和来回往返并读取WebGL GLSL ES 1.0中的像素?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆