news 2026/6/7 21:45:48

three-bvh-csg glb分割

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
three-bvh-csg glb分割

复杂模型报错:

E:\project\3d_label\three-bvh-csg-main\examples\demo.html

<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8" /> <title>Stable GLB BVH CSG Cut</title> <style> body { margin: 0; overflow: hidden; } canvas { display: block; } </style> </head> <body> <script type="importmap"> { "imports": { "three": "https://unpkg.com/three@0.160.0/build/three.module.js", "three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/", "three-bvh-csg": "https://unpkg.com/three-bvh-csg@0.0.18/build/index.module.js" } } </script> <script type="module"> import * as THREE from "three"; import { OrbitControls } from "three/addons/controls/OrbitControls.js"; import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js"; import { Brush, Evaluator, INTERSECTION, SUBTRACTION } from "three-bvh-csg"; /* ===================== 基础场景 ===================== */ const scene = new THREE.Scene(); scene.background = new THREE.Color(0x222222); const camera = new THREE.PerspectiveCamera(60, innerWidth/innerHeight, 0.1, 1000); camera.position.set(3, 3, 3); const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(innerWidth, innerHeight); document.body.appendChild(renderer.domElement); const controls = new OrbitControls(camera, renderer.domElement); controls.update(); scene.add(new THREE.AmbientLight(0xffffff, 0.8)); const light = new THREE.DirectionalLight(0xffffff, 1); light.position.set(5, 10, 5); scene.add(light); /* ===================== CSG ===================== */ const evaluator = new Evaluator(); /* ===================== GLB ===================== */ const loader = new GLTFLoader(); function cleanGeometry(mesh) { mesh.updateWorldMatrix(true, false); let geo = mesh.geometry.clone(); // ❗ 1. 转 world space geo.applyMatrix4(mesh.matrixWorld); // ❗ 2. 确保有 position attribute if (!geo.attributes || !geo.attributes.position) { console.warn("Invalid geometry skipped"); return null; } // ❗ 3. 转 indexed(关键) if (!geo.index) { geo = THREE.BufferGeometryUtils.mergeVertices(geo); } // ❗ 4. 清理 NaN / 冗余 geo.computeVertexNormals(); geo.computeBoundingBox(); geo.computeBoundingSphere(); return geo; } loader.load("yotown_clear.glb", (gltf) => { const model = gltf.scene; scene.add(model); model.updateMatrixWorld(true); /* ===================== 计算包围盒 ===================== */ const box = new THREE.Box3().setFromObject(model); const center = new THREE.Vector3(); const size = new THREE.Vector3(); box.getCenter(center); box.getSize(size); console.log("center:", center); console.log("size:", size); /* ===================== 创建切割平面 ===================== */ const planeGeo = new THREE.PlaneGeometry(200, 200); const planeMesh = new THREE.Mesh( planeGeo, new THREE.MeshBasicMaterial() ); planeMesh.position.copy(center); planeMesh.rotation.x = Math.PI / 2; // 水平切割(XZ 平面) /* ===================== 收集 meshes ===================== */ const meshes = []; model.traverse(o => { if (o.isMesh) { o.geometry.computeVertexNormals(); meshes.push(o); } }); const upperGroup = new THREE.Group(); const lowerGroup = new THREE.Group(); /* ===================== 执行切割 ===================== */ meshes.forEach(mesh => { const geometry = cleanGeometry(mesh); if (!geometry) return; const brushA = new Brush(geometry); /* ===== 切割体 ===== */ const upperPlane = new Brush(new THREE.BoxGeometry(1000, 1000, 1000)); upperPlane.position.set(center.x, center.y + 1000, center.z); upperPlane.updateMatrixWorld(true); const lowerPlane = new Brush(new THREE.BoxGeometry(1000, 1000, 1000)); lowerPlane.position.set(center.x, center.y - 1000, center.z); lowerPlane.updateMatrixWorld(true); /* ===== 上半 ===== */ const upper = evaluator.evaluate( brushA, upperPlane, INTERSECTION ); if (upper?.geometry?.attributes?.position) { upperGroup.add(new THREE.Mesh(upper.geometry, mesh.material.clone())); } /* ===== 下半 ===== */ const lower = evaluator.evaluate( brushA, lowerPlane, INTERSECTION ); if (lower?.geometry?.attributes?.position) { const m = new THREE.Mesh(lower.geometry, mesh.material.clone()); m.position.x += size.x * 1.2; lowerGroup.add(m); } }); scene.add(upperGroup); scene.add(lowerGroup); }); /* ===================== 渲染 ===================== */ function animate() { requestAnimationFrame(animate); renderer.render(scene, camera); } animate(); window.addEventListener("resize", () => { camera.aspect = innerWidth / innerHeight; camera.updateProjectionMatrix(); renderer.setSize(innerWidth, innerHeight); }); </script> </body> </html>
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/7 21:36:39

微信小程序日历组件架构解析:企业级高性能日历实现方案

微信小程序日历组件架构解析&#xff1a;企业级高性能日历实现方案 【免费下载链接】wx_calendar 微信小程序&#xff0d;日历组件 &#x1f4c5; 项目地址: https://gitcode.com/gh_mirrors/wx/wx_calendar wx_calendar是一款专为微信小程序设计的专业级日历组件&#…

作者头像 李华
网站建设 2026/6/7 21:35:37

如何快速提升英雄联盟游戏效率:智能辅助工具的完整指南

如何快速提升英雄联盟游戏效率&#xff1a;智能辅助工具的完整指南 【免费下载链接】League-Toolkit An all-in-one toolkit for LeagueClient. Gathering power &#x1f680;. 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit LeagueAkari是一款基于官方…

作者头像 李华
网站建设 2026/6/7 21:34:52

041、NPU的固件设计:启动流程与自检

041、NPU的固件设计:启动流程与自检 去年调试某款自研NPU芯片时,遇到一个诡异的“冷启动死机”问题——芯片上电后,NPU核心死活不响应指令,但热重启却一切正常。用逻辑分析仪抓了三天波形,最后发现是固件里一个自检超时参数设得太紧,导致电源纹波稍大就触发看门狗复位。…

作者头像 李华
网站建设 2026/6/7 21:28:11

Windows任务栏透明化神器:3分钟让你的桌面焕然一新!

Windows任务栏透明化神器&#xff1a;3分钟让你的桌面焕然一新&#xff01; 【免费下载链接】TranslucentTB A lightweight utility that makes the Windows taskbar translucent/transparent. 项目地址: https://gitcode.com/gh_mirrors/tr/TranslucentTB 还在为Window…

作者头像 李华
网站建设 2026/6/7 21:27:22

镜像视界高清视场还原技术,实现司法办案区全场景视频孪生呈现

镜像视界高清视场还原技术&#xff0c;实现司法办案区全场景视频孪生呈现一、技术前言深耕司法办案区智能化建设多年&#xff0c;我深知办案区是讯问、询问、取证、羁押集中的核心涉密场景&#xff0c;要求“无盲区、高保真、可回溯、强安全”。传统方案长期面临“画面割裂、广…

作者头像 李华