我一直在研究从 Three.js 中的数据创建纹理。 这非常简单,但有一些注意事项,有些部分可能会令人困惑。 很多年前我曾陷入过一些陷阱,最近又再次陷入其中,所以我决定写下来!
NSDT工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎 - Three.js虚拟轴心开发包 - 3D模型在线减面 - STL模型在线切割
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 格式。 (官方文档)
Format | Type | Byte per Pixel |
---|---|---|
RGBA | FLOAT | 16 |
RGB | FLOAT | 12 |
LUMINANCE_ALPHA | FLOAT | 8 |
LUMINANCE | FLOAT | 4 |
ALPHA | FLOAT | 4 |
RGBA | HALF_FLOAT_OES | 8 |
RGB | HALF_FLOAT_OES | 6 |
LUMINANCE_ALPHA | HALF_FLOAT_OES | 4 |
LUMINANCE | HALF_FLOAT_OES | 2 |
ALPHA | HALF_FLOAT_OES | 2 |
2.3 Type转TypedArray
这非常简单:
Type | Byte per Pixel | Typed Array |
---|---|---|
UnsignedByte | 1 | Uint8Array |
HalfFloat | 2 | Uint16Array |
Float | 4 | Float32Array |
重要的是类型数组中的位数与表中每个像素的字节数相匹配。 此外,对于 HalfFloat
,应适当准备数据。
2.4 访问片段着色器中的数据
所有整数纹理(包括 UnsignedByteType
)在上传到着色器时都会自动标准化,而浮动/整数纹理(包括 Float
和 HalfFloat
)则按原样传递。
根据格式名称,您可以知道您正在处理哪种类型的数据以及是否会为您进行标准化。 (参考)。
换句话说,在片段着色器中,当使用 UnsignedByteType
纹理时,从纹理 2D 获取的值会自动标准化为 0 到 1 之间。 对于 FloatType
和 HalfFloatType
,你将获得类型化数组中的值,而无需任何标准化。
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 );
}
原文链接:Three.js数据转纹理 - BimAnt