# 纹理

纹理Texture是GPU中的图像,供着色器采样使用。在框架中其一般被作为材质的一部uniforms使用。

由于纹理来源的复杂性,有普通纹理、立方体纹理、视频纹理、渲染纹理等,它们均有不同的创建或者加载方式,却往往有相同的用法,所以框架为其特别约定了一套资源引用方式,详见使用纹理一节。

# 普通纹理

普通纹理即2D纹理,创建普通纹理一般有以下几种方式:

# 通过加载器

通过加载器加载是最为通用的方式,我们可以用标签来加载:

<xr-asset-load type="texture" asset-id="waifu" src="/assets/waifu.png" options="wrapU:1,wrapV:2" />

也可以使用代码加载:

scene.assets.loadAsset({type: 'texture', assetId: 'waifu', src: '/assets/waifu.png'});

注意这里的options我们可以给纹理添加一些配置选项,比如wrapfilter等,其对应的值都是枚举,详细可见ITextureLoaderOptions

# 代码创建

除了加载器,还可以通过代码创建的方式,这里的options详见ITextureOptions

function createGreenTexture(scene: XrFrame.Scene) {
  return scene.createTexture({
    source: [new Uint8Array([0, 1, 0, 1])],
    pixelFormat: xrFrameSystem.ETextureFormat.RGBA8,
    width: 1,
    height: 1,
    magFilter: xrFrameSystem.EFilterMode.NEAREST,
    minFilter: xrFrameSystem.EFilterMode.NEAREST,
    wrapU: xrFrameSystem.EWrapMode.CLAMP_TO_EDGE,
    wrapV: xrFrameSystem.EWrapMode.CLAMP_TO_EDGE,
    anisoLevel:1
  });
}

// 可以将其注册到资源系统
xrFrameSystem.registerTexture('green', createGreenTexture);

# 更新

除了一开始就创建完毕,有时候开发者可能需要去动态更新纹理的内容:

tex.update({
  buffer: source,
  xoffset: 0, yoffset: 0,
  width: 256, height: 256
});

这里面的buffer可以是ArrayBufferArrayBufferView或者IImage(图像)。

# 图像

图像资源在框架中一般用于作为纹理的source或者AR识别的来源等,有两种方式来创建图片,首先是xml中:

<xr-asset-load type="image" asset-id="waifu-img" src="/assets/waifu.png" />

也可以用代码加载:

scene.createImage({type: 'texture', assetId: 'waifu-img', src: '/assets/waifu.png'})

还可以代码创建:

const image = scene.createImage();
image.onload = () => {};
image.onerror = error => {};
image.src = '/assets/waifu.png';

# 立方体纹理

立方体纹理CubeTexture是一种特殊的纹理,一般用于实现天空盒或者环境贴图,虽然在框架中一般使用普通全景纹理,但仍然提供给开发者一个选择。

# 通过加载器

立方体纹理的创建也可以通过加载的方式创建。用xml加载:

<xr-asset-load
  type="cube-texture" asset-id="sky" src="/assets/sky/"
  options="faces: right.jpg left.jpg top.jpg bottom.jpg front.jpg back.jpg,wrapU:1,wrapV:2"
/>

或是使用代码加载:

scene.assets.loadAsset({
  type: 'cube-texture', assetId: 'sky', src: '/assets/sky/',
  options: {faces: ['right.jpg', 'left.jpg', 'top.jpg', 'bottom.jpg', 'front.jpg', 'back.jpg']}
});

options中除了faces用于配置每个面的纹理地址,其他参数和TextureLoader一致。

# 代码创建

也可以代码创建,通过注册加入资源系统:

function createGreenCubeTexture(scene: XrFrame.Scene) {
  const buffer = new Uint8Array([0, 1, 0, 1]);
  return scene.createTexture({
    type: xrFrameSystem.ETextureType.Cube,
    slices: 6,
    source: [buffer, buffer, buffer, buffer, buffer, buffer],
    pixelFormat: xrFrameSystem.ETextureFormat.RGBA8,
    width: 1,
    height: 1,
    magFilter: xrFrameSystem.EFilterMode.NEAREST,
    minFilter: xrFrameSystem.EFilterMode.NEAREST,
    wrapU: xrFrameSystem.EWrapMode.CLAMP_TO_EDGE,
    wrapV: xrFrameSystem.EWrapMode.CLAMP_TO_EDGE,
    anisoLevel:1
  });
}

