目录

WebGL2Fundamentals.org

Fix, Fork, Contribute

WebGL2 三维数据纹理

此文上接 WebGL2 系列文章,第一篇是基础概念, 上一篇是纹理

上节中讲到了纹理的工作原理以及如何使用,我们用下载的图像创建纹理, 在这篇文章中我们将直接用 JavaScript 创建数据。

根据纹理格式,用 JavaScript 为纹理创建数据是比较直接的。 WebGL2 支持大量纹理格式。 WebGL2 支持所有在 WebGL1 中 未定义大小 的格式

格式数据类型通道数单像素字节数
RGBAUNSIGNED_BYTE44
RGBUNSIGNED_BYTE33
RGBAUNSIGNED_SHORT_4_4_4_442
RGBAUNSIGNED_SHORT_5_5_5_142
RGBUNSIGNED_SHORT_5_6_532
LUMINANCE_ALPHAUNSIGNED_BYTE22
LUMINANCEUNSIGNED_BYTE11
ALPHAUNSIGNED_BYTE11

它们被称为 未定义大小 因为它们在内部的实际表示方式在 WebGL1 中是未定义的。 它们在 WebGL2 中被定义。 除了那些未定义大小的格式之外,还有许多定义了大小的格式,包括

大小
格式
基础
格式
R
G
B
A
共享
可渲染
颜色
纹理
可过滤
R8 RED 8
R8_SNORM RED s8
RG8 RG 8 8
RG8_SNORM RG s8 s8
RGB8 RGB 8 8 8
RGB8_SNORM RGB s8 s8 s8
RGB565 RGB 5 6 5
RGBA4 RGBA 4 4 4 4
RGB5_A1 RGBA 5 5 5 1
RGBA8 RGBA 8 8 8 8
RGBA8_SNORM RGBA s8 s8 s8 s8
RGB10_A2 RGBA 10 10 10 2
RGB10_A2UI RGBA ui10 ui10 ui10ui2
SRGB8 RGB 8 8 8
SRGB8_ALPHA8 RGBA 8 8 8 8
R16F RED f16
RG16F RG f16 f16
RGB16F RGB f16 f16 f16
RGBA16F RGBA f16 f16 f16 f16
R32F RED f32
RG32F RG f32 f32
RGB32F RGB f32 f32 f32
RGBA32F RGBA f32 f32 f32 f32
R11F_G11F_B10F RGB f11 f11 f10
RGB9_E5 RGB 9 9 9 5
R8I RED i8
R8UI RED ui8
R16I RED i16
R16UI RED ui16
R32I RED i32
R32UI RED ui32
RG8I RG i8 i8
RG8UI RG ui8 ui8
RG16I RG i16 i16
RG16UI RG ui16 ui16
RG32I RG i32 i32
RG32UI RG ui32 ui32
RGB8I RGB i8 i8 i8
RGB8UI RGB ui8 ui8 ui8
RGB16I RGB i16 i16 i16
RGB16UI RGB ui16 ui16 ui16
RGB32I RGB i32 i32 i32
RGB32UI RGB ui32 ui32 ui32
RGBA8I RGBA i8 i8 i8 i8
RGBA8UI RGBA ui8 ui8 ui8 ui8
RGBA16I RGBA i16 i16 i16 i16
RGBA16UI RGBA ui16 ui16 ui16ui16
RGBA32I RGBA i32 i32 i32 i32
RGBA32UI RGBA ui32 ui32 ui32ui32

还有这些深度和模板格式 And these depth and stencil formats as well

大小
格式
基本
格式
深度
模板
DEPTH_COMPONENT16 DEPTH_COMPONENT 16
DEPTH_COMPONENT24 DEPTH_COMPONENT 24
DEPTH_COMPONENT32F DEPTH_COMPONENT f32
DEPTH24_STENCIL8 DEPTH_STENCIL 24 ui8
DEPTH32F_STENCIL8 DEPTH_STENCIL f32 ui8

