要将压力值显示到 3D 模型上(而不是仅在 HTML 页面上显示),我们可以使用 Three.js 的 CSS2DRendererSprite + Canvas 纹理 方式。
其中,CSS2DRenderer 是最简单、清晰、可交互且支持中文的方式,特别适合在模型特定位置叠加文本标签(如“压力: 45.2 kPa”)。


✅ 解决方案:使用 CSS2DRenderer 在模型上方显示压力值

✨ 优势:

  • 文字始终面向屏幕(不会随模型旋转变形)
  • 支持 HTML/CSS 样式(字体、颜色、背景等)
  • 可绑定到模型的任意位置(如顶部、中心、某个零件)

📌 实现步骤:

  1. 引入 CSS2DRendererCSS2DObject
  2. 创建一个 <div> 作为压力标签
  3. 将该标签绑定到模型的某个位置(如模型中心或顶部)
  4. 每10秒更新该标签内容
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>3D模型 + 模型上显示压力值(修复版)</title>
    <style>
        body { margin: 0; overflow: hidden; font-family: Arial, sans-serif; }
        canvas { display: block; }
        #info {
            position: absolute;
            top: 10px;
            left: 10px;
            color: white;
            background: rgba(0,0,0,0.7);
            padding: 10px;
            border-radius: 5px;
            z-index: 100;
        }
        .pressure-label {
            color: cyan;
            font-size: 18px;
            font-weight: bold;
            background: rgba(0, 0, 0, 0.6);
            padding: 8px 12px;
            border-radius: 6px;
            white-space: nowrap;
            /* 不拦截鼠标事件 */
            pointer-events: none;
        }
    </style>
</head>
<body>
    <div id="info">正在加载模型... 请等待</div>

    <script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/loaders/GLTFLoader.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/controls/OrbitControls.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/loaders/TextureLoader.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/renderers/CSS2DRenderer.js"></script>

    <script>
        // 场景
        const scene = new THREE.Scene();
        scene.background = new THREE.Color(0x222222); // 深色背景更易看清标签

        // 相机
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.set(0, 0, 5);

        // WebGL 渲染器
        const renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.gammaOutput = true;
        renderer.gammaFactor = 2.2;
        document.body.appendChild(renderer.domElement);

        // CSS2D 渲染器(用于标签)
        const labelRenderer = new THREE.CSS2DRenderer();
        labelRenderer.setSize(window.innerWidth, window.innerHeight);
        labelRenderer.domElement.style.position = 'absolute';
        labelRenderer.domElement.style.top = '0px';
        labelRenderer.domElement.style.pointerEvents = 'none'; // 👈 关键:允许鼠标穿透
        document.body.appendChild(labelRenderer.domElement);

        // 光照
        const ambientLight = new THREE.AmbientLight(0xffffff, 0.8);
        scene.add(ambientLight);
        const dirLight = new THREE.DirectionalLight(0xffffff, 1);
        dirLight.position.set(5, 5, 5);
        scene.add(dirLight);

        // 控制器(绑定到 WebGL 的 canvas)
        const controls = new THREE.OrbitControls(camera, renderer.domElement);
        controls.enableDamping = true;
        controls.dampingFactor = 0.05;

        let pressureLabelDiv = null;
        let pressureLabelObject = null;

        // 模拟压力值
        function getPressure() {
            return (Math.random() * 100).toFixed(1);
        }

        function updatePressure() {
            if (pressureLabelDiv) {
                pressureLabelDiv.textContent = `压力: ${getPressure()} kPa`;
            }
        }

        // 加载模型
        const loader = new THREE.GLTFLoader();
        loader.load(
            'well.glb', // 确保该文件存在!否则标签不会创建
            (gltf) => {
                const model = gltf.scene;
                scene.add(model);

                // 居中 & 缩放
                const box = new THREE.Box3().setFromObject(model);
                const center = box.getCenter(new THREE.Vector3());
                const size = box.getSize(new THREE.Vector3());
                const maxDim = Math.max(size.x, size.y, size.z);
                const fov = camera.fov * (Math.PI / 180);
                const dist = Math.abs(maxDim / 2 / Math.tan(fov / 2)) * 1.5;
                camera.position.set(center.x, center.y, center.z + dist);
                controls.target.copy(center);
                controls.update();

                // 创建压力标签
                pressureLabelDiv = document.createElement('div');
                pressureLabelDiv.className = 'pressure-label';
                pressureLabelDiv.textContent = '压力: -- kPa';

                pressureLabelObject = new THREE.CSS2DObject(pressureLabelDiv);
                // 放在模型顶部上方
                pressureLabelObject.position.copy(center);
                pressureLabelObject.position.y += size.y / 2 + 0.3; // 微调高度

                scene.add(pressureLabelObject);

                document.getElementById('info').textContent = '模型加载完成';
                updatePressure(); // 立即显示初始值
            },
            (xhr) => {
                if (xhr.lengthComputable) {
                    const percent = (xhr.loaded / xhr.total * 100).toFixed(0);
                    document.getElementById('info').textContent = `加载中 ${percent}%`;
                }
            },
            (error) => {
                console.error('模型加载失败:', error);
                document.getElementById('info').textContent = '❌ 模型加载失败,请检查 well.glb 是否存在';
            }
        );

        //10秒更新
        setInterval(updatePressure, 10000);

        // 窗口缩放
        window.addEventListener('resize', () => {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
            labelRenderer.setSize(window.innerWidth, window.innerHeight);
        });

        // 渲染循环
        function animate() {
            requestAnimationFrame(animate);
            controls.update();
            renderer.render(scene, camera);
            labelRenderer.render(scene, camera); // 必须调用
        }
        animate();
    </script>
</body>
</html>

在这里插入图片描述

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