# 纹理

上一节我们说过有一种特殊的Uniform,其类型为SAMPLER,这就是纹理。纹理本质上就是贴图在 GPU 上的说法,当贴图被作为纹理提交给 GPU 后,便可以被 GPU 进行采样

纹理在小游戏框架中可以分为2D纹理Texture2D和立方体纹理CubeTexture两种,后者主要用作天空盒。

# 创建

为了使用纹理,开发者首先要创建纹理。小游戏框架提供了许多种方式来创建纹理,但主要可以归为两类:

# 在 IDE 中编辑

首先开发者可以将一张图片(比如JPG、PNG)拖入到 IDE 中并选中:

然后便可以在右侧的 Inspector 中看到其配置选项:

一张图片生成的是2D纹理(显而易见),在TextureSetting中开发者可以配置其一些参数,有比如滤波模式、是否生成 Mipmaps 等常规配置,也有压缩纹理的详细配置。

压缩纹理是一种节省 GPU 带宽的技术,不同平台有不同的压缩纹理支持度,所以小游戏框架在构建时会根据配置构建全平台的压缩纹理并做分包下发。

通过EtcQualityAstcQualityPvrQuality可以配置这三种移动平台最为常见的压缩纹理格式的质量。

# 通过代码创建

除了将图片作为资源导入 IDE 来创建纹理,有时候开发者还需要直接通过既有数据来创建,比如ArrayBuffer。如果要手动创建一张纹理,开发者需要先创建一个实例,然后使用成员方法来初始化,比如2D纹理:

// 创建纹理并设置采样参数
const tex = new engine.Texture2D({
  filterMode: engine.TextureFilterMode.BILINEAR,
  wrapU: CLAMP_TO_EDGE,
  wrapV: CLAMP_TO_EDGE,
  anisoLevel: 1
});

// 通过`ArrayBuffer`初始化一张红色的纹理,宽高皆为1,并生成mipmaps
tex.initWithRGBABuffer(new Uint8Array([255, 0, 0, 255]), 1, 1. true);

// 通过`engine.Image`实例初始化一张纹理,并生成mipmaps
tex.initWithImage(image, true);

立方体纹理也差不多,只不过需要六个面:

// 创建纹理并设置采样参数,多出了一个`wrapW`维度
const tex = new engine.TextureCube({
  filterMode: engine.TextureFilterMode.BILINEAR,
  wrapU: CLAMP_TO_EDGE,
  wrapV: CLAMP_TO_EDGE,
  wrapW: CLAMP_TO_EDGE,
  anisoLevel: 1
});

// 通过`ArrayBuffer`初始化一张红色的立方体纹理,宽高皆为1,并生成mipmaps
tex.initWithTextureSources([
  new Uint8Array([255, 0, 0, 255]),
  new Uint8Array([255, 0, 0, 255]),
  new Uint8Array([255, 0, 0, 255]),
  new Uint8Array([255, 0, 0, 255]),
  new Uint8Array([255, 0, 0, 255]),
  new Uint8Array([255, 0, 0, 255])
], 1, 1. true);

// 通过`engine.Image`实例数组初始化一张宽高256的立方体纹理,并生成mipmaps
tex.initWithTextureSources([img1, img2, img3, img4, img5, img6], 256, 256, true);

# 更新纹理

除了一开始就创建完毕,有时候开发者可能需要去动态更新纹理的内容,目前小游戏框架开启了针对2D纹理的动态更新支持,需要一开始将纹理创建为动态的:

// 创建一张256x256大小的动态纹理
tex.initDynamicTexture(256, 256);

// 使用`ArrayBuffer`或者`engine.Image`来更新纹理的一部分。
tex.updateSubTexture(source, 0, 0, 256, 256);

# 使用纹理

创建好纹理之后便需要使用,这个要分为两部分,在 Runtime 的部分中,如果是使用 Prefab 流程,则纹理会被自动设置好,如果需要手动管理,一般是需要将其设置到全局或材质的 Uniform 中,这个将在后续的章节效果和材质中论述。

而最终纹理需要在 Shader 中采样,详见着色器