Legend:

  • 单个数字比如 8 表示 8 位将归一化为 0 到 1
  • 前面有 s 的数字比如 s8 表示一个有符号的 8 位数字,将归一化为 -1 到 1
  • 前面有 f 的数字比如 f16 表示浮点数。
  • 前面有 i 的数字比如 i8 表示整数。
  • 前面有 ui 的数字比如 ui8 表示无符号整数。

我们不会在此处使用此信息, 但我 突出显示 了 half 和 float 纹理格式以显示与 WebGL1 的不同,它们始终在 WebGL2 中可用, 但默认情况下它们未标记为可渲染颜色和/或可过滤纹理。 不可渲染意味着它们不能被渲染。 渲染到纹理将在另一课中介绍。不可过滤纹理意味着它们必须仅用于 gl.NEAREST 。这两个特性都可以作为 WebGL2 中的可选扩展。

对于每种格式,你都指定 内部格式 (GPU 将在内部使用的格式)以及您提供给 WebGL 的数据的 格式类型 。这是你提供给内部格式的数据的 格式类型

内部
格式
格式 类型 每像素
源字节数
RGBA8
RGB5_A1
RGBA4
SRGB8_ALPHA8
RGBA UNSIGNED_BYTE 4
RGBA8_SNORM RGBA BYTE 4
RGBA4 RGBA UNSIGNED_SHORT_4_4_4_4 2
RGB5_A1 RGBA UNSIGNED_SHORT_5_5_5_1 2
RGB10_A2
RGB5_A1
RGBA UNSIGNED_INT_2_10_10_10_REV 4
RGBA16F RGBA HALF_FLOAT 8
RGBA32F
RGBA16F
RGBA FLOAT 16
RGBA8UI RGBA_INTEGER UNSIGNED_BYTE 4
RGBA8I RGBA_INTEGER BYTE 4
RGBA16UI RGBA_INTEGER UNSIGNED_SHORT 8
RGBA16I RGBA_INTEGER SHORT 8
RGBA32UI RGBA_INTEGER UNSIGNED_INT 16
RGBA32I RGBA_INTEGER INT 16
RGB10_A2UI RGBA_INTEGER UNSIGNED_INT_2_10_10_10_REV 4
RGB8
RGB565
SRGB8
RGB UNSIGNED_BYTE 3
RGB8_SNORM RGB BYTE 3
RGB565 RGB UNSIGNED_SHORT_5_6_5 2
R11F_G11F_B10F RGB UNSIGNED_INT_10F_11F_11F_REV 4
RGB9_E5 RGB UNSIGNED_INT_5_9_9_9_REV 4
RGB16F
R11F_G11F_B10F
RGB9_E5
RGB HALF_FLOAT 6
RGB32F
RGB16F
R11F_G11F_B10F
RGB9_E5
RGB FLOAT 12
RGB8UI RGB_INTEGER UNSIGNED_BYTE 3
RGB8I RGB_INTEGER BYTE 3
RGB16UI RGB_INTEGER UNSIGNED_SHORT 6
RGB16I RGB_INTEGER SHORT 6
RGB32UI RGB_INTEGER UNSIGNED_INT 12
RGB32I RGB_INTEGER INT 12
RG8 RG UNSIGNED_BYTE 2
RG8_SNORM RG BYTE 2
RG16F RG HALF_FLOAT 4
RG32F
RG16F
RG FLOAT 8
RG8UI RG_INTEGER UNSIGNED_BYTE 2
RG8I RG_INTEGER BYTE 2
RG16UI RG_INTEGER UNSIGNED_SHORT 4
RG16I RG_INTEGER SHORT 4
RG32UI RG_INTEGER UNSIGNED_INT 8
RG32I RG_INTEGER INT 8
R8 RED UNSIGNED_BYTE 1
R8_SNORM RED BYTE 1
R16F RED HALF_FLOAT 2
R32F
R16F
RED FLOAT 4
R8UI RED_INTEGER UNSIGNED_BYTE 1
R8I RED_INTEGER BYTE 1
R16UI RED_INTEGER UNSIGNED_SHORT 2
R16I RED_INTEGER SHORT 2
R32UI RED_INTEGER UNSIGNED_INT 4
R32I RED_INTEGER INT 4
DEPTH_COMPONENT16 DEPTH_COMPONENT UNSIGNED_SHORT 2
DEPTH_COMPONENT24
DEPTH_COMPONENT16
DEPTH_COMPONENT UNSIGNED_INT 4
DEPTH_COMPONENT32F DEPTH_COMPONENT FLOAT 4
DEPTH24_STENCIL8 DEPTH_STENCIL UNSIGNED_INT_24_8 4
DEPTH32F_STENCIL8 DEPTH_STENCIL FLOAT_32_UNSIGNED_INT_24_8_REV 8
RGBA RGBA UNSIGNED_BYTE 4
RGBA RGBA UNSIGNED_SHORT_4_4_4_4 2
RGBA RGBA UNSIGNED_SHORT_5_5_5_1 2
RGB RGB UNSIGNED_BYTE 3
RGB RGB UNSIGNED_SHORT_5_6_5 2
LUMINANCE_ALPHA LUMINANCE_ALPHA UNSIGNED_BYTE 2
LUMINANCE LUMINANCE UNSIGNED_BYTE 1
ALPHA ALPHA UNSIGNED_BYTE 1

