ThreeJs材质、模型加载、核心API

一、材质系统:从基础到高级应用

材质是控制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(推荐)、FBXOBJ,以下是各格式的加载方案与优化技巧:

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-PipelineBlender导出时启用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);
  }
});

四、实战避坑指南

  1. 模型坐标系问题 :glTF默认Y轴向上,若模型为Z轴向上,需旋转模型:model.rotation.x = -Math.PI / 2
  2. 材质透明问题 :半透明材质需关闭depthWrite: false,避免遮挡其他透明物体
  3. 性能瓶颈 :使用Stats.js监控帧率,通过WebGLRenderer.info查看Draw Call与内存占用
  4. 模型加载失败:检查模型路径是否正确,跨域问题需配置CORS,Draco解码器路径是否正确

五、工具与资源推荐

相关推荐
a1117762 天前
飞机躲避炸弹 网页游戏
前端·开源·html·threejs
a1117762 天前
3D赛车躲避游戏(html threeJS开源)
前端·游戏·3d·开源·html·threejs
a1117763 天前
水体渲染系统(html开源)
前端·开源·threejs·水体渲染
洲创实业4 天前
led灯珠材质寿命
材质·led灯珠
爱看书的小沐4 天前
【小沐杂货铺】基于Three.js渲染三维无人机Drone(WebGL / vue / react )
javascript·vue.js·react.js·无人机·webgl·three.js·drone
小猫咪yi7 天前
1、绘制点
webgl
小猫咪yi10 天前
7、三角形旋转
webgl
CG_MAGIC10 天前
写实皮肤材质PBR渲染:核心流程与关键参数
3d·材质·贴图·渲云渲染·3d软件
小猫咪yi11 天前
3、绘制线
webgl