C++方式实现stl、obj、3DS三种3D模型加载,并实现Arcball方法控制旋转缩放平移(采用glut库等文件,具体可在项目文件中实现),开发软件为VS2019

获取建模得到的三维模型并解析

STL****文件

STL (STereoLithography, 立体光刻)是由3D Systems软件公司创立、原本用于立体光刻计算机辅助设计软件的文件格式。它有一些事后诸葛的字头语如“标准三角语言(Standard Triangle Language)”、“标准曲面细分语言(Standard Tessellation Language)”、“立体光刻语言(STereolithography Language)”和“(立体光刻曲面细分语言)”。许多套装软件支持这种格式,它被广泛用于快速成型、3D打印和计算机辅助制造(CAM)。STL文件仅描述三维物体的表面几何形状,没有颜色、材质贴图或其它常见三维模型的属性。

STL格式有文字和二进码两种型式。二进码型式因较简洁而较常见。

ASCII格式

ASCII码格式的STL文件逐行给出三角面片的几何信息,每一行以1个或2个关键字开头。

在STL文件中的三角面片的信息单元 facet 是一个带矢量方向的三角面片,STL三维模型就是由一系列这样的三角面片构成。整个STL文件的首行给出了文件路径及文件名。在一个 STL文件中,每一个facet由7 行数据组成,facet normal 是三角面片指向实体外部的法矢量坐标,outer loop 说明随后的3行数据分别是三角面片的3个顶点坐标,3顶点沿指向实体外部的法矢量方向逆时针排列。

ASCII格式的STL 文件结构如下:

//字符段意义

solidfilenamestl//文件路径及文件名

facetnormalxyz//三角面片法向量的3个分量值

outerloop

vertexxyz//三角面片第一个顶点坐标

vertexxyz//三角面片第二个顶点坐标

vertexxyz//三角面片第三个顶点坐标

endloop

endfacet//完成一个三角面片定义

…//其他facet

解析输出为:
在这里插入图片描述

二进制格式

二进制STL文件用固定的字节数来给出三角面片的几何信息。

文件起始的80个字节是文件头,用于存贮文件名;(如下所示)

solid Exported from Blender-2078 (sub 0)

紧接着用 4 个字节的整数来描述模型的三角面片个数,如下所示,即为一个三角得信息。

facet normal -0.000000 0.000000 -1.000000
outer loop
vertex 1.000000 1.000000 -1.000000
vertex 1.000000 -1.000000 -1.000000
vertex -1.000000 -1.000000 -1.000000
endloop
endfacet

后面逐个给出每个三角面片的几何信息。每个三角面片占用固定的50个字节,依次是:

3个4字节浮点数(角面片的法矢量)

3个4字节浮点数(1个顶点的坐标)

3个4字节浮点数(2个顶点的坐标)

3个4字节浮点数(3个顶点的坐标)个

三角面片的最后2个字节用来描述三角面片的属性信息。

一个完整二进制STL文件的大小为三角形面片数乘以 50再加上84个字节。

二进制格式的STL 文件结构如下:

UINT8//Header//文件头

UINT32//Numberoftriangles//三角面片数量

//foreachtriangle(每个三角面片中)

REAL32[3]//Normalvector//法线矢量

REAL32[3]//Vertex1//顶点1坐标

REAL32[3]//Vertex2//顶点2坐标

REAL32[3]//Vertex3//顶点3坐标

UINT16//Attributebytecountend//文件属性统计

解析输出为:
在这里插入图片描述

OBJ文件

obj(或者.obj)是一种几何定义文件格式,第一次是 Wavefront Technologies在他们的可视化加强动画包里面使用的。文件格式是公开的,并能很好的在其他的3D应用中被支持。

Obj文件格式是一种简单的单独表示3D几何图元的文件格式——也就是,顶点的坐标,每个顶点纹理的UV坐标,顶点法向量,以及组成多边形的面的顶点坐标、以及纹理UV坐标序列。面的顶点默认为逆时针顺序,法向量不是必须的。OBJ文件并非归一化的,但是可以在注释中加入缩放信息。

Obj文件可以是ASCII的编码(.obj)方式也可以是二进制格式(.mod)。但是二进制类型其作为专利未公开,因此这里不作讨论。以ASCII格式存储的obj文件必须用.obj作为文件拓展名。

文件的开始,有以哈希字符(#)开始的一行表示注释。

# this is a comment

对于obj来说,一个obj格式的文件可能包含了顶点数据,自由形式的曲面/表面属性,绘制索引序列,自由形式的曲面/表面内容声明,关联自由形式的表面,组和渲染属性信息。大多数常见的绘制索引表现为几何顶点,纹理坐标,顶点法线以及多边形的面:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gK3xEAGG-1590112131477)(https://raw.githubusercontent.com/adonispeace/adonispeace.github.io/master/dailyPic/20200401/clip_image012.jpg “Image_border+rounded”)]{:.border.rounded}

通常处理obj文件的时候,会抛弃顶点法线数据,而通过顶点信息来进行计算。有了以上的顶点坐标、法线、纹理坐标等信息,就可以进行3D模型文件的渲染了。

解析输出为:
在这里插入图片描述g “Image_border+rounded”)]{:.border.rounded}

3DS文件

3ds文件是3D Max的一种二进制存储格式,它始终没被官方公开,但是也基本被大家hack出来了大半。其“格式”总的来说非常简单,这里介绍一个概念:chunk。3ds文件里的数据都是按chunk一块一块隔离的。每个chunk都有两个标记:2个字节大小的chunkId,用来标识这个chunk存的是什么数据。接着是一个4个字节大小的chunkLen,它根据chunkId不同,可能表示该chunk的大小,也可能表示下一个chunk的位置偏移。

