Как получить немного от поплавка, используя GLSL на WebGL - PullRequest
2 голосов
/ 31 марта 2020

В веб-приложении с тремя Js у меня есть массив float32, который я передаю своему вершинному шейдеру в качестве атрибута bufferA с именем "state" моей геометрии. Как я могу получить n-й бит каждого числа с плавающей точкой в ​​вершинном шейдере? Мне нужно что-то вроде:

  vertexShader: `
  attribute float state;
  varying float active;

  void main() {
      active = getnthbit(state, 4); // get the fourth bit of the float value of state
     ...
 }`,...

Я проверил ответ Томми здесь: { ссылка } Используя эту формулу

float bit = step (0.5, mod (float (value) / 8.0, 1.0));

, чтобы получить третий бит, но он не работает, и я его не получаю.

1 Ответ

2 голосов
/ 31 марта 2020

В WebGL2 вы можете передавать целочисленные данные и выполнять целочисленную математику (со знаком и без знака) в шейдерах.

В WebGL1 вы действительно получаете числа с плавающей запятой, и даже вещи, помеченные как int, могут фактически не быть целыми числами. Таким образом, AFAIK вы можете получить только первые 23 бита, используя метод мода

. Вы можете реализовать getNthBit(float v, int bit) как

float getNthBit(float v, int bit) {
  return mod(floor((v + 0.5) / pow(2.0, float(bit))), 2.0);
}

или что-то в этом роде.

const gl = document.querySelector('canvas').getContext('webgl');
const vs = `
void main() {
  gl_PointSize = 150.0;
  gl_Position = vec4(0, 0, 0, 1);
}
`;
const fs = `
precision highp float;
uniform float offset;

float getNthBit(float v, int bit) {
  return mod(floor((v + 0.5) / pow(2.0, float(bit))), 2.0);
}

void main() {
  float v = gl_FragCoord.x + offset;
  int bit = int(gl_FragCoord.y);
  float nthBit = getNthBit(v, bit);
  
  gl_FragColor = vec4(0, nthBit > 0.0 ? 1.0 : 0.0, 0, 1);
}
`;

const prg = twgl.createProgram(gl, [vs, fs]);
const offLoc = gl.getUniformLocation(prg, 'offset');

gl.useProgram(prg);

let offset = 0;
function render(time) {
  offset += 150;
  gl.uniform1f(offLoc, offset);
  gl.drawArrays(gl.POINTS, 0, 1);  // draw 1 point
  requestAnimationFrame(render);
}

requestAnimationFrame(render);
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas 
    width="150"
    height="32"
    style="
      width: 450px;
      height: 192px;
      image-rendering: pixelated;
      border: 1px solid black;
    "></canvas>

Обратите внимание, что если вы просто хотите извлекать каждый бит, как будто это двоичные данные, вы можете просто объявить его как vec2 UNSIGNED_SHORT, так что даже если вы поставите float32s в буфер, вы читаете их как UNSIGNED_SHORT

, а затем используете что-то вроде

float getNthBit(vec2 v, int bit) {
  float t = bit < 16 ? v[0] : v[1];
  int b = bit < 16 ? bit : bit - 16;
  return mod(floor((t + 0.5) / pow(2.0, float(b))), 2.0);
}

Пример

const gl = document.querySelector('canvas').getContext('webgl');
const vs = `
attribute vec2 v;
varying vec2 v_v;
void main() {
  gl_PointSize = 150.0;
  gl_Position = vec4(0, 0, 0, 1);
  v_v = v;
}
`;
const fs = `
precision highp float;
varying vec2 v_v;

float getNthBit(vec2 v, int bit) {
  float t = bit < 16 ? v[0] : v[1];
  int b = bit < 16 ? bit : bit - 16;
  return mod(floor((t + 0.5) / pow(2.0, float(b))), 2.0);
}

void main() {
  int bit = 31 - int(gl_FragCoord.x / 4.0);
  float nthBit = getNthBit(v_v, bit);  
  gl_FragColor = vec4(0, nthBit > 0.0 ? 1.0 : 0.0, 0, 1);
}
`;

const prg = twgl.createProgram(gl, [vs, fs]);
const vLoc = gl.getAttribLocation(prg, 'v');

const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf, gl.STATIC_DRAW);
gl.enableVertexAttribArray(vLoc);
gl.vertexAttribPointer(vLoc, 2, gl.UNSIGNED_SHORT, false, 0, 0);

gl.useProgram(prg);

const data = new Float32Array(1);
const bits = new Uint32Array(data.buffer);
const inputElem = document.querySelector('input');
const codeElem = document.querySelector('pre');

function render() {
  data[0] = inputElem.value;
  codeElem.textContent = bits[0].toString(2).padStart(32, '0');
  gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
  gl.drawArrays(gl.POINTS, 0, 1);  // draw 1 point
}
render();
inputElem.addEventListener('input', render);


...