跳到主要内容

纹理

纹理通常是一张图片。

加载纹理

  1. 简单方式
const loader = new THREE.TextureLoader()
const texture = loader.load('/images/wall.jpg')

图片加载是异步的,Three.js 不会停止其他工作,等到图片加载完成之后,会自动更新纹理。

在图片加载完成之前,会展示透明的纹理。

  1. 等待纹理加载完成

可以使用回调方法:

loader.load('/images/wall.jpg', (texture) => {
texture.colorSpace = THREE.SRGBColorSpace;
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshBasicMaterial({
map: texture,
});
cube = new THREE.Mesh(geometry, material);
scene.add(cube);
})
  1. 多张图片

使用 loadManager 来管理多张图片:

const loadManager = new THREE.LoadingManager()
const loader = new THREE.TextureLoader(loadManager)

// 创建材质
const materials = [
'/images/flower-1.jpg',
'/images/flower-2.jpg',
'/images/flower-3.jpg',
'/images/flower-4.jpg',
'/images/flower-5.jpg',
'/images/flower-6.jpg',
].map(url => {
const texture = loader.load(url)
return new THREE.MeshBasicMaterial({ map: texture })
})

let cube: THREE.Mesh

loadManager.onLoad = () => {
console.log('加载完成')
// 创建几何体
const geometry = new THREE.BoxGeometry(1, 1, 1)
// 创建网格
cube = new THREE.Mesh(geometry, materials)
// 将几何体添加到场景中
scene.add(cube)
}
  1. 从其他源加载纹理

记得纹理资源可访问,否则会报错

内存使用

纹理的内存公式:

width×height×4×1.33width \times height \times 4 \times 1.33
  • 44: 每个像素占用 4 个字节(RGBA 信息:红、绿、蓝、透明度)
  • 1.331.33: 用于 mipmap 的额外内存占用

Mipmaps 是预先计算好的、经过优化的图像序列,每个图像都是原始纹理的分辨率逐渐降低的表示。它们能提高渲染性能和视觉质量,尤其是在纹理从远处观看或以较小尺寸显示时。生成 Mipmaps 会使内存使用量增加约 33.3%,因此乘以 1.333。(更多看下下节)

注意这个内存占用公式和图片的压缩方式无关

假设有一张压缩过的 .jpg 图片,分辨率为 3024×37613024 \times 3761,大小为 157k,但它作为纹理在内存中的大小为:

3024×3761×4×1.33=60505764.53024 \times 3761 \times 4 \times 1.33 = 60505764.5

大概是60MB,多几张这样的图片内存就会爆炸。

JPG 和 PNG

JPG 是有损压缩,PNG 是无损压缩。

PNG 的加载会比较慢。

但两者的内存占用是一样的(根据上节公式)。

并且 PNG 支持透明度,也适合作为非图形数据(比如法线图)。

Mipmap 和 Filter

Mip 是纹理的副本,分辨率逐渐降低。一个 Mip 是上一个 Mip 的一半,最终的 Mip 是 1x1 的像素。

GPU 会根据纹理的大小自动选择合适的 Mip。

有点类似于 <image>srcset,设置多张不同分辨率的图片,浏览器会根据设备像素比自动选择合适的图片。

当纹理放大得比原来尺寸还大时,可以设置 magFilter 来控制纹理的放大:

  • THREE.NearestFilter: 使用最近的单个像素值来代替放大后的像素值,对于低分辨率的纹理变得会更加像素化,有点类似《我的世界》
  • THREE.LinearFilter: 使用最近的 4 个像素,根据远近距离,计算出最终混合的像素值

当纹理缩小得比原来尺寸还小时,可以设置 minFilter 来控制纹理的缩小::

  • THREE.NearestFilter: 同上,选择最近的 1 个像素
  • THREE.LinearFilter: 同上,选择最近的 4 个像素并混合
  • THREE.NearestMipmapNearestFilter: 选择合适的 mip 图,然后选择其中一个像素
  • THREE.NearestMipmapLinearFilter: 选择 2 张 mip 图,各选取一个像素并混合
  • THREE.LinearMipmapNearestFilter: 选择合适的 mip 图,然后选择 4 个像素并混合
  • THREE.LinearMipmapLinearFilter: 选择 2 张 mip 图,各选取 4 个像素并混合

Repeat

默认情况下,纹理图不会重复。

有 2 个属性可以控制纹理的重复:

  • wrapS: 水平方向
  • wrapT: 垂直方向

S 代表水平轴,T 代表垂直轴,这是来自于 OpenGL 的俗称约定,主要是为了和 XYZ、UVW 等坐标系区分。