-----------------------------

chunk Id 2 Byte

chunk Len  4 Byte

-----------------------------

想要读取3ds文件,chunk Id是比较重要的部分,下面是hack出来的常用Id:

---------------------------------------------------------------------------------------

0x4D4D // Main Chunk

├─ 0x3D3D // 3D Editor Chunk

│ ├─ 0x4000 // Object Block

│ │ ├─ 0x4100 // Triangular Mesh

│ │ │ ├─ 0x4110 // Vertices List

│ │ │ ├─ 0x4120 // Faces Description

│ │ │ │ └─ 0x4130 // Faces Material

│ │ │ ├─ 0x4140 // Mapping Coordinates List

│ │ │ │ └─ 0x4150 // Smoothing Group List

│ │ │ └─ 0x4160 // Local Coordinates System

│ │ ├─ 0x4600 // Light

│ │ │ └─ 0x4610 // Spotlight

│ │ └─ 0x4700 // Camera

│ └─ 0xAFFF // Material Block

│ ├─ 0xA000 // Material Name

│ ├─ 0xA010 // Ambient Color

│ ├─ 0xA020 // Diffuse Color

│ ├─ 0xA030 // Specular Color

│ ├─ 0xA200 // Texture Map 1

│ ├─ 0xA230 // Bump Map

│ └─ 0xA220 // Reflection Map

│ │ /* Sub Chunks For Each Map */

│ ├─ 0xA300 // Mapping Filename

│ └─ 0xA351 // Mapping Parameters

└─ 0xB000 // Keyframer Chunk

├─ 0xB002 // Mesh Information Block

├─ 0xB007 // Spot Light Information Block

└─ 0xB008 // Frames (Start and End)

├─ 0xB010 // Object Name

├─ 0xB013 // Object Pivot Point

├─ 0xB020 // Position Track

├─ 0xB021 // Rotation Track

├─ 0xB022 // Scale Track

└─ 0xB030 // Hierarchy Position

---------------------------------------------------------------------------

一般来说,一个chunk可能还包含子chunk,比如0x4100表示Mesh的chunk里就包含了0x4110,0x4120等chunk,同时,0x4100这个chunk的长度标识,是把这些子chunk的长度也计算在内的。一般来说,我们读取3ds文件,只要解析主要chunk,遇到不识别的chunk,跳过即可。因为3ds里的数据,大多数并不是程序员需要的。我们只要顶点和uv这些就够了。

读取数据并载入:
在这里插入图片描述

配置环境

本人使用的VS2019,在配置环境时,希望能够讲库进行集中管理,因此在E盘单独建立文件夹,将include和lib项放入,并在项目中引入,虽然在后期新建项目中需要重复引入,但是保证了相关文件的整理便捷性。
在这里插入图片描述

同时添加依赖项:
在这里插入图片描述

绘制三维模型

对于三种文件格式来说,有很多的选择来进行重绘,在这里我选择使用glut的库来完成操作。

上边已经显示出数据的解析,并且得到绘制的图形。可以先使用如下函数进行绘制,设窗口的大小以及形式等信息。

glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA | GLUT_MULTISAMPLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(GL_WIN_WIDTH, GL_WIN_HEIGHT);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); // | GLUT_STENCIL
glutInit(argc, argv);
GLUTwindow = glutCreateWindow("powerful_reader~");
glutReshapeFunc(glutResize);

最后的选择如上图所示。

Arcball实现控制

由于屏幕是二维的,无法直接表示旋转,可以通过辅助几何体来完成。想象在屏幕后面有一个球体,球体正好与这个屏幕相切,如下图的俯视图所示
在这里插入图片描述

我们在求旋转轴之前需要将二维坐标转化为三维向量OP,如下图,T为屏幕上的二维坐标,P为T向屏幕后方发出射线与球体相交的点。这里做作OM水平面平行于屏幕,P到水平线的深度z需要先行求得。

为了运算处理方便,我们需要将T坐标限定在[-1,1]之间,设定这个球体的半径为1。

x = 2*x / 屏幕宽度 - 1

y = -(2*y / 屏幕高度 - 1)

由勾股定理可知,z = sqrt(1-xx-yy)。

当xx+yy大于1时,P不在球体上,那就在OM上找一点P,且满足P在球体上,PT垂直于平面OM,即xx+yy大小限定在1这个值,z = 0.

具体流程如下:

x,y,z的值可以唯一确定OP向量,接下来,给定输入的两个坐标T1,T2,我们做出如下处理:

\1. 分别求出T1和T2对应的三维向量OP1和OP2

\2. s = OP1 · OP2,即先求两个向量的内积

\3. v = OP1 × OP2,即求两个向量的外积

\4. 记四元数q = [s, v],将其单位化,此时q为旋转四元数。

\5. 旋转角α = 2arccos(q.s) ,旋转轴V = ( q.v / sqrt(1-q.s*q.s) )

结果展示

如下所示,stl格式的ascii、binary存储格式的读取和绘制以及控制,以及obj和3DS格式,运行过程存储为gif****格式,可另行保存观看。

或者在百度云下载:

链接:https://pan.baidu.com/s/1mH9cy1nC9WNrOfa7HDqa9g

提取码:xv8t

STL_binary
在这里插入图片描述
STL_ASCII
在这里插入图片描述
OBJ
在这里插入图片描述
3DS
在这里插入图片描述

评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值