侧边栏壁纸
博主头像
MicroMatrix 博主等级

明月别枝惊鹊,清风半夜鸣蝉

  • 累计撰写 135 篇文章
  • 累计创建 41 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

Three.js 纹理知识点笔记

David
2026-04-28 / 0 评论 / 0 点赞 / 20 阅读 / 0 字

纹理是什么

纹理本质上是贴到几何体表面的图片,但它不只是“给模型上颜色”。同一组纹理可以控制颜色、透明度、凹凸、光照细节、金属感、粗糙度等多个视觉属性。课程里先用门的贴图举例,说明纹理可以从不同角度影响模型外观。


常见纹理类型

纹理类型 作用 记忆点
Color / Albedo 基础颜色贴图 把图片像素直接映射到模型表面
Alpha 透明度贴图 灰度图:白色可见,黑色不可见
Height 高度贴图 通过移动顶点制造起伏;需要足够细分才明显
Normal 法线贴图 不移动顶点,但改变光照计算,让表面看起来有细节
Ambient Occlusion 环境遮蔽贴图 伪造缝隙、凹陷处的阴影,增强对比
Metalness 金属度贴图 白色偏金属,黑色偏非金属
Roughness 粗糙度贴图 白色粗糙、反光散;黑色光滑、反光强
PBR 基于物理的渲染 常由金属度、粗糙度、法线等贴图共同实现更真实的材质

其中 Normal Map 很重要:它不会增加模型面数,却能模拟细节,性能比真的增加几何体细分更好。


加载纹理的几种方式

方式一:原生 JavaScript 加载图片

流程是:创建 Image → 设置 src → 图片加载完成后创建或更新 THREE.Texture

const image = new Image()
const texture = new THREE.Texture(image)

image.addEventListener('load', () => {
  texture.needsUpdate = true
})

image.src = '/textures/door/color.jpg'

这里的关键是:图片加载是异步的,所以图片加载完成后要设置 texture.needsUpdate = true,通知 Three.js 把纹理上传/更新到 GPU。课程中也强调,WebGL 需要把图片转换成 GPU 可用的纹理格式。


方式二:使用 TextureLoader

这是实际开发中更常用的写法:

const textureLoader = new THREE.TextureLoader()
const colorTexture = textureLoader.load('/textures/door/color.jpg')

const material = new THREE.MeshBasicMaterial({
  map: colorTexture
})

TextureLoader.load() 会返回一个 Texture,并在图片加载完成后自动更新。Three.js 官方文档也说明,TextureLoader 用于加载纹理,内部会通过 ImageLoader 加载图片。


方式三:使用 LoadingManager

当你有多张贴图需要加载,比如 color、alpha、normal、roughness、metalness 等,可以用 LoadingManager 统一监听加载状态:

const loadingManager = new THREE.LoadingManager()

loadingManager.onStart = () => {
  console.log('loading started')
}

loadingManager.onLoad = () => {
  console.log('loading finished')
}

loadingManager.onProgress = () => {
  console.log('loading progressing')
}

loadingManager.onError = () => {
  console.log('loading error')
}

const textureLoader = new THREE.TextureLoader(loadingManager)

const colorTexture = textureLoader.load('/textures/door/color.jpg')
const alphaTexture = textureLoader.load('/textures/door/alpha.jpg')
const normalTexture = textureLoader.load('/textures/door/normal.jpg')
const roughnessTexture = textureLoader.load('/textures/door/roughness.jpg')

适合做“加载进度条”“所有资源加载完再进入场景”等功能。课程页也明确提到,LoadingManager 适合在多个图片加载时统一处理开始、完成、进度和错误事件。


颜色空间:SRGBColorSpace

如果颜色贴图看起来发灰,通常是颜色空间没设置对。课程更新说明里提到,用作 mapmatcap 的纹理通常是 sRGB 编码,需要设置:

colorTexture.colorSpace = THREE.SRGBColorSpace

Three.js 官方颜色管理文档也说明,像 .map.emissiveMap 这种包含颜色信息的 PNG/JPEG 纹理,应标注为 SRGBColorSpace;而 normal、roughness 这类非颜色数据纹理通常保持 NoColorSpace

记法:

// 颜色贴图:需要
colorTexture.colorSpace = THREE.SRGBColorSpace

// 非颜色数据贴图:通常不需要
normalTexture.colorSpace // 默认 NoColorSpace 即可
roughnessTexture.colorSpace // 默认 NoColorSpace 即可
metalnessTexture.colorSpace // 默认 NoColorSpace 即可

纹理映射到材质

最基本的用法是把纹理放到材质的 map 属性里:

const material = new THREE.MeshBasicMaterial({
  map: colorTexture
})

map 是颜色贴图入口。后面学更真实的材质时,会用到:

