В 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);