当前位置: 首页 > news >正文

用Open CASCADE从零到一:手把手教你用C++代码‘捏’一个3D瓶子模型

用Open CASCADE从零到一:手把手教你用C++代码‘捏’一个3D瓶子模型

在数字创作的世界里,3D建模往往让人联想到复杂的专业软件界面和繁琐的鼠标操作。但今天,我们将打破这种刻板印象,用程序员最熟悉的武器——代码,来创造一个有形的3D物体。这就像用数字黏土雕塑作品,只不过我们的工具是C++和Open CASCADE(OCCT)这个强大的几何内核。

Open CASCADE是一个开源的C++库,专为CAD/CAM/CAE应用程序开发而设计。与常见的图形API如OpenGL不同,OCCT提供了更高层次的几何抽象,让我们能够像真正的工程师一样思考形状的构造。想象一下,你不再是被动地使用建模工具,而是可以直接用数学语言描述物体的几何本质——这就是编程建模的魅力所在。

1. 环境准备与基础概念

在开始"捏"我们的数字瓶子之前,需要确保开发环境准备就绪。推荐使用支持C++17的编译器(如GCC 9+或MSVC 2019+),并通过CMake管理项目。Open CASCADE可以通过官网下载预编译库或从源码构建。

# 示例CMake配置片段 find_package(OpenCASCADE REQUIRED) add_executable(BottleModeler main.cpp) target_link_libraries(BottleModeler OpenCASCADE::TKernel OpenCASCADE::TKG3d)

OCCT的核心概念可以分为几何(Geometric)和拓扑(Topologic)两个层面:

几何元素处理数学描述:

  • gp_Pnt:表示3D空间中的点
  • gp_Vec:表示向量
  • Geom_Curve及其派生类:描述曲线几何

拓扑结构定义形状关系:

  • TopoDS_Vertex:对应几何点的零维形状
  • TopoDS_Edge:基于曲线的一维形状
  • TopoDS_Wire:由边连接成的环
  • TopoDS_Face:由线包围的曲面部分

理解这些基础概念后,我们可以开始构建瓶子的轮廓。就像雕塑家先勾勒大致形状一样,我们将从二维轮廓开始,然后通过拉伸等操作赋予其体积。

2. 构建瓶子基础轮廓

瓶子的轮廓设计从定义关键点开始。假设我们要创建一个高70mm、宽50mm、厚度30mm的瓶子,首先在XOY平面定义五个特征点:

Standard_Real myWidth = 50.0; Standard_Real myHeight = 70.0; Standard_Real myThickness = 30.0; gp_Pnt aPnt1(-myWidth/2, 0, 0); // 左端点 gp_Pnt aPnt2(-myWidth/2, -myThickness/4, 0); gp_Pnt aPnt3(0, -myThickness/2, 0); // 底部中心点 gp_Pnt aPnt4(myWidth/2, -myThickness/4, 0); gp_Pnt aPnt5(myWidth/2, 0, 0); // 右端点

将这些点连接起来形成轮廓曲线。左侧使用线段,底部使用圆弧,右侧同样使用线段:

// 创建底部圆弧 Handle(Geom_TrimmedCurve) anArc = GC_MakeArcOfCircle(aPnt2, aPnt3, aPnt4); // 创建左右线段 Handle(Geom_TrimmedCurve) aSegment1 = GC_MakeSegment(aPnt1, aPnt2); Handle(Geom_TrimmedCurve) aSegment2 = GC_MakeSegment(aPnt4, aPnt5);

将这些几何曲线转换为拓扑边,然后组合成闭合环:

TopoDS_Edge edge1 = BRepBuilderAPI_MakeEdge(aSegment1); TopoDS_Edge edge2 = BRepBuilderAPI_MakeEdge(anArc); TopoDS_Edge edge3 = BRepBuilderAPI_MakeEdge(aSegment2); TopoDS_Wire halfWire = BRepBuilderAPI_MakeWire(edge1, edge2, edge3);

为了得到完整的对称轮廓,我们通过镜像操作复制另一半:

gp_Ax1 mirrorAxis = gp::OX(); // 以X轴为镜像轴 gp_Trsf mirrorTransform; mirrorTransform.SetMirror(mirrorAxis); BRepBuilderAPI_Transform mirrorMaker(halfWire, mirrorTransform); TopoDS_Wire fullWire = BRepBuilderAPI_MakeWire( halfWire, TopoDS::Wire(mirrorMaker.Shape()) );

