Three.js 贴图:给 3D 世界穿上花衣裳

305 阅读5分钟

在 Three.js 的魔法世界里,我们搭建的 3D 模型就像一个个等待盛装出席舞会的 “裸模”。它们光秃秃地站在那里,虽然已经有了迷人的身材(几何形状)和挺拔的身姿(空间位置),但总觉得缺了点什么 —— 没错,就是那件能让它们惊艳全场的 “衣裳”,而这件衣裳,就是我们今天要讲的贴图

一、揭开贴图的神秘面纱

从底层原理来看,贴图本质上就是一张二维图像,它会被 “包裹” 在三维模型表面,给模型赋予丰富的视觉细节。这就好比你给一个素色的陶瓷花瓶贴上精美的贴纸,原本平淡无奇的花瓶瞬间变得五彩斑斓。在计算机的世界里,显卡就像是一位心灵手巧的裁缝,它会按照一定的规则,把二维图像准确地 “缝制” 到三维模型上。

1.1 贴图的基本类型

Three.js 支持多种类型的贴图,常见的有纹理贴图法线贴图高光贴图等。

  • 纹理贴图:这是最基础、最常用的贴图类型,就像给模型贴上一张高清照片。比如你想创建一个木纹桌面,只需要找一张逼真的木纹图片作为纹理贴图,贴到桌面模型上,瞬间就能让桌面看起来质感十足。
// 加载纹理贴图
const textureLoader = new THREE.TextureLoader();
const woodTexture = textureLoader.load('wood.jpg');
// 创建材质并应用纹理
const material = new THREE.MeshBasicMaterial({ map: woodTexture });
  • 法线贴图:它就像给模型添加了 “凹凸滤镜”,通过改变模型表面的光照计算方式,让模型看起来有凹凸不平的效果。想象一下,你在平面上贴了一张有岩石纹路的法线贴图,原本平滑的表面在光线照射下,就能呈现出岩石那种坑坑洼洼的立体感,而实际上模型的几何形状并没有改变。
const normalMapLoader = new THREE.TextureLoader();
const normalMap = normalMapLoader.load('rock_normal.jpg');
const bumpMaterial = new THREE.MeshPhongMaterial({ 
    map: woodTexture,
    normalMap: normalMap,
    normalScale: new THREE.Vector2(1, 1) 
});
  • 高光贴图:决定了模型表面哪些地方更亮、更反光,就像是给模型的 “皮肤” 调整光泽度。比如制作一个金属质感的物体,通过高光贴图可以让它看起来闪闪发亮,仿佛镀了一层金属膜。
const specularMapLoader = new THREE.TextureLoader();
const specularMap = specularMapLoader.load('metal_specular.jpg');
const shinyMaterial = new THREE.MeshPhongMaterial({ 
    map: metalTexture,
    specularMap: specularMap,
    specular: 0x111111 
});

二、贴图的 “穿衣” 过程

当我们把贴图加载好之后,该怎么让它准确地 “穿” 到模型身上呢?这就涉及到UV 映射。UV 映射可以理解为给三维模型制作的 “裁剪图”,它定义了二维贴图上的每个点对应到三维模型表面的位置。

想象你要把一张世界地图贴到一个地球仪上,如果随便乱贴,肯定会变得乱七八糟。UV 映射就是告诉你,地图上的某个角落应该贴在地球仪的北极,另一个地方应该贴在赤道附近。在 Three.js 中,很多基础的几何模型都已经内置了合理的 UV 映射,我们可以直接使用。但如果是自定义的复杂模型,可能就需要手动调整 UV 映射了。

// 创建一个立方体
const geometry = new THREE.BoxGeometry(1, 1, 1);
// 为立方体指定材质(包含纹理贴图)
const cubeMaterial = new THREE.MeshBasicMaterial({ map: someTexture });
const cube = new THREE.Mesh(geometry, cubeMaterial);

三、进阶技巧:让贴图更 “丝滑”

3.1 纹理重复与偏移

有时候,我们的贴图尺寸可能不够大,覆盖不了整个模型,或者想实现一些有趣的图案效果,这时候就可以用到纹理重复和偏移。

  • 纹理重复:就像用同一块瓷砖铺满整个地面,通过设置repeat属性,可以让贴图在模型表面重复显示。
woodTexture.wrapS = THREE.RepeatWrapping;
woodTexture.wrapT = THREE.RepeatWrapping;
woodTexture.repeat.set(2, 2); // 在UV两个方向上都重复2次
  • 纹理偏移:则是把贴图在模型表面 “挪动” 一下位置,通过offset属性来实现。比如你想让木纹图案从模型的某个角落开始显示,就可以调整偏移值。
woodTexture.offset.set(0.5, 0.5); // 让纹理在UV方向上都偏移0.5

3.2 纹理过滤

当模型离我们很远或者进行快速移动时,为了防止贴图出现模糊、锯齿等难看的效果,我们需要设置纹理过滤。纹理过滤就像是给贴图加上一个 “美颜滤镜”,让它在各种情况下都能保持良好的视觉效果。Three.js 提供了多种过滤方式,如THREE.NearestFilter(最近邻过滤,速度快但可能有锯齿)、THREE.LinearFilter(线性过滤,效果平滑但计算稍复杂)等。

const filteredTexture = new THREE.TextureLoader().load('image.jpg');
filteredTexture.minFilter = THREE.LinearFilter;
filteredTexture.magFilter = THREE.LinearFilter;

四、实战演练:打造一个奇幻小屋

现在,我们就用刚刚学到的知识,来搭建一个充满童话色彩的小屋。

  1. 加载屋顶的瓦片纹理贴图和墙壁的砖块纹理贴图。
const roofTexture = textureLoader.load('tiles.jpg');
const wallTexture = textureLoader.load('bricks.jpg');
  1. 创建屋顶和墙壁的几何模型,并分别赋予对应的材质。
// 屋顶
const roofGeometry = new THREE.ConeGeometry(5, 2, 32);
const roofMaterial = new THREE.MeshBasicMaterial({ map: roofTexture });
const roof = new THREE.Mesh(roofGeometry, roofMaterial);
// 墙壁
const wallGeometry = new THREE.BoxGeometry(4, 3, 4);
const wallMaterial = new THREE.MeshBasicMaterial({ map: wallTexture });
const wall = new THREE.Mesh(wallGeometry, wallMaterial);
  1. 将屋顶和墙壁组合在一起,调整位置和角度,一个可爱的小屋就诞生啦!

在 Three.js 的世界里,贴图就像是我们手中的调色盘和画笔,能让原本单调的 3D 模型变得栩栩如生。掌握了贴图的应用技巧,你就能成为这个魔法世界里的顶级设计师,创造出无数令人惊叹的作品。快去发挥你的创意,给你的 3D 模型们都穿上华丽的 “衣裳” 吧!

上述内容从多方面展示了 Three.js 贴图应用。若你对某个部分想深入了解,或有其他功能添加需求,欢迎随时告诉我。