JavaScript TypedArray混合类型 [英] JavaScript TypedArray mixing types

查看:83
本文介绍了JavaScript TypedArray混合类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用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,其中有Int8ArrayUint8ArrayInt16ArrayUint16ArrayInt32ArrayUint32ArrayFloat32ArrayFloat64Array各种类型. >

您可以从头开始创建ArrayBuffer.

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

或者您可以做更普通的事情,即创建特定类型的ArrayBufferView,它将同时创建该类型的ArrayBufferView 如果您不将其传递给构造函数,则也是如此.然后,您可以从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;
  }
} 

I'm trying to use WebGL and would like to mix some different types into one buffer of bytes. I understand TypedArrays serve this purpose but it's not clear if I can mix types with them (OpenGL vertex data is often floats mixed with unsigned bytes or integers).

In my test I want to pack 2 floats into a UInt8Array using set(), but it appears to just place the 2 floats into the first 2 elements of the UInt8Array. I would expect this to fill the array of course since we have 8 bytes of data.

Is there anyway to achieve this in JavaScript or do I need to keep all my vertex data as floats?

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)

解决方案

You can mix types by making different views on the same buffer.

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

The way TypeArrays really work is there is something called an ArrayBuffer which is a certain number of bytes long. To view the bytes you need an ArrayBufferView of which there are various types Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array.

You can create the ArrayBuffer from scratch.

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

Or you can do the more normal thing which is to create an ArrayBufferView of a specific type and it will create both the ArrayBufferView of that type and create the ArrayBuffer for it as well if you don't pass one into the constructor. You can then access that buffer from someArrayBufferView.buffer as shown in the first example above.

You can also assign a view an offset in the ArrayBuffer and a length to make it smaller than the ArrayBuffer. Example:

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

So if you want to for example mix float positions and Uint8 colors for WebGL you might do something like

// 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;
  }
}

这篇关于JavaScript TypedArray混合类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