现在,我们已经有了一个完整的瓶子底部轮廓线。这个阶段就像完成了瓶子的二维设计图纸,接下来要将其转化为三维实体。

3. 从2D到3D:构建瓶体

将二维轮廓转化为三维实体最直接的方法是拉伸(Prism)。首先需要将轮廓线转换为面:

TopoDS_Face profileFace = BRepBuilderAPI_MakeFace(fullWire);

然后沿Z轴方向拉伸这个面,形成实体瓶身:

gp_Vec extrusionVec(0, 0, myHeight); TopoDS_Shape bottleBody = BRepPrimAPI_MakePrism(profileFace, extrusionVec);

此时的瓶子边缘还很锐利,我们需要添加圆角使其更真实。OCCT提供了方便的圆角工具:

BRepFilletAPI_MakeFillet filletMaker(bottleBody); // 遍历所有边并添加圆角 TopExp_Explorer edgeExplorer(bottleBody, TopAbs_EDGE); while(edgeExplorer.More()) { filletMaker.Add(myThickness/12, TopoDS::Edge(edgeExplorer.Current())); edgeExplorer.Next(); } bottleBody = filletMaker.Shape();

接下来添加瓶颈部分。瓶颈是一个圆柱体,位于瓶体顶部中心:

gp_Pnt neckLocation(0, 0, myHeight); gp_Ax2 neckAxis(neckLocation, gp::DZ()); Standard_Real neckRadius = myThickness/4; Standard_Real neckHeight = myHeight/10; TopoDS_Shape neck = BRepPrimAPI_MakeCylinder(neckAxis, neckRadius, neckHeight); // 将瓶颈与瓶体融合 bottleBody = BRepAlgoAPI_Fuse(bottleBody, neck);

真正的瓶子需要能够盛装液体,因此我们需要将实体挖空。OCCT提供了创建空心实体的工具:

// 寻找要移除的顶面 TopoDS_Face faceToRemove; Standard_Real maxZ = -1; for(TopExp_Explorer faceExp(bottleBody, TopAbs_FACE); faceExp.More(); faceExp.Next()) { TopoDS_Face face = TopoDS::Face(faceExp.Current()); Handle(Geom_Surface) surface = BRep_Tool::Surface(face); if(surface->DynamicType() == STANDARD_TYPE(Geom_Plane)) { gp_Pnt loc = Handle(Geom_Plane)::DownCast(surface)->Location(); if(loc.Z() > maxZ) { maxZ = loc.Z(); faceToRemove = face; } } } // 创建空心实体 TopTools_ListOfShape facesToRemove; facesToRemove.Append(faceToRemove); BRepOffsetAPI_MakeThickSolid hollowMaker; hollowMaker.MakeThickSolidByJoin(bottleBody, facesToRemove, -myThickness/50, 1e-3); bottleBody = hollowMaker.Shape();

至此,我们已经完成了瓶子的主体结构。接下来将为瓶颈添加螺纹细节,这是最有趣也最具挑战性的部分。

4. 创建瓶口螺纹

螺纹的创建需要一些技巧,因为它是缠绕在圆柱面上的螺旋形状。我们将使用两个不同半径的圆柱面作为螺纹的基础:

Handle(Geom_CylindricalSurface) cyl1 = new Geom_CylindricalSurface(neckAxis, neckRadius*0.99); Handle(Geom_CylindricalSurface) cyl2 = new Geom_CylindricalSurface(neckAxis, neckRadius*1.05);

在参数空间定义螺纹的二维轮廓。这里使用椭圆弧作为螺纹截面:

gp_Pnt2d center(2*M_PI, neckHeight/2); gp_Dir2d xDir(2*M_PI, neckHeight/4); gp_Ax2d profileAxes(center, xDir); Handle(Geom2d_Ellipse) ellipse1 = new Geom2d_Ellipse(profileAxes, 2*M_PI, neckHeight/10); Handle(Geom2d_Ellipse) ellipse2 = new Geom2d_Ellipse(profileAxes, 2*M_PI, neckHeight/40); // 修剪椭圆为半圆 Handle(Geom2d_TrimmedCurve) arc1 = new Geom2d_TrimmedCurve(ellipse1, 0, M_PI); Handle(Geom2d_TrimmedCurve) arc2 = new Geom2d_TrimmedCurve(ellipse2, 0, M_PI);

将这些2D曲线投影到3D圆柱面上创建边:

