获取建模得到的三维模型并解析
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