定義
貼圖(Texture)是 Three.js 中用于為物體表面添加紋理的一種技術(shù)。它可以將圖像、視頻或其他類型的媒體映射到物體的表面,使其看起來更加真實和生動。
基本原理
貼圖的基本原理是將圖像或視頻映射到物體的表面,使其看起來更加真實和生動。在 Three.js 中,貼圖是通過 Texture 對象來實現(xiàn)的。Texture 對象包含了圖像或視頻的數(shù)據(jù),以及一些用于控制貼圖行為的屬性和方法。
類型
在 Three.js 中,常用的貼圖類型包括:
- 紋理貼圖(Texture): 紋理貼圖是最常見的貼圖類型,它可以將圖像映射到物體的表面。紋理貼圖可以通過 TextureLoader 加載圖像文件。
- 視頻紋理(VideoTexture): 視頻紋理是一種特殊的貼圖類型,它可以將視頻映射到物體的表面。視頻紋理可以通過 VideoTextureLoader 加載視頻文件。
- 立方紋理(CubeTexture): 立方紋理是一種特殊的貼圖類型,它可以將一個立方體的六個面分別映射到物體的表面。立方紋理可以通過 CubeTextureLoader 加載圖像文件。
- ...
Texture
屬性
- image: 紋理圖像的引用。可以通過設(shè)置該屬性來更新紋理圖像。
- needsUpdate: 一個布爾值,表示紋理是否需要更新。當紋理圖像發(fā)生變化時,需要將該屬性設(shè)置為 true。
//可以鏈式調(diào)用
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load("path/to/texture.jpg");
const material = new THREE.MeshBasicMaterial({ map: texture });
TextureLoader
TextureLoader 構(gòu)造函數(shù)通過 load 加載圖片是一個異步操作,所以 load 還有其它參數(shù)
- url: 紋理圖像的路徑。
- onLoad: 加載完成后的回調(diào)函數(shù)。該函數(shù)會在加載完成后被調(diào)用,參數(shù)為加載完成的紋理對象。
- onProgress: 加載進度的回調(diào)函數(shù)。該函數(shù)會在加載過程中被調(diào)用,參數(shù)為加載進度的對象。(暫不支持)
- onError: 加載失敗的回調(diào)函數(shù)。該函數(shù)會在加載失敗后被調(diào)用,參數(shù)為加載失敗的錯誤對象。
這個只是最基本的貼圖效果,如果想做比較細節(jié)的貼圖,在材質(zhì)里面除了 map 這個屬性還有:
- aoMap: 環(huán)境遮擋貼圖,用于模擬環(huán)境遮擋效果。
- envMap: 環(huán)境貼圖,用于模擬環(huán)境反射效果。
- lightMap: 光照貼圖,用于模擬光照效果。
- specularMap: 高光貼圖,用于模擬高光效果。
- alphaMap: 透明度貼圖,用于模擬物體表面的透明度效果。
- displacementMap: 紋理位移貼圖,用于模擬物體表面的位移效果。
- roughnessMap: 粗糙度貼圖,用于模擬物體表面的粗糙度效果。
- normalMap: 法線貼圖,用于模擬物體表面的法線效果。
- metalnessMap: 金屬度貼圖,用于模擬物體表面的金屬度效果。
- emissiveMap: 自發(fā)光貼圖,用于模擬物體表面的自發(fā)光效果。
- ...
示例
如果想實現(xiàn)更加逼真的效果,可以使用多個貼圖來模擬物體表面的不同特性。例如,可以使用環(huán)境貼圖來模擬環(huán)境反射效果,使用高光貼圖來模擬高光效果,使用法線貼圖來模擬物體表面的法線效果等等。
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 創(chuàng)建場景
const scene = new THREE.Scene();
// 創(chuàng)建相機
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 20, 0);
camera.lookAt(0, 0, 0);
// 創(chuàng)建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
//添加燈光
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
const directionLight = new THREE.DirectionalLight(0xffffff, 0.5);
directionLight.position.set(3, 3, 3);
scene.add(ambientLight, directionLight);
//創(chuàng)建貼圖
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load("./texture/basecolor.png");
//創(chuàng)建一個平面
const geometry = new THREE.PlaneGeometry(20, 20);
const material = new THREE.MeshStandardMaterial({
map: texture,
});
const plane = new THREE.Mesh(geometry, material);
plane.rotation.x = -Math.PI / 2;
scene.add(plane);
//創(chuàng)建控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 啟用阻尼效果
controls.enableDamping = true;
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
//監(jiān)聽窗口大小變化
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});

texture-init.png
這只是通過貼圖實現(xiàn)的效果,可以看出如果只是通過貼圖,并不能實現(xiàn)非常逼真的效果,所以接下來我們通過多種貼圖來實現(xiàn)更加逼真的效果。
會使用到環(huán)境光貼圖(aoMap)、法線貼圖(normalMap)、金屬度貼圖(metalnessMap)、粗糙度貼圖(roughnessMap)、自發(fā)光貼圖(emissiveMap)、紋理位移貼圖(displacementMap)。
// 省略...
//創(chuàng)建貼圖
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load("./texture/basecolor.png"); //基礎(chǔ)顏色貼圖
const aoTexture = textureLoader.load("./texture/ao.png"); //環(huán)境光貼圖
const normalTexture = textureLoader.load("./texture/normal.png"); //法線貼圖
const roughnessTexture = textureLoader.load("./texture/roughness.png"); //粗糙度貼圖
const heightTexture = textureLoader.load("./texture/height.png"); //紋理位移貼圖
const emissiveTexture = textureLoader.load("./texture/emissive.png"); //自發(fā)光貼圖
const metalnessTexture = textureLoader.load("./texture/metallic.png"); //金屬度貼圖
//創(chuàng)建一個平面
const geometry = new THREE.PlaneGeometry(20, 20);
const material = new THREE.MeshStandardMaterial({
map: texture,
aoMap: aoTexture,
normalMap: normalTexture,
roughnessMap: roughnessTexture,
displacementMap: heightTexture,
emissiveMap: emissiveTexture,
metalnessMap: metalnessTexture,
});
const plane = new THREE.Mesh(geometry, material);
plane.rotation.x = -Math.PI / 2;
scene.add(plane);
//省略...