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

关于在Vite+Three.js+Vue.js项目中集成OpenCascade.js的技术求助

关于在Vite+Three.js+Vue.js项目中集成OpenCascade.js的技术求助

我之前刚好在Vite + Vue 3 + Three.js的项目里成功集成了OpenCascade.js,踩了好几个坑,给你分享下完整的步骤和避坑指南,应该能帮到你!

一、依赖安装与Vite基础配置

首先安装所需依赖:

npm install @opencascade/opencascade.js three @vitejs/plugin-vue

然后修改vite.config.js,解决WASM加载的跨域和预构建问题:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  // 排除OpenCascade.js的依赖预构建,避免Vite处理WASM出错
  optimizeDeps: {
    exclude: ['@opencascade/opencascade.js']
  },
  server: {
    // 必须设置这两个头,否则浏览器会阻止WASM加载(跨域隔离要求)
    headers: {
      'Cross-Origin-Embedder-Policy': 'require-corp',
      'Cross-Origin-Opener-Policy': 'same-origin'
    }
  },
  // 生产打包配置:避免Rollup代码分割破坏WASM模块
  build: {
    rollupOptions: {
      output: {
        manualChunks: undefined
      }
    }
  }
})

二、Vue组件中的实际集成(结合Three.js)

下面是一个完整的Vue单文件组件示例,包含OpenCascade.js初始化、创建3D形状并转换为Three.js几何体渲染:

<template>
  <div ref="canvasWrapper" class="canvas-container"></div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import * as THREE from 'three'

const canvasWrapper = ref(null)
let ocInstance = null
let renderer = null
let animationId = null

// 初始化OpenCascade.js并渲染
onMounted(async () => {
  try {
    // 异步加载并初始化OpenCascade.js
    const ocModule = await import('@opencascade/opencascade.js')
    ocInstance = await ocModule.default()
    console.log('✅ OpenCascade.js 初始化成功')

    // 1. 用OpenCascade创建一个简单的立方体
    const boxShape = new ocInstance.BRepPrimAPI_MakeBox(15, 15, 15).Shape()

    // 2. 把OpenCascade形状转换为Three.js可识别的几何体
    const threeGeometry = convertOCShapeToThreeGeometry(boxShape, ocInstance)

    // 3. 搭建Three.js场景
    const scene = new THREE.Scene()
    scene.background = new THREE.Color(0xf0f0f0)

    const mesh = new THREE.Mesh(
      threeGeometry,
      new THREE.MeshStandardMaterial({ color: 0x2196f3, metalness: 0.3, roughness: 0.5 })
    )
    scene.add(mesh)

    // 加入灯光
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.5)
    scene.add(ambientLight)
    const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8)
    directionalLight.position.set(5, 10, 7.5)
    scene.add(directionalLight)

    // 相机与渲染器
    const camera = new THREE.PerspectiveCamera(
      75,
      canvasWrapper.value.clientWidth / canvasWrapper.value.clientHeight,
      0.1,
      1000
    )
    camera.position.z = 40

    renderer = new THREE.WebGLRenderer({ antialias: true })
    renderer.setSize(canvasWrapper.value.clientWidth, canvasWrapper.value.clientHeight)
    canvasWrapper.value.appendChild(renderer.domElement)

    // 动画循环
    const animate = () => {
      animationId = requestAnimationFrame(animate)
      mesh.rotation.x += 0.005
      mesh.rotation.y += 0.005
      renderer.render(scene, camera)
    }
    animate()
  } catch (error) {
    console.error('❌ OpenCascade.js 初始化失败:', error)
  }
})

// 组件卸载时清理资源,避免内存泄漏
onUnmounted(() => {
  if (animationId) cancelAnimationFrame(animationId)
  if (renderer) renderer.dispose()
  // 清理OpenCascade实例(如果有提供销毁方法)
  if (ocInstance) {
    // 部分版本的OpenCascade.js提供了销毁接口,可按需调用
    // ocInstance.dispose()
  }
})

/**
 * 把OpenCascade形状转换为Three.js BufferGeometry的工具函数
 * @param {object} ocShape OpenCascade创建的Shape对象
 * @param {object} oc OpenCascade.js实例
 * @returns {THREE.BufferGeometry}
 */
function convertOCShapeToThreeGeometry(ocShape, oc) {
  // 先对OpenCascade形状进行三角化
  const meshBuilder = new oc.BRepMesh_IncrementalMesh_2(ocShape, 0.1)
  meshBuilder.Perform()

  const vertices = []
  const indices = []
  const normals = []

  // 遍历所有面
  const faceExplorer = new oc.TopExp_Explorer_1(ocShape, oc.TopAbs_ShapeEnum.TopAbs_FACE)
  while (faceExplorer.More()) {
    const face = oc.TopoDS.Face_1.DownCast(faceExplorer.Current())
    const location = new oc.TopLoc_Location()
    const triangulation = oc.BRep_Tool.Triangulation_1(face, location)

    if (triangulation) {
      const nodes = triangulation.Nodes()
      const triangles = triangulation.Triangles()
      const normalsPerFace = triangulation.Normals()

      // 提取顶点坐标
      for (let i = 1; i <= triangulation.NbNodes(); i++) {
        const point = nodes.Value(i)
        vertices.push(point.X(), point.Y(), point.Z())
        
        // 提取顶点法线(如果有)
        if (normalsPerFace) {
          const normal = normalsPerFace.Value(i)
          normals.push(normal.X(), normal.Y(), normal.Z())
        }
      }

      // 提取三角面索引(注意OpenCascade是1基索引,Three.js是0基)
      for (let i = 1; i <= triangulation.NbTriangles(); i++) {
        const tri = triangles.Value(i)
        const idx1 = tri.Value(1) - 1
        const idx2 = tri.Value(2) - 1
        const idx3 = tri.Value(3) - 1
        indices.push(idx1, idx2, idx3)
      }
    }

    faceExplorer.Next()
  }

  const geometry = new THREE.BufferGeometry()
  geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3))
  if (normals.length > 0) {
    geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3))
  } else {
    geometry.computeVertexNormals()
  }
  geometry.setIndex(indices)
  return geometry
}
</script>

<style scoped>
.canvas-container {
  width: 100vw;
  height: 100vh;
  margin: 0;
  padding: 0;
}
</style>

三、常见坑与解决方法

  • WASM加载报错(跨域):必须在Vite的server.headers中设置Cross-Origin-Embedder-Policy和Cross-Origin-Opener-Policy,否则浏览器会因为跨域隔离策略阻止WASM加载。
  • 同步导入导致的初始化失败:OpenCascade.js的WASM模块是异步加载的,绝对不能在Vue组件中同步导入后直接调用API,必须用await等待初始化完成。
  • 生产打包后白屏/报错:Vite默认的Rollup代码分割会把WASM模块拆分成多个chunk,导致加载失败,所以需要在build.rollupOptions.output中设置manualChunks: undefined禁用自动代码分割。
  • 内存泄漏:在Vue组件卸载时,一定要清理Three.js的渲染器、动画帧,以及OpenCascade的实例(如果有提供销毁方法),否则会导致浏览器内存占用持续升高。

如果你的项目还有具体的报错信息(比如控制台的错误日志),可以贴出来,这样能更精准地帮你定位问题!


内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 11:27:59