Reputation: 705
On a web app with threeJs I have a float32array that I pass to my vertex shader as a bufferAttribute named "state" of my Geometry. How can I get the nth bit of each float in the vertex shader ? I need something like :
vertexShader: `
attribute float state;
varying float active;
void main() {
active = getnthbit(state, 4); // get the fourth bit of the float value of state
...
}`,...
I checked the answer of Tommy here : https://stackoverflow.com/a/41946046/3548345 Using this formulat
float bit = step(0.5, mod(float(value) / 8.0, 1.0));
to get the third bit, but it doesn't work and I don't get it.
Upvotes: 2
Views: 1935
Reputation:
In WebGL2 you can pass integer data and do integer math (signed and unsigned) in shaders.
In WebGL1 you really get floats and even things marked as an int may actually not be ints. As such AFAIK you can really only get the first 23 bits using the mod method
You could implement getNthBit(float v, int bit)
as
float getNthBit(float v, int bit) {
return mod(floor((v + 0.5) / pow(2.0, float(bit))), 2.0);
}
or something like that.
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>
Note that if you just want to pull out every bit as though it's binary data then you could just declare it as a vec2 of UNSIGNED_SHORT so even though you put float32s into the buffer you read them as UNSIGNED_SHORT
and then use something like
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);
}
Example
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);
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas
width="128"
height="4"
style="
width: 256px;
height: 8px;
image-rendering: pixelated;
border: 1px solid black;
display: block;
"></canvas>
<pre></pre>
<input type="number" value="13445">
Upvotes: 3