const material = new THREE.MeshStandardMaterial({
  map: colorTexture,
  alphaMap: alphaTexture,
  aoMap: ambientOcclusionTexture
})

注意:MeshBasicMaterial 不受光照影响,适合先看贴图效果;如果要看 normal、metalness、roughness 这些跟光照相关的效果,一般要用 MeshStandardMaterial 或类似 PBR 材质。


纹理重复、偏移、旋转

Three.js 的纹理对象有这些常用属性:

texture.repeat.set(2, 3)
texture.offset.set(0.5, 0.25)
texture.rotation = Math.PI * 0.25
texture.center.set(0.5, 0.5)

含义:

属性 作用
repeat 控制纹理重复次数
offset 控制纹理偏移
rotation 控制纹理旋转,单位是弧度
center 控制旋转中心,(0.5, 0.5)是纹理中心

Three.js 文档说明,repeat 大于 1 时,需要配合 wrapS / wrapT 设置成 RepeatWrappingMirroredRepeatWrapping,否则不会真正平铺。

texture.wrapS = THREE.RepeatWrapping
texture.wrapT = THREE.RepeatWrapping
texture.repeat.set(4, 2)

常见包裹模式:

模式 效果
ClampToEdgeWrapping 默认模式,边缘像素被拉伸
RepeatWrapping 正常重复
MirroredRepeatWrapping 镜像重复

纹理过滤:放大与缩小

纹理显示时会遇到两个问题:

一是贴图被放大,比如低分辨率图片贴到很大的面上;二是贴图被缩小,比如高分辨率贴图在远处只占几个像素。

对应两个属性:

texture.magFilter = THREE.NearestFilter
texture.minFilter = THREE.LinearMipmapLinearFilter
属性 什么时候用 常见值
magFilter 纹理被放大时 NearestFilter/LinearFilter
minFilter 纹理被缩小时 可使用 mipmap 相关过滤

NearestFilter 会产生像素风、颗粒感;LinearFilter 会更平滑。Three.js 文档中也说明,magFilter 控制纹理放大采样,minFilter 控制纹理缩小采样,默认 magFilterLinearFilter,默认 minFilterLinearMipmapLinearFilter


Mipmaps:远处纹理优化

Mipmaps 可以理解为:Three.js / GPU 为同一张纹理准备一系列逐级缩小的版本。物体远离摄像机时,不再硬采样原始大图,而是使用更合适的小图版本。

优点:

  • 减少远处纹理闪烁。
  • 提高远处显示质量。
  • 在很多情况下提升采样效率。

代价:

  • 会额外占用显存。
  • 不一定适合像素风纹理。

Three.js 文档中提到,纹理默认会生成 mipmaps;如果你手动创建 mipmaps,可以把 generateMipmaps 设为 false


纹理内存与性能

纹理往往是 Three.js 项目中最吃显存的资源之一。Three.js manual 给出的经验公式是:

纹理显存 ≈ width × height × 4 × 1.33 bytes

这里的重点是:JPG / PNG 文件在硬盘上很小,不代表进 GPU 后也小。进入 GPU 后通常会按像素展开,占用大小主要取决于图片分辨率。

优化建议:

  • 不要盲目使用超大尺寸贴图。
  • 能用 1024 就不要用 4096。
  • JPG 适合无透明通道的颜色贴图。
  • PNG 适合需要透明通道或清晰边缘的贴图。
  • 多张贴图时考虑纹理图集 Texture Atlas。
  • 生产项目可考虑压缩纹理格式。

实战记忆版流程

做一个带门贴图的材质,可以这样记:

const loadingManager = new THREE.LoadingManager()
const textureLoader = new THREE.TextureLoader(loadingManager)

const colorTexture = textureLoader.load('/textures/door/color.jpg')
colorTexture.colorSpace = THREE.SRGBColorSpace

const alphaTexture = textureLoader.load('/textures/door/alpha.jpg')
const normalTexture = textureLoader.load('/textures/door/normal.jpg')
const roughnessTexture = textureLoader.load('/textures/door/roughness.jpg')
const metalnessTexture = textureLoader.load('/textures/door/metalness.jpg')

const material = new THREE.MeshStandardMaterial({
  map: colorTexture,
  alphaMap: alphaTexture,
  aoMap: ambientOcclusionTexture
  transparent: true
})

推荐阅读文章

PBR的介绍

最后总结

这节纹理课的核心不是“怎么贴一张图”,而是理解:纹理是一组控制材质外观的数据。颜色贴图控制表面颜色,Alpha 控制透明,Height / Normal 控制凹凸细节,AO 控制缝隙阴影,Metalness / Roughness 共同服务于 PBR 真实感渲染。加载时优先用 TextureLoader,多资源用 LoadingManager,颜色贴图要设置 THREE.SRGBColorSpace,性能上要关注尺寸、mipmap、过滤方式和显存占用。

0

评论区