JavaScript TypedArray 混合类型

JavaScript TypedArray mixing types

我正在尝试使用 WebGL,并希望将一些不同的类型混合到一个字节缓冲区中。我了解 TypedArrays 用于此目的,但不清楚我是否可以将类型与它们混合使用(OpenGL 顶点数据通常是与无符号字节或整数混合的浮点数)。

在我的测试中,我想使用 set() 将 2 个浮点数打包到 UInt8Array 中,但它似乎只是将 2 个浮点数放入 UInt8Array 的前 2 个元素中。我希望这当然会填充数组,因为我们有 8 个字节的数据。

有什么方法可以在 JavaScript 中实现这一点,或者我是否需要将所有顶点数据保留为浮点数?

src = new Float32Array(2); // 2 elements = 8 bytes
src[0] = 100;
src[1] = 200;

dest = new UInt8Array(8); // 8 elements = 8 bytes
dest.set(src, 0); // insert src at offset 0

dest = 100,200,0,0,0,0,0,0 (only the first 2 bytes are set)

您可以通过在同一缓冲区上创建不同的视图来混合类型。

const asFloats = new Float32Array(2);
// create a uint8 view to the same buffer as the float32array
const asBytes = new Uint8Array(asFloats.buffer);

console.log(asFloats);
asBytes[3] = 123;
console.log(asFloats);

TypeArrays 的真正工作方式是有一种叫做 ArrayBuffer 的东西,它有一定的字节数。要查看字节,您需要一个 ArrayBufferView,其中有各种类型 Int8ArrayUint8ArrayInt16ArrayUint16ArrayInt32ArrayUint32Array, Float32Array, Float64Array.

您可以从头开始创建 ArrayBuffer

const buffer = new ArrayBuffer(8);
const asFloats = new Float32Array(buffer);
asFloats[0] = 1.23;
asFloats[1] = 4.56;
console.log(asFloats);

或者您可以做更正常的事情,即创建一个特定类型的 ArrayBufferView,它将同时创建该类型的 ArrayBufferView 如果您不将其传递给构造函数,也为其创建 ArrayBuffer 。然后,您可以从 someArrayBufferView.buffer 访问该缓冲区,如上面第一个示例所示。

您还可以为视图指定一个 ArrayBuffer 中的偏移量和一个使其小于 ArrayBuffer 的长度。示例:

// make a 16byte ArrayBuffer and a Uint8Array (ArrayBufferView)
const asUint8 = new Uint8Array(16);

// make a 1 float long view in the same buffer
// that starts at byte 4 in that buffer
const byteOffset = 4;
const length = 1;  // 1 float32
const asFloat = new Float32Array(asUint8.buffer, byteOffset, length);

// show the buffer is all 0s
console.log(asUint8);

// set the float
asFloat[0] = 12345.6789

// show the buffer is affected at byte 4
console.log(asUint8);

// set a float out of range of its length
asFloat[1] = -12345.6789;  // this is effectively a no-op

// show the buffer is NOT affected at byte 8
console.log(asUint8);

因此,例如,如果您想为 WebGL 混合浮动位置和 Uint8 颜色,您可以做类似的事情

// we're going to have
// X,Y,Z,R,G,B,A, X,Y,Z,R,G,B,A, X,Y,Z,R,G,B,A, 
// where X,Y,Z are float32
// and R,G,B,A are uint8

const sizeOfVertex = 3 * 4 + 4 * 1;  // 3 float32s + 4 bytes
const numVerts = 3;
const asBytes = new Uint8Array(numVerts * sizeOfVertex);
const asFloats = new Float32Array(asBytes.buffer);

// set the positions and colors
const positions = [
  -1,  1, 0,
   0, -1, 0,
   1,  1, 0,
];
const colors = [
   255, 0, 0, 255,
   0, 255, 0, 255,
   0, 0, 255, 255,
];
{
  const numComponents = 3;
  const offset = 0;  // in float32s
  const stride = 4;  // in float32s
  copyToArray(positions, numComponents, offset, stride, asFloats);
}
{
  const numComponents = 4;
  const offset = 12;  // in bytes
  const stride = 16;  // in bytes
  copyToArray(colors, numComponents, offset, stride, asBytes);
}

console.log(asBytes);
console.log(asFloats);

function copyToArray(src, numComponents, offset, stride, dst) {
  const strideDiff = stride - numComponents;
  let srcNdx = 0;
  let dstNdx = offset;
  const numElements = src.length / numComponents;
  if (numElements % 1) {
    throw new Error("src does not have an even number of elements");
  }
  for (let elem = 0; elem < numElements; ++elem) {
    for(let component = 0; component < numComponents; ++component) {
      dst[dstNdx++] = src[srcNdx++];
    }
    dstNdx += strideDiff;
  }
}