包括 Float32Arrays 在内的类型化数组只是压缩数组(想想 C/C++)。更新它们是即时的。如果您想在 GPU 上查看数据,则必须使用 texImage2D 再次上传数据,否则,没有魔法,没有疯狂的缓冲,非常简单。如果您了解 C/C++,它在功能上等同于
void* arrayBuffer = malloc(sizeOfBuffer);
float* viewAsFloat = (float*)arrayBuffer;
类型化数组不是 JS 数组的视图。使用原生 JS 数组来初始化类型化数组只是初始化类型化数组的一种便捷方式。一旦创建,TypedArray 就是一个新数组。
你可以在同一个 ArrayBuffer 中获取多个 ArrayBuffer 视图。
例子
var b = new ArrayBuffer(16); // make a 16 byte ArrayBuffer
var bytes = new Uint8Array(b); // make a Uint8Array view into ArrayBuffer
var longs = new Uint32Array(b); // make a Uint32Array view into ArrayBuffer
var floats = new Float32Array(b); // make a Float32Array view into ArrayBuffer
// print the contents of the views
console.log(bytes);
console.log(longs);
console.log(floats);
// change a byte using one of the views
bytes[1] = 255;
// print the contents again
console.log(bytes);
console.log(longs);
console.log(floats);
将所有代码复制并粘贴到您的 JavaScript 控制台中。您应该会看到类似
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[65280, 0, 0, 0]
[9.147676375112406e-41, 0, 0, 0]
注意:在同一个数组缓冲区上使用不同类型的多个视图不是跨平台兼容的。换句话说,你会在大端平台和小端平台上得到不同的结果。目前还没有流行的大端平台支持 TypedArrays 的浏览器,所以你可以忽略这个问题,尽管你的页面可能会在未来的某个平台上中断。如果您想以独立于平台的方式读取/写入数据,您应该使用DataView。否则,在同一个缓冲区上使用多个视图的主要目的是上传打包的顶点数据,例如用 uint32 RGBA 颜色打包的浮点位置。在这种情况下,它将跨平台工作,因为您不会使用视图读取/写入相同的数据。
正如所指出的,JS 原生数组和 TypedArrays 没有关系,只是你可以使用 JS 原生数组来初始化 TypedArray
var jsArray = [1, 2, 3, 4];
var floats = new Float32Array(jsArray); // this is a new array, not a view.
console.log(jsArray);
console.log(floats);
jsArray[1] = 567; // effects only the JS array
console.log(jsArray);
console.log(floats);
floats[2] = 89; // effects only the float array
console.log(jsArray);
console.log(floats);
我得到的粘贴到控制台
[1, 2, 3, 4]
[1, 2, 3, 4]
[1, 567, 3, 4]
[1, 2, 3, 4]
[1, 567, 3, 4]
[1, 2, 89, 4]
请注意,您可以从任何类型的数组中获取底层 ArrayBuffer。
var buffer = floats.buffer;
并创建新视图
var longs = new Uint8Array(buffer);
console.log(longs);
// prints [0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 178, 66, 0, 0, 128, 64]
您还可以创建覆盖部分缓冲区的视图。
var offset = 8; // Offset is in bytes
var length = 2; // Length is in units of type
// a buffer that looks at the last 2 floats
var f2 = new Float32Array(buffer, offset, length);
console.log(f2);
// prints [89, 4]
至于纹理和类型化数组,这里有一个 sn-p,它使用 Float32Array 来更新浮点纹理。
main();
function main() {
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl");
if (!gl) {
alert("no WebGL");
return;
}
var f = gl.getExtension("OES_texture_float");
if (!f) {
alert("no OES_texture_float");
return;
}
var program = twgl.createProgramFromScripts(
gl, ["2d-vertex-shader", "2d-fragment-shader"]);
gl.useProgram(program);
var positionLocation = gl.getAttribLocation(program, "a_position");
var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1, -1, 1, -1, -1, 2,
-1, 1, 1, -1, 1, 1]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
var width = 64;
var height = 64;
var pixels = new Float32Array(width * height * 4);
for (var y = 0; y < height; ++y) {
for (var x = 0; x < width; ++x) {
var offset = (y * width + x) * 4;
pixels[offset + 0] = (x * 256 / width) * 1000;
pixels[offset + 1] = (y * 256 / height) * 1000;
pixels[offset + 2] = (x * y / (width * height)) * 1000;
pixels[offset + 3] = 256000;
}
}
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.FLOAT,
pixels);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
function randInt(range) {
return Math.floor(Math.random() * range);
}
function render() {
// update a random pixel
var x = randInt(width);
var y = randInt(height);
var offset = (y * width + x) * 4;
pixels[offset + 0] = randInt(256000);
pixels[offset + 1] = randInt(256000);
pixels[offset + 2] = randInt(256000);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.FLOAT,
pixels);
gl.drawArrays(gl.TRIANGLES, 0, 6);
requestAnimationFrame(render);
}
render();
}
<script src="https://twgljs.org/dist/2.x/twgl.min.js"></script>
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
void main() {
gl_Position = vec4(a_position, 0, 1);
}
</script>
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;
uniform vec2 u_resolution;
uniform sampler2D u_tex;
void main() {
vec2 texCoord = gl_FragCoord.xy / u_resolution;
vec4 floatColor = texture2D(u_tex, texCoord);
gl_FragColor = floatColor / 256000.0;
}
</script>
<canvas id="canvas" width="400" height="300"></canvas>
所有这些都表明您看到的问题可能与其他问题有关?至于调试,正如上面指出的,ArrayBuffers 非常简单,没有惰性缓冲或任何东西。因此,如果您想查看 ArrayBuffer,请为它创建一个视图,以便调试器通过某种方式知道您想要显示的内容。