一、材质系统:从基础到高级应用
材质是控制3D物体视觉表现的核心,需根据场景需求选择合适的材质类型,以下是实战中最常用的材质及技巧:
1. 基础材质:快速实现简单视觉效果
| 材质类型 | 适用场景 | 核心API与技巧 |
|---|---|---|
MeshBasicMaterial |
无光照的2D平面/UI元素 | - 关闭深度测试:depthTest: false(用于UI层) - 使用map加载纹理,transparent: true实现透明 |
MeshLambertMaterial |
静态漫反射场景(低性能设备) | - 仅支持方向光/环境光,不支持高光 - 配合flatShading: true实现低多边形风格 |
MeshPhongMaterial |
需要高光效果的静态场景 | - shininess控制高光强度,specular设置高光颜色 - 性能略高于PBR材质 |
实战示例:UI层透明平面
javascript
// 创建无光照的UI材质
const uiMaterial = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load('ui-bg.png'),
transparent: true,
depthTest: false, // 避免遮挡3D场景
side: THREE.DoubleSide // 双面渲染
});
const uiPlane = new THREE.Mesh(new THREE.PlaneGeometry(2, 1), uiMaterial);
uiPlane.position.z = 5; // 置于3D场景前方
scene.add(uiPlane);
2. PBR材质:物理真实感渲染(推荐)
MeshStandardMaterial/MeshPhysicalMaterial是实战中最常用的PBR材质,支持金属度/粗糙度、环境光遮蔽(AO)等物理属性,适配WebGIS中建筑、地形的真实感渲染:
核心API与技巧
javascript
// 创建PBR材质(适配glTF模型材质标准)
const pbrMaterial = new THREE.MeshStandardMaterial({
color: 0xffffff,
metalness: 0.1, // 金属度(0-1,0为非金属)
roughness: 0.8, // 粗糙度(0-1,0为镜面)
map: baseColorTexture, // 基础颜色纹理
aoMap: aoTexture, // 环境光遮蔽纹理
normalMap: normalTexture, // 法线纹理(增强细节)
transparent: true,
alphaTest: 0.5 // 透明裁剪(避免半透明锯齿)
});
// 技巧:动态修改材质属性
pbrMaterial.color.setHex(0xff0000); // 实时改变颜色
pbrMaterial.roughness = 0.3; // 降低粗糙度实现镜面效果
实战优化
- 纹理复用:多个模型共享同一材质纹理,减少GPU内存占用
- 环境贴图 :添加
envMap实现反射效果(需配合PMREMGenerator生成环境贴图) - 着色器扩展 :通过
onBeforeCompile方法自定义材质着色器(如实现渐变、纹理动画)
3. 高级材质:ShaderMaterial自定义渲染
当内置材质无法满足需求时,使用ShaderMaterial编写自定义GLSL着色器,实现特殊视觉效果(如渐变、流体、动态纹理):
实战示例:渐变材质
javascript
const gradientMaterial = new THREE.ShaderMaterial({
uniforms: {
topColor: { value: new THREE.Color(0x001122) },
bottomColor: { value: new THREE.Color(0xffffff) },
offset: { value: 33 },
exponent: { value: 0.6 }
},
vertexShader: `
varying vec3 vWorldPosition;
void main() {
vec4 worldPosition = modelMatrix * vec4(position, 1.0);
vWorldPosition = worldPosition.xyz;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform vec3 topColor;
uniform vec3 bottomColor;
uniform float offset;
uniform float exponent;
varying vec3 vWorldPosition;
void main() {
float h = normalize(vWorldPosition + offset).y;
gl_FragColor = vec4(mix(bottomColor, topColor, max(pow(max(h, 0.0), exponent), 0.0)), 1.0);
}
`,
side: THREE.BackSide // 用于天空盒等场景
});
二、模型加载:主流格式与实战技巧
实战中常用的模型格式包括glTF(推荐)、FBX、OBJ,以下是各格式的加载方案与优化技巧:
1. 推荐格式:glTF/GLB(Three.js官方首选)
glTF是Three.js原生支持的高效模型格式,支持材质、动画、骨骼等完整属性,且文件体积小,加载速度快。
核心API:GLTFLoader
javascript
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; // 用于Draco压缩模型
// 初始化加载器
const loader = new GLTFLoader();
// 启用Draco压缩加载(需提前引入draco解码器)
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('path/to/draco/'); // 解码器路径(从three.js/examples/jsm/libs/draco复制)
loader.setDRACOLoader(dracoLoader);
// 加载模型
loader.load(
'models/model.glb', // 模型路径
(gltf) => {
const model = gltf.scene;
// 模型后处理
model.scale.set(1, 1, 1); // 调整缩放
model.position.set(0, 0, 0); // 调整位置
model.traverse((child) => {
if (child.isMesh) {
child.castShadow = true; // 开启阴影
child.receiveShadow = true;
// 替换材质(如需自定义)
child.material = new THREE.MeshStandardMaterial({
map: child.material.map, // 保留原纹理
metalness: 0.5
});
}
});
scene.add(model);
// 控制模型动画
if (gltf.animations.length > 0) {
const mixer = new THREE.AnimationMixer(model);
const action = mixer.clipAction(gltf.animations[0]);
action.play();
// 动画更新(需在render循环中调用)
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
mixer.update(delta);
}
animate();
}
},
(xhr) => {
console.log(`加载进度:${(xhr.loaded / xhr.total) * 100}%`);
},
(error) => {
console.error('模型加载失败:', error);
}
);
glTF实战技巧
- 模型优化 :使用
glTF-Pipeline或Blender导出时启用Draco压缩,减少文件体积 - 坐标系转换 :glTF默认Y轴向上,若模型为Z轴向上(如CAD导出),需旋转模型:
model.rotation.x = -Math.PI / 2 - 材质复用:遍历模型节点共享材质,避免重复创建
- LOD实现 :为复杂模型创建多细节层级,使用
LOD类动态切换
2. 其他格式加载
FBX格式(适用于带骨骼动画的模型)
javascript
import { FBXLoader } from 'three/addons/loaders/FBXLoader.js';
const loader = new FBXLoader();
loader.load('model.fbx', (fbx) => {
fbx.scale.set(0.1, 0.1, 0.1);
scene.add(fbx);
// 骨骼动画控制
const mixer = new THREE.AnimationMixer(fbx);
const action = mixer.clipAction(fbx.animations[0]);
action.play();
});
OBJ格式(适用于简单静态模型)
javascript
import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
import { MTLLoader } from 'three/addons/loaders/MTLLoader.js';
// 先加载材质,再加载模型
const mtlLoader = new MTLLoader();
mtlLoader.load('model.mtl', (materials) => {
materials.preload();
const objLoader = new OBJLoader();
objLoader.setMaterials(materials);
objLoader.load('model.obj', (obj) => {
scene.add(obj);
});
});
三、实战高频API与技巧
1. 模型性能优化API
- 实例化渲染 :使用
InstancedMesh渲染大量重复模型(如树木、建筑),大幅减少Draw Call
javascript
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const instancedMesh = new THREE.InstancedMesh(geometry, material, 1000); // 1000个实例
// 设置每个实例的位置
for (let i = 0; i < 1000; i++) {
const matrix = new THREE.Matrix4();
matrix.setPosition(Math.random() * 100 - 50, 0, Math.random() * 100 - 50);
instancedMesh.setMatrixAt(i, matrix);
}
scene.add(instancedMesh);
- 视锥体剔除 :开启
frustumCulled: true(默认开启),自动剔除视窗外的模型 - LOD层级 :使用
LOD类为模型创建多细节层级
javascript
const lod = new THREE.LOD();
const highDetail = new THREE.Mesh(highGeometry, material);
const mediumDetail = new THREE.Mesh(mediumGeometry, material);
const lowDetail = new THREE.Mesh(lowGeometry, material);
lod.addLevel(highDetail, 0); // 0-100米显示高细节
lod.addLevel(mediumDetail, 100); // 100-300米显示中细节
lod.addLevel(lowDetail, 300); // 300米以上显示低细节
scene.add(lod);
2. 材质与纹理优化
- 纹理压缩 :使用
KTX2Loader加载压缩纹理,减少内存占用 - 纹理复用:多个材质共享同一纹理对象,避免重复加载
- Mipmap生成 :为纹理开启
generateMipmaps: true,提升远处纹理清晰度
javascript
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('texture.jpg');
texture.generateMipmaps = true;
texture.minFilter = THREE.LinearMipmapLinearFilter;
3. 交互与控制API
- 轨道控制器 :
OrbitControls实现模型旋转、缩放、平移
javascript
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // 阻尼效果(平滑移动)
controls.dampingFactor = 0.05;
controls.minDistance = 1;
controls.maxDistance = 100;
// 在render循环中更新控制器
function render() {
requestAnimationFrame(render);
controls.update();
renderer.render(scene, camera);
}
- 射线检测:实现模型点击交互
javascript
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
window.addEventListener('click', (event) => {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length > 0) {
const clickedObject = intersects[0].object;
console.log('点击的模型:', clickedObject);
// 点击后修改材质
clickedObject.material.color.setHex(0xff0000);
}
});
四、实战避坑指南
- 模型坐标系问题 :glTF默认Y轴向上,若模型为Z轴向上,需旋转模型:
model.rotation.x = -Math.PI / 2 - 材质透明问题 :半透明材质需关闭
depthWrite: false,避免遮挡其他透明物体 - 性能瓶颈 :使用
Stats.js监控帧率,通过WebGLRenderer.info查看Draw Call与内存占用 - 模型加载失败:检查模型路径是否正确,跨域问题需配置CORS,Draco解码器路径是否正确
五、工具与资源推荐
- 模型优化:glTF-Pipeline、Blender(导出glTF时启用Draco压缩)
- 材质资源:Poly Haven(免费PBR纹理)
- 调试工具:Three.js Inspector(浏览器插件,实时查看场景结构)
- 官方示例:Three.js Examples(https://threejs.org/examples/),包含大量实战场景代码