TopoDS_Edge edgeOnCyl1 = BRepBuilderAPI_MakeEdge(arc1, cyl1); TopoDS_Edge edgeOnCyl2 = BRepBuilderAPI_MakeEdge(arc2, cyl2); // 创建连接线段 gp_Pnt2d p1 = ellipse1->Value(0); gp_Pnt2d p2 = ellipse1->Value(M_PI); Handle(Geom2d_TrimmedCurve) segment = GCE2d_MakeSegment(p1, p2); TopoDS_Edge segEdge1 = BRepBuilderAPI_MakeEdge(segment, cyl1); TopoDS_Edge segEdge2 = BRepBuilderAPI_MakeEdge(segment, cyl2);

将这些边组合成线,然后通过放样操作创建实体螺纹:

TopoDS_Wire wire1 = BRepBuilderAPI_MakeWire(edgeOnCyl1, segEdge1); TopoDS_Wire wire2 = BRepBuilderAPI_MakeWire(edgeOnCyl2, segEdge2); // 构建3D曲�� BRepLib::BuildCurves3d(wire1); BRepLib::BuildCurves3d(wire2); // 通过放样创建螺纹实体 BRepOffsetAPI_ThruSections loftMaker(Standard_True); loftMaker.AddWire(wire1); loftMaker.AddWire(wire2); loftMaker.CheckCompatibility(Standard_False); TopoDS_Shape threading = loftMaker.Shape();

最后,将瓶体和螺纹组合成一个完整的模型:

TopoDS_Compound result; BRep_Builder builder; builder.MakeCompound(result); builder.Add(result, bottleBody); builder.Add(result, threading);

这个完整的建模过程展示了如何用代码一步步构建复杂的3D形状。与传统的GUI建模相比,编程建模提供了无与伦比的精确性和可重复性。你可以轻松调整参数如瓶子的高度、宽度或螺纹的细节,而无需手动重新建模。

http://www.jsqmd.com/news/938635/

相关文章:

  • 从聊天记录到数字资产:如何用WeChatMsg挖掘微信对话的隐藏价值
  • 基于Circuit Playground Express的可编程LED徽章制作指南
  • 终极免费自动化脚本工具:Pulover‘s Macro Creator完全指南
  • 在阿里云上搞定NI LinuxRT 23.5编译:从零配置Ubuntu服务器到生成ISO镜像
  • 2026年10款论文降AI率网站实测:从90%降至10%的宝藏之选
  • 终极指南:用ROFL-Player轻松解析英雄联盟回放文件,快速提升游戏水平
  • 那一天
  • 基于Arduino的万圣节互动糖果滑道:传感器、灯光与音效的融合实践
  • 3步搞定多平台数据采集:MediaCrawler让社交媒体分析变得简单
  • 如何快速掌握Smithbox游戏修改工具:从入门到精通的完整指南
  • 2026实测盘点:16款降AIGC网站测评,论文降重降ai率终极答案!
  • 如何快速实现AI桌面自动化:面向普通用户的完整指南
  • 终极指南:如何用KMS_VL_ALL_AIO智能激活工具永久激活Windows和Office
  • Tinkercad Circuits入门:从点亮LED到电路仿真实践
  • 贴吧 Server 团队 10 周落地小码哥 AI CR:评审占比提至 84%,bug 密度降 66.87%!
  • 5步掌握BilibiliDown:跨平台B站视频下载实用技巧
  • 手把手教你用Simulink搭建PMSM位置三闭环模型(附模型下载与参数详解)
  • WorkshopDL终极指南:无需Steam客户端,轻松获取创意工坊模组的完整解决方案
  • 3分钟快速解锁加密音乐文件:Unlock Music完整使用指南
  • 基于ESP32的复古水声电台:从I2S音频到交互设计的完整实现
  • 用按钮模拟重量传感器:Arduino入门项目与嵌入式控制核心原理实践
  • 快速掌握mootdx:Python通达信数据读取的终极解决方案
  • 资源强的大湾区EMBA推荐:5大高含金量优质项目盘点
  • 华硕笔记本终极轻量控制神器:5分钟快速上手G-Helper完全指南
  • Solon 框架热加载与热插拔机制揭秘:从开发到生产的完整技术链路
  • COM3D2.MaidFiddler终极指南:3步掌握女仆实时编辑的强大功能
  • 基于TinyML与Arduino Nicla的嵌入式坡度感知系统实践
  • HsMod:炉石传说终极增强插件,55项功能全面优化游戏体验
  • 数据科学如何预测奥斯卡:从多元数据到动态概率模型的实战解析
  • 为什么Android用户需要一款专业的3D模型查看器?ModelViewer3D给出了完美答案