Three.js数据转纹理

我一直在研究从 Three.js 中的数据创建纹理。 这非常简单,但有一些注意事项,有些部分可能会令人困惑。 很多年前我曾陷入过一些陷阱,最近又再次陷入其中,所以我决定写下来!

1、什么让(我)感到困惑?

从数据创建新纹理时,你必须设置格式、类型,并以特定类型的 TypedArray 提供数据:

const texture = new DataTexture(data, width, height, format, type, ...);

文档里说:

“类型”必须与“格式”相对应。

好的...那么我如何知道为我想要使用的格式设置哪种类型?

2、灰度纹理

在这篇文章中,我将只讨论 WebGL1 的灰度(单通道)纹理,因为它是我当前的重点。 本文其余部分的所有内容都将适用于你想要支持的任何内容。

2.1 哪种格式?

好吧,我想创建一个单通道(灰度)纹理。

在文档的内部格式部分,你可以根据通道数和每个像素的字节数找到最适合你的格式。

亮度非常适合灰度纹理。 需要注意的是,它仅支持 Webgl1 的 UnsignedByte 纹理类型。

如果你使用 WebGL2, R* 格式允许你支持各种不同的位深度。

要了解有关不同文件格式含义的更多信息,我发现此页面很有用,它解释了文件格式后缀 F_SNORM 的含义以及如何解释这些类型的纹理。 这对于数据标准化很重要。

2.2 亮度格式允许三种类型(WebGL1)

好吧,我们刚刚了解到,对于 WebGL1,亮度格式采用 UnsignedByte 类型。

我们可以做得更好吗?

如果你的浏览器支持 OES_texture_float 扩展,则一堆新类型( Float 和  HalfFloat)可用于 LUMINANCE 格式。 (官方文档

FormatTypeByte per Pixel
RGBAFLOAT16
RGBFLOAT12
LUMINANCE_ALPHAFLOAT8
LUMINANCEFLOAT4
ALPHAFLOAT4
RGBAHALF_FLOAT_OES8
RGBHALF_FLOAT_OES6
LUMINANCE_ALPHAHALF_FLOAT_OES4
LUMINANCEHALF_FLOAT_OES2
ALPHAHALF_FLOAT_OES2

2.3 Type转TypedArray

这非常简单:

TypeByte per PixelTyped Array
UnsignedByte1Uint8Array
HalfFloat2Uint16Array
Float4Float32Array

重要的是类型数组中的位数与表中每个像素的字节数相匹配。 此外,对于 HalfFloat,应适当准备数据。

2.4 访问片段着色器中的数据

所有整数纹理(包括 UnsignedByteType)在上传到着色器时都会自动标准化,而浮动/整数纹理(包括 FloatHalfFloat)则按原样传递。

根据格式名称,您可以知道您正在处理哪种类型的数据以及是否会为您进行标准化。 (参考)。

换句话说,在片段着色器中,当使用 UnsignedByteType 纹理时,从纹理 2D 获取的值会自动标准化为 0 到 1 之间。 对于 FloatTypeHalfFloatType,你将获得类型化数组中的值,而无需任何标准化。

3、示例

  • UnsignedByte Texture
const textureSize = 16
const dataSize = 10;
const data = new Uint8Array(dataSize);

for (let i = 0; i < dataSize) {
  data[i] = Math.round(Math.random() * 255); // pass anything from 0 to 255
}

const texture = new DataTexture(data, textureSize, textureSize, LUMINACE, UnsignedByteType);
varying vec2 vUv;
uniform sampler2D uData;

void main(){
  vec3 color;
  vec4 data = texture2D( uData, vUv );
  gl_FragColor = vec4( data.xyz, 1.0 );
}
  • HalfFloat Texture

要将数字转换为半浮点数,请按照 Three.js 的方式进行:像这样

⚠️ 将数字转换为半浮点精度时请注意精度错误
const textureSize = 16
const dataSize = 10;
const data = new Uint16Array(dataSize);

for (let i = 0; i < dataSize) {
  const largeNumber = Math.random() * 10000; // pass anything from 0 to 10000
  data[i] = toHalfFloat(largeNumber);
}

const texture = new DataTexture(data, textureSize, textureSize, LUMINACE, HalfFloatType);
varying vec2 vUv;
uniform sampler2D uData;
uniform float uMax;
uniform float uMin;

void main(){
  vec3 color;
  vec4 data = texture2D( uData, vUv );
  vec4 normalizedData = (data - uMin) / (uMax - uMin);
  gl_FragColor = vec4( data.xyz, 1.0 );
}
  • Float Texture
const textureSize = 16
const dataSize = 10;
const data = new Float32Array(dataSize);

for (let i = 0; i < dataSize) {
  const largeNumber = Math.random() * 10000; // pass anything from 0 to 10000
  data[i] = largeNumber;
}

const texture = new DataTexture(data, textureSize, textureSize, LUMINACE, FloatType);
varying vec2 vUv;
uniform sampler2D uData;
uniform float uMax;
uniform float uMin;

void main(){
  vec3 color;
  vec4 data = texture2D( uData, vUv );
  vec4 normalizedData = (data - uMin) / (uMax - uMin);
  gl_FragColor = vec4( data.xyz, 1.0 );
}

原文链接:Create textures from data in ThreeJS

BimAnt翻译整理,转载请标明出处