04 纹理与贴图
学习在 Three.js 中加载和使用纹理图片
纹理(Texture)是什么
纹理就是贴在 3D 物体表面的一张图片。比如给立方体贴上砖墙图片,它看起来就像一面砖墙。
加载纹理
方式一:TextureLoader
js
const textureLoader = new THREE.TextureLoader()
// 加载图片作为纹理
const texture = textureLoader.load('/textures/wood.jpg')
// 设置纹理参数
texture.colorSpace = THREE.SRGBColorSpace // 正确色彩空间(Three.js r152+)
// 应用到材质
const material = new THREE.MeshStandardMaterial({
map: texture
})
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
方式二:加载管理器(推荐)
管理多个纹理加载进度:
js
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js'
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
const loadingManager = new THREE.LoadingManager()
// 进度回调
loadingManager.onProgress = (url, loaded, total) => {
const percent = (loaded / total * 100).toFixed(0)
console.log(`加载中: ${percent}%`)
}
// 错误回调
loadingManager.onError = (url) => {
console.error('加载失败:', url)
}
const textureLoader = new THREE.TextureLoader(loadingManager)
const gltfLoader = new THREE.GLTFLoader(loadingManager)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
方式三:GLTFLoader(自动处理纹理)
js
const gltfLoader = new GLTFLoader()
gltfLoader.load(
'/models/scene.glb',
(gltf) => {
const model = gltf.scene
scene.add(model)
// 纹理已经自动加载并应用好了
model.traverse((child) => {
if (child.isMesh) {
child.material.envMapIntensity = 1 // 环境光强度
}
})
}
)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
纹理参数
重复与偏移
js
const texture = textureLoader.load('/textures/tile.jpg')
// 重复(平铺)
texture.wrapS = THREE.RepeatWrapping // 水平方向重复
texture.wrapT = THREE.RepeatWrapping // 垂直方向重复
texture.repeat.set(4, 4) // 水平和垂直各重复4次
// 偏移
texture.offset.set(0.5, 0) // 偏移50%
// 旋转(弧度)
texture.rotation = Math.PI / 4 // 旋转45°
texture.center.set(0.5, 0.5) // 旋转中心(0-1)1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
过滤
纹理被缩小时如何计算像素颜色:
js
// 纹理缩小时的过滤方式
texture.minFilter = THREE.LinearMipmapLinearFilter // 多级渐远纹理(推荐)
// 其他选项:NearestFilter(像素风), LinearFilter(模糊)
// 纹理放大时的过滤方式
texture.magFilter = THREE.LinearFilter // 线性插值(平滑)
// 其他选项:NearestFilter(像素风)1
2
3
4
5
6
7
2
3
4
5
6
7
生成多级渐远纹理(mipmap)
自动生成不同分辨率的纹理版本,优化远处物体的渲染性能:
js
texture.generateMipmaps = true // 默认开启1
常用贴图类型
颜色贴图(Albedo/Color Map)
最基本的纹理,定义物体表面颜色:
js
const colorTexture = textureLoader.load('/textures/wood.jpg')
material.map = colorTexture1
2
2
法线贴图(Normal Map)
在不增加几何体复杂度的情况下,让表面看起来有凹凸细节:
js
const normalTexture = textureLoader.load('/textures/brick-normal.jpg')
material.normalMap = normalTexture
material.normalScale.set(1, 1) // 凹凸强度1
2
3
2
3
粗糙度/金属度贴图
分别控制不同区域的粗糙度和金属度:
js
const roughnessTexture = textureLoader.load('/textures/roughness.jpg')
const metalnessTexture = textureLoader.load('/textures/metalness.jpg')
material.roughnessMap = roughnessTexture
material.metalnessMap = metalnessTexture1
2
3
4
5
2
3
4
5
环境光遮蔽贴图(AO Map)
模拟物体缝隙、角落的阴影,增加真实感:
js
const aoTexture = textureLoader.load('/textures/ao.jpg')
material.aoMap = aoTexture
material.aoMapIntensity = 11
2
3
2
3
位移贴图(Displacement Map)
真正改变几何体形状(需要高细分):
js
const dispTexture = textureLoader.load('/textures/displacement.jpg')
material.displacementMap = dispTexture
material.displacementScale = 0.1 // 位移强度1
2
3
2
3
HDR/环境贴图
用于反射和环境光照:
js
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js'
new RGBELoader().load('/textures/environment.hdr', (texture) => {
texture.mapping = THREE.EquirectangularReflectionMapping
scene.environment = texture // 用于环境光照
scene.background = texture // 或作为背景
})1
2
3
4
5
6
7
2
3
4
5
6
7
加载跨域图片
图片必须支持 CORS 才能作为纹理:
js
// 图片服务器需要设置 Access-Control-Allow-Origin 头
// 常用免费支持CORS的图床:
// - imgur
// - github (raw)
// - 任何带 Access-Control-Allow-Origin 的CDN
const texture = textureLoader.load(
'https://example.com/image.png',
undefined,
undefined,
(error) => {
console.error('加载失败,可能需要配置CORS:', error)
}
)1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
视频纹理
把视频作为纹理,实时更新:
js
const video = document.getElementById('video')
video.play()
const videoTexture = new THREE.VideoTexture(video)
videoTexture.colorSpace = THREE.SRGBColorSpace
const material = new THREE.MeshBasicMaterial({
map: videoTexture
})
const mesh = new THREE.Mesh(planeGeometry, material)
scene.add(mesh)1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
Canvas 纹理
用 Canvas 绘制生成纹理:
js
const canvas = document.createElement('canvas')
canvas.width = 512
canvas.height = 512
const ctx = canvas.getContext('2d')
// 绘制
ctx.fillStyle = '#ff6b6b'
ctx.fillRect(0, 0, 512, 512)
ctx.fillStyle = '#ffffff'
ctx.font = '64px Arial'
ctx.fillText('Hello', 100, 100)
// 创建纹理
const canvasTexture = new THREE.CanvasTexture(canvas)
const material = new THREE.MeshBasicMaterial({
map: canvasTexture
})1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
实用技巧
灰度图转法线
没有法线贴图时,可以用灰度图模拟:
js
// 普通纹理也可以做法线效果
material.normalMap = colorTexture
material.normalScale.set(0.5, 0.5)1
2
3
2
3
优化纹理大小
纹理尺寸应为 2 的幂次方(如 256、512、1024、2048):
js
// ✅ 好:256×256, 512×512, 2048×2048
// ❌ 差:500×500, 1024×7681
2
2
纹理合并(Atlas)
减少 draw call,把多个小纹理合并成一张大图:
js
// 所有UI元素放在一张2048×2048的图上
// 通过 offset 和 repeat 切分不同部分
iconTexture.offset.set(0, 0.5) // 取下半部分
iconTexture.repeat.set(0.25, 0.5) // 宽高各1/41
2
3
4
2
3
4