// 可以将其注册到资源系统
xrFrameSystem.registerCubeTexture('green', createGreenCubeTexture);

可见其实它也是一种普通的纹理,只不过类型不一样。

立方体纹理也支持更新,和普通纹理基本一致,只不过必须提供slice参数。

# 视频纹理

有时候我们需要将视频放入场景中进行渲染,使用视频纹理VideoTexture资源就可以实现这个需求。视频纹理本质上是创建一个普通纹理,然后定时用视频解码数据对它进行更新。

# 通过加载器

视频纹理的创建也可以通过加载的方式创建。用xml加载:

<xr-asset-load
  type="video-texture" asset-id="vt" src="/assets/video.mp4"
  options="autoPlay:true,loop:true,abortAudio:false,placeHolder:/assets/video.jpg"
/>

或是使用代码加载:

scene.assets.loadAsset({
  type: 'video-texture', assetId: 'vt', src: '/assets/video.mp4',
  options: {autoPlay: true}
});

注意到视频纹理的几个选项,autoPlay开启后视频加载成功时会自动播放,loop开启时会循环播放,abortAudio用于指定是否要禁止声音(默认禁止),placeHolder则是作为视频尚未加载成功时的一个占位图,可选。

特别注意,placeHolder的尺寸必须和视频完全一致!!!

# 代码创建

视频纹理也可以手动在代码中创建:

const vt = await createVideoTexture({src, autoPlay, loop, placeHolder});

注意这是个异步方法,在有placeHolder时会在图片加载完毕时返回,否则将在视频准备好时返回。

# 控制

视频纹理对于开发者而言主要是视频,所以我们提供了一些用于控制视频播放的方法:

// 开始播放,异步方法
await vt.play();

// 从`pos`秒开始播放,异步方法
await vt.seek(pos);

// 停止播放
vt.stop();

// 释放视频
vt.release();

// 在播放结束并且非loop的情况下,会执行
vt.onEnd = () => {};

// 在基础库`v2.33.0`及以上,提供了暂停/唤醒方法
// 同时可以配合新暴露的播放状态使用
const xrSystem = wx.getXrFrameSystem();
if (vt.state === xrSystem.EVideoState.Playing) {
  vt.pause();
} else if (vt.state === xrSystem.EVideoState.Paused) {
  vt.resume();
}

注意,如果是自己创建的视频资源,请务必自己调用release方法释放!!!

# 渲染纹理

渲染纹理比较特殊,详见渲染纹理

# 使用纹理

加载或者创建了纹理后,便可以在组件或者材质的uniforms中使用。但通过以上的章节,我们知道了纹理的种类有许多,虽然组件数据可以通过指定具体的类型,比如cube-texture来取得具体的类型的资源,但对于uniforms来说是无法判断的,同时也比较繁琐。

为了解决这个问题,我们遵循约定大于配置的原则,对于所有这些纹理资源,开发者只需要将组件数据类型指定为texture,加上不同的前缀,资源系统会自动匹配获取对应的资源:

  1. 不加前缀,取得普通纹理。
  2. cube-前缀,取得立方体纹理资源。
  3. video-前缀,取得视频纹理。
  4. render-前缀,取得渲染纹理。

如果是应用于uniforms中,开发者不需要自己去关心它们的区别,只需要uniforms="u_baseColorMap:video-vt"这样即可,但如果是自定义组件数据,就需要开发者自己处理一下了:

onUpdate(data: {texture: XrFrame.Texture | XrFrame.ITextureWrapper}) {
  const realTex = xrFrameSystem.isTextureWrapper(data.texture)
    ? data.texture.texture
    : data.texture;
}