从零开始学习three.js(8):贴图深度解析,从基础到高级应用

596 阅读5分钟

在 Three.js 中,贴图(Texture)是为 3D 物体表面添加纹理和细节的关键技术。通过贴图,我们可以将图像、视频或其他媒体映射到物体表面,使其看起来更加真实和生动。本文将详细介绍 Three.js 中的贴图类型、加载方式以及如何通过贴图实现丰富的视觉效果。具体可实现:

  • 基础颜色纹理:替代单一颜色,呈现复杂图案(如木纹、砖墙)
  • 物理细节模拟- 通过特殊贴图实现凹凸、金属质感等效果
  • 环境交互:实现反射、折射等光学特性 核心原理是将图像数据通过UV坐标映射到几何体表面,Three.js提供TextureLoader等加载器实现这一过程。

1. 贴图的基本原理

贴图的核心是将图像或视频数据映射到几何体的表面。在 Three.js 中,贴图通过 Texture 对象实现,它包含了图像或视频的数据,并提供了控制贴图行为的属性和方法。贴图的映射方式称为 UV 映射,它决定了图像如何被拉伸或挤压以覆盖几何体。

2. 常见的贴图类型

2.1 纹理贴图(Texture)

纹理贴图是最常见的类型,用于将图像映射到物体表面。它可以通过 TextureLoader 加载图像文件。

const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load("path/to/texture.jpg");

2.2 视频纹理(VideoTexture)

视频纹理可以将视频内容动态映射到物体表面,适合创建动态背景或交互效果。

const video = document.createElement("video");
video.src = "path/to/video.mp4";
video.play();

const videoTexture = new THREE.VideoTexture(video);

2.3 立方纹理(CubeTexture)

立方纹理用于环境反射或天空盒,它由六个图像组成,分别对应立方体的六个面。

const cubeTextureLoader = new THREE.CubeTextureLoader();
const cubeTexture = cubeTextureLoader.load([
  "path/to/posx.jpg", "path/to/negx.jpg",
  "path/to/posy.jpg", "path/to/negy.jpg",
  "path/to/posz.jpg", "path/to/negz.jpg"
]);

2.4 法线贴图(NormalMap)

法线贴图用于模拟物体表面的凹凸效果,增强细节。常用于砖墙/岩石细节增强。

const normalTexture = textureLoader.load("path/to/normal.jpg");

2.5 环境光遮蔽贴图(AO Map)

遮蔽贴图用于模拟间接照明,即环境中光线被阻挡的程度。它通过减少阴影区域的亮度来增强深度感和体积感,使模型看起来更加立体。

const aoTexture = textureLoader.load("path/to/ao.jpg");

2.6 自发光贴图(EmissiveMap)

自发光贴图可以让物体表面在黑暗中发光,常用于霓虹灯或发光标志。

const emissiveTexture = textureLoader.load("path/to/emissive.jpg");

2.7 金属度贴图(MetalnessMap)

用于模拟物体表面的金属质感。常用于PBR材质制作。

const metalnessTexture = textureLoader.load("path/to/metalness.jpg");

2.7 粗糙度贴图(RoughnessMap)

用于模拟物体表面的粗糙度。粗糙度贴图可以用于模拟各种现实生活中的物体表面,如木材、石头、金属等。通过调整粗糙度,可以更好地反映这些物体的自然质感‌。

const roughnessTexture = textureLoader.load("path/to/roughness.jpg");

2.8 高光贴图(SpecularMap)

用于模拟物体表面的高光效果。主要用于模拟具有光泽表面的物体,如金属、塑料、瓷器等。

const specularMapTexture = textureLoader.load("path/to/specularMap.jpg");

3. 加载贴图

加载贴图时,可以使用 TextureLoader,它支持加载成功、加载进度和加载失败的回调函数。

const textureLoader = new THREE.TextureLoader();

textureLoader.load(
  "path/to/texture.jpg",
  (texture) => {
    console.log("加载成功");
  },
  (xhr) => {
    console.log("加载进度", (xhr.loaded / xhr.total * 100) + "%");
  },
  (error) => {
    console.error("加载失败", error);
  }
);

此外,LoadingManager 可以全局管理加载进度。

const loadingManager = new THREE.LoadingManager();
loadingManager.onStart = () => console.log("加载开始");
loadingManager.onLoad = () => console.log("加载完成");
loadingManager.onProgress = () => console.log("正在加载");

const textureLoader = new THREE.TextureLoader(loadingManager);

4. 高级贴图技巧

1. 多贴图混合

material = new THREE.MeshStandardMaterial({
  map: diffuseTexture,    // 基础颜色
  normalMap: normalTexture, 
  roughnessMap: roughnessTexture,
  metalnessMap: metalnessTexture,
  displacementMap: heightTexture,
  displacementScale: 0.1  // 置换强度
});

2. HDR环境映射

javascript
// 加载HDR环境贴图
new RGBELoader().load('textures/env.hdr', (texture) => {
  texture.mapping = THREE.EquirectangularReflectionMapping;
  scene.environment = texture; // 全局环境光
  material.envMap = texture;   // 物体反射
});

3. 动态纹理控制

  • UV偏移texture.offset.set(x,y)

  • 旋转纹理texture.rotation = Math.PI/4

  • 平铺模式texture.wrapS = THREE.RepeatWrapping

5.贴图优化

5.1 优化贴图加载

问题描述:在three.js中,如果贴图加载过多或贴图分辨率过高,会导致性能下降。

解决方案

  1. 使用纹理压缩:使用纹理压缩可以减少加载时间和内存使用。例如,可以使用.dds格式的纹理,或者在导出时使用工具如texturepackerTexture LOD功能。
  2. 使用LOD(Level of Detail)技术:根据相机距离的不同,加载不同分辨率的纹理。
  3. 异步加载:使用TextureLoader的异步加载功能,确保在纹理加载完成前不渲染模型。

5.2 优化渲染循环

问题描述:在渲染循环中频繁更改贴图或进行不必要的渲染操作会导致性能问题。

解决方案

  1. 减少渲染次数:使用requestAnimationFrame控制渲染频率。
  2. 避免不必要的材质更新:只在必要时更新材质属性。
  3. 合理使用渲染状态:例如,避免频繁改变光源或摄像机属性。

6、常见报错处理

6.1. 报错问题解释(假设报错为“Uncaught TypeError: Cannot read properties of undefined (reading 'map')”)

解释:这个错误通常意味着你尝试访问一个未定义对象的map属性。这通常发生在材质对象未正确初始化或者在访问之前材质对象被设置为undefined

6.2. 问题的解决方法:

  1. 确保材质初始化:在设置任何材质属性之前,确保材质对象已经被正确创建和初始化。
  2. 检查材质赋值时机:确保在纹理加载完成后再给材质赋值。
  3. 错误处理:在代码中添加错误处理逻辑,防止程序崩溃。

更多three.js、cesium.js开源案例,请移至gitee.com/giser2017/t…