You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在MUI组件中使用three.js显示OBJ格式3D模型?

在MUI组件中用Three.js展示OBJ模型

问题分析

你的代码已成功加载OBJ模型,但缺少Three.js渲染的核心环节:未创建相机、渲染器,也未将渲染结果挂载到MUI组件的DOM节点上,同时存在状态管理和对象生命周期的不合理处理。

修正后的完整代码

import React, { useState, useEffect, useRef } from 'react';
import { Container, Typography, Box } from '@mui/material';
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader';
import * as THREE from 'three';

const useStyles = () => ({
  container: {
    marginTop: 2,
    marginBottom: 2,
  },
  canvas: {
    width: '100%',
    height: '600px',
    backgroundColor: '#f0f0f0',
  },
});

const View3DWound = (props) => {
  const [isModelLoaded, setIsModelLoaded] = useState(false);
  const { id } = useParams();
  const classes = useStyles();
  const canvasContainerRef = useRef(null);
  // 用ref存储Three.js核心对象,避免组件重复渲染时重复创建
  const threeObjectsRef = useRef({
    scene: null,
    camera: null,
    renderer: null,
    animationId: null,
  });

  useEffect(() => {
    const container = canvasContainerRef.current;
    if (!container) return;

    // 初始化Three.js核心对象
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(
      75,
      container.clientWidth / container.clientHeight,
      0.1,
      1000
    );
    const renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(container.clientWidth, container.clientHeight);
    renderer.setClearColor(0xf0f0f0);
    container.appendChild(renderer.domElement);

    // 添加光源,否则模型会显示为黑色
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
    scene.add(ambientLight);
    const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
    directionalLight.position.set(5, 5, 5);
    scene.add(directionalLight);

    // 加载OBJ模型
    const loader = new OBJLoader();
    loader.load(
      './assets/TechnicLEGO_CAR_1.obj',
      (object) => {
        scene.add(object);
        // 调整模型位置和缩放,适配相机视角
        object.position.set(0, 0, 0);
        object.scale.set(0.5, 0.5, 0.5);
        // 相机定位到能完整看到模型的位置
        camera.position.z = 5;
        setIsModelLoaded(true);
      },
      (xhr) => {
        console.log(`${(xhr.loaded / xhr.total) * 100}% 已加载`);
      },
      (error) => {
        console.error('模型加载出错:', error);
      }
    );

    // 动画循环,持续渲染场景
    const animate = () => {
      const animationId = requestAnimationFrame(animate);
      // 可选:添加模型旋转效果
      scene.children.forEach((child) => {
        if (child.type === 'Group') {
          child.rotation.y += 0.01;
        }
      });
      renderer.render(scene, camera);
      threeObjectsRef.current.animationId = animationId;
    };
    animate();

    // 保存核心对象到ref
    threeObjectsRef.current = { scene, camera, renderer, animationId: null };

    // 组件卸载时清理资源,避免内存泄漏
    return () => {
      cancelAnimationFrame(threeObjectsRef.current.animationId);
      renderer.dispose();
      container.removeChild(renderer.domElement);
    };
  }, []);

  return (
    <Container className={classes.container}>
      <Typography variant="h4">查看患者 {id} 的3D模型</Typography>
      <Box className={classes.canvas} ref={canvasContainerRef}>
        {!isModelLoaded && <Typography>模型加载中...</Typography>}
      </Box>
    </Container>
  );
};

export default View3DWound;

关键修正点

  • 使用useRef获取MUI的Box容器,用来挂载Three.js生成的canvas元素
  • 将Three.js的场景、相机、渲染器存储在ref中,避免组件重复渲染时重复创建实例
  • 添加环境光和方向光,解决模型加载后显示为黑色的问题
  • 调整相机位置与模型缩放比例,确保模型在视野内完整展示
  • 增加动画循环,实现场景持续渲染,可选添加模型旋转效果
  • 组件卸载时清理渲染器和动画帧,避免内存泄漏
  • 用isModelLoaded状态替代存储模型对象,更合理地管理加载状态

内容的提问来源于stack exchange,提问作者Thakur Saad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 21:11:29