Three.js - 纹理
Three.js - 纹理
Section titled “Three.js - 纹理”纹理是将图像映射到3D模型表面,以增加颜色、细节和真实感。在现代3D图形中,特别是基于物理渲染(PBR)技术中,纹理不仅仅是添加颜色——它们还定义了粗糙度、金属度和表面高度等属性。本章将介绍如何加载和应用这些强大的贴图。
加载基础纹理
Section titled “加载基础纹理”纹理的基础是加载图像文件。Three.js为此提供了 TextureLoader(纹理加载器)。加载的纹理可以应用于材质的 map 属性,该属性对应于基础颜色。
import * as THREE from 'three';
// 1. 创建一个 TextureLoader 实例const textureLoader = new THREE.TextureLoader();
// 2. 加载图像文件const colorTexture = textureLoader.load('/textures/door/color.jpg');
// 3. 将其应用于材质const material = new THREE.MeshStandardMaterial({ map: colorTexture });加载是异步的。纹理可能不会立即显示。Three.js 在后台处理此事,但对于更复杂的场景,你可以使用加载器的回调函数来了解何时加载完成。
配置纹理:平铺与过滤
Section titled “配置纹理:平铺与过滤”你可以控制纹理的重复方式(平铺)以及在放大或缩小时的外观。
- 纹理包裹(平铺):使用
texture.wrapS(水平方向)和texture.wrapT(垂直方向)配合THREE.RepeatWrapping,使纹理在表面上平铺。然后你可以通过texture.repeat.set(x, y)设置重复次数。 - 过滤:当纹理被拉伸到大面积(
magFilter)或缩小到小面积(minFilter)时,过滤决定了像素的渲染方式。THREE.LinearFilter(线性过滤)提供平滑的外观,而THREE.NearestFilter(最近点过滤)则呈现锐利、像素化的外观。为了在minFilter上获得最佳质量,请使用 Mipmap 选项之一,例如THREE.LinearMipmapLinearFilter。
// 使纹理在水平方向重复4次,垂直方向重复2次colorTexture.wrapS = THREE.RepeatWrapping;colorTexture.wrapT = THREE.RepeatWrapping;colorTexture.repeat.set(4, 2);
// 用于像素艺术或锐利纹理colorTexture.magFilter = THREE.NearestFilter;
// 为 PBR 材质设置颜色空间colorTexture.colorSpace = THREE.SRGBColorSpace;重要提示:对于 MeshStandardMaterial 等 PBR 材质,包含颜色信息(如 map)的纹理,务必设置正确的 colorSpace(颜色空间)。THREE.SRGBColorSpace 是大多数颜色纹理的正确选择。
用于真实感的PBR纹理贴图
Section titled “用于真实感的PBR纹理贴图”MeshStandardMaterial 通过使用多种类型的纹理贴图来模拟逼真的光照。这些通常是灰度图或专用图像,用于控制表面的特定物理属性。
- 法线贴图(
normalMap):在不增加多边形数量的情况下,添加凹凸、划痕和纹理等精细表面细节。它通过操纵光的反射方式来模拟细节。这比凹凸贴图更高级。 - 环境光遮蔽(AO)贴图(
aoMap):一种灰度贴图,用于在光线被阻挡的缝隙和角落处添加伪造阴影。它使物体看起来更具立体感,不那么扁平。这需要第二组UV坐标(uv2)。 - 粗糙度贴图(
roughnessMap):一种灰度贴图,白色表示非常粗糙的表面(漫反射,如粉笔),黑色表示非常光滑的表面(锐利反射,如镜子)。 - 金属度贴图(
metalnessMap):一种灰度贴图,白色表示表面100%是金属,黑色表示非金属(电介质)。 - 置换贴图(
displacementMap):与仅模拟细节的法线贴图不同,置换贴图实际移动了几何体的顶点。这可以创建真实的几何细节,但计算成本高昂,并且需要高度细分的网格。
示例:应用完整的PBR纹理集
Section titled “示例:应用完整的PBR纹理集”此示例演示了如何将一套完整的PBR纹理应用于一个球体。它假设你的公共目录中有一个 /textures/ 文件夹,其中包含了一组纹理(颜色、法线、粗糙度等)。你可以在 AmbientCG 等网站上找到免费的PBR纹理集。
import * as THREE from 'three';import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
// 基础设置(场景、相机、渲染器、控制器,与前面的示例相同)// ...
/** * 纹理 */const textureLoader = new THREE.TextureLoader();
// 加载所有必需的 PBR 贴图const colorTexture = textureLoader.load('/textures/metal_plate/color.jpg');const aoTexture = textureLoader.load('/textures/metal_plate/ambientOcclusion.jpg');const heightTexture = textureLoader.load('/textures/metal_plate/height.png');const normalTexture = textureLoader.load('/textures/metal_plate/normal.jpg');const roughnessTexture = textureLoader.load('/textures/metal_plate/roughness.jpg');const metalnessTexture = textureLoader.load('/textures/metal_plate/metalness.jpg');
// 为颜色纹理设置正确的颜色空间colorTexture.colorSpace = THREE.SRGBColorSpace;
/** * 对象 */// 使用高细节几何体来查看置换效果const geometry = new THREE.SphereGeometry(1, 64, 64);
// 添加 aoMap 的 UV 属性。它与颜色贴图共享相同的 UV。geometry.setAttribute('uv2', new THREE.BufferAttribute(geometry.attributes.uv.array, 2));
const material = new THREE.MeshStandardMaterial({ map: colorTexture, aoMap: aoTexture, aoMapIntensity: 1, normalMap: normalTexture, roughnessMap: roughnessTexture, metalnessMap: metalnessTexture, displacementMap: heightTexture, displacementScale: 0.05 // 小心调整此值});
const sphere = new THREE.Mesh(geometry, material);scene.add(sphere);
/** * 光源 */const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);scene.add(ambientLight);
const pointLight = new THREE.PointLight(0xffffff, 20);pointLight.position.set(2, 3, 4);scene.add(pointLight);
// ... (动画循环和响应式处理,与前面的示例相同)纹理优化最佳实践
Section titled “纹理优化最佳实践”纹理通常是3D应用程序文件大小中最大的一部分。优化纹理对于实现快速加载时间至关重要。
- 文件格式:对于颜色贴图和其他无需透明度的贴图,使用
.jpg格式。仅当你需要 Alpha 通道时才使用.png格式。 - 分辨率:使用在视觉上仍保持良好效果的最小分辨率。为了广泛兼容性和Mipmap效率,请保持尺寸为二的幂次(例如,512x512,1024x1024)。
- 纹理压缩:对于生产环境,使用像 Basis Universal 这样的工具来创建超级压缩的
.basis或.ktx2文件,这些文件可以被GPU高效解码,从而显著减少内存使用和加载时间。