让我们创建一个 3×2 像素的 R8 纹理, 因为是 R8 纹理, 所以在红色通道里每个像素只有一个值

我们继续使用上篇文章中的例子,首先修改纹理坐标,每个面使用整个纹理

// 填充立方体纹理坐标的缓冲
function setTexcoords(gl) {
  gl.bufferData(
      gl.ARRAY_BUFFER,
      new Float32Array([
        // 正面
        0, 0,
        0, 1,
        1, 0,
        1, 0,
        0, 1,
        1, 1,
        ...

然后修改代码创建一个纹理

// 创建一个纹理
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);

-// 用 1x1 的蓝色像素填充纹理
-gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
-              new Uint8Array([0, 0, 255, 255]));

// 用 3x2 的像素填充纹理
const level = 0;
const internalFormat = gl.R8;
const width = 3;
const height = 2;
const border = 0;
const format = gl.RED;
const type = gl.UNSIGNED_BYTE;
const data = new Uint8Array([
  128,  64, 128,
    0, 192,   0,
]);
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, width, height, border,
              format, type, data);

// 设置筛选器,我们不需要使用贴图所以就不用筛选器了
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
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);

-// 异步加载图像
-...

这是结果

哦!为什么不管用?!?!!

查看 JavaScript 控制台看到这样的错误信息

WebGL: INVALID_OPERATION: texImage2D: ArrayBufferView not big enough for request

结果是 WebGL 中有一种首次创建 OpenGL 后的模糊设定, 计算机有时在数据为某些特定大小时速度会快一些, 例如一次拷贝 2,4 或 8 个字节比一次拷贝 1 个字节要快, WebGL 默认使用 4 字节长度,所以它期望每一行数据是多个 4 字节数据(最后一行除外)。

我们之前的数据每行只有 3 个字节,总共为 6 字节, 但是 WebGL 试图在第一行获取 4 个字节,第二行获取 3 个字节, 总共 7 个字节,所以会出现这样的报错。

我们可以告诉 WebGL 一次处理 1 个字节

const alignment = 1;
gl.pixelStorei(gl.UNPACK_ALIGNMENT, alignment);

有效参数为 1,2,4 和 8.

我觉得你可能无法计算出对齐数据和非对齐数据的速度区别, 所以希望默认值是 1 而不是 4, 这样这个问题就不会困扰新手, 但是为了适配 OpenGL,所以要保留相同的默认设置,这样移植应用就不用改变行数, 然后可以为新的应用在需要的地方设置属性为 1。

有了这个设置后就能正常运行了

有着这些基础就可以讲渲染到材质了。

Pixel vs Texel

有时纹理上的像素叫 texels,像素是图片元素的简写,Texel 是纹理元素的简写。

我知道我可能会收到一些图形学大师的牢骚,但是我所说的 "texel" 是一种行话。 我通常在使用纹理的元素时不假思索的使用了“像素”这个词。 😇

有意见或建议? 在GitHub上提issue.
comments powered by Disqus