纹理
纹理通常是一张图片。
加载纹理
- 简单方式
const loader = new THREE.TextureLoader()
const texture = loader.load('/images/wall.jpg')
图片加载是异步的,Three.js 不会停止其他工作,等到图片加载完成之后,会自动更新纹理。
在图片加载完成之前,会展示透明的纹理。
- 等待纹理加载完成
可以使用回调方法:
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);
})
- 多张图片
使用 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)
}
- 从其他源加载纹理
记得纹理资源可访问,否则会报错
内存使用
纹理的内存公式:
- : 每个像素占用 4 个字节(RGBA 信息:红、绿、蓝、透明度)
- : 用于 mipmap 的额外内存占用
Mipmaps 是预先计算好的、经过优化的图像序列,每个图像都是原始纹理的分辨率逐渐降低的表示。它们能提高渲染性能和视觉质量,尤其是在纹理从远处观看或以较小尺寸显示时。生成 Mipmaps 会使内存使用量增加约 33.3%,因此乘以 1.333。(更多看下下节)
注意这个内存占用公式和图片的压缩方式无关。
假设有一张压缩过的 .jpg 图片,分辨率为 ,大小为 157k,但它作为纹理在内存中的大小为:
大概是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 等坐标系区分。