opengl加载和绘制STL模型+旋转 平移 缩放 光照

一、开发环境说明

  • 操作系统:windows
  • 开发软件:Visual Studio 2017
  • 编程语言:基于控制台下的opengl
  • 用到的库: glut

opengl环境配置查看博客

二、STL文件介绍

2.1 STL的二进制格式

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

ASCII格式的STL文件结构如下

solid finename stl   //文件路径及文件名
facet normal x y  z  //三角片面法向量的3个分量
outer loop           
vertex x y z         //三角片面第一个顶点的坐标
vertex x y z         //三角片面第二个顶点的坐标
vertex x y z         //三角片面第三个顶点的坐标
endloop
endfacet             //第一个三角面片定义完毕
.........
.........
.........
endsolid filename stl//整个文件结束
2.2 STL文件截图

如果所示,是一个牙齿模型的stl文件
opengl加载和绘制STL模型+旋转 平移 缩放 光照_第1张图片

三、代码实现及验证

定义一个类STLModel,用来加载和显示模型

3.1 STLModel头文件代码如下
#pragma once
#include//包括容器
typedef float M3DVector3f[3];//定义坐标点
class STLModel
{
public:
	STLModel();//构造函数
	STLModel(const char* stlFileName);//构造函数
	~STLModel();//析构函数
	bool LoadStlFile(const char* stlFileName);//加载STL模型文件并解析
	void Draw(int model);//绘制模型

protected:
	struct RenderTri//一个三角形所有数据结构体
	{
		M3DVector3f m_Normal; //法线
		M3DVector3f m_Vertx1;//三角形第一个顶点
		M3DVector3f m_Vertx2;//三角形第二个顶点
		M3DVector3f m_Vertx3;//三角形第三个顶点
	};
private:
   std::vector m_MeshTris;    //保存加载的三角形数据
   M3DVector3f m_MaxPos;//记录最大的X Y Z点  方便平移 缩放用来最佳显示
   M3DVector3f m_MinPos;//记录最小的X Y Z点 方便平移 缩放用来最佳显示
};
3.2 编写STL文件读取函数【LoadStlFile】,代码如下
bool STLModel::LoadStlFile(const char* stlFileName)
{
	FILE* file = NULL;
	errno_t err;//记录错误
	int Triangle_num = 0;//记录读取的三角形个数
	err = fopen_s(&file, stlFileName, "r");//打开文件
	if (err != 0)
		return false;//如果读取文件错误,返回
	char buffer[133];//每行字符串
	char str1[80], str2[80];//提取的字符串
	int lineno = 0;//记录读取的行数
	//读取头文件 solid finename stl   : 文件路径及文件名
	fgets(buffer, 133, file);
	lineno++;
	sscanf_s(buffer, "%s %s", str1, 80, str2, 80);
	assert(_strcmpi(str1, "solid") == 0);//solid
	if (_strcmpi(str1, "solid") != 0)//检测是否有solid ,如果没有返回错误
		return false;
	m_MeshTris.clear();//清空数据
	while (fgets(buffer, 133, file))//开始读取每一个三角形数据
	{
		lineno++;//记录读取的行数 加1
		RenderTri tri;//三角形结构体数据,包括3个顶点坐标和1个法线坐标

		//读取法线坐标  facet normal x y  z  : 三角片面法向量的3个分量
		sscanf_s(buffer, "%s %s %f %f %f", str1, 80, str2, 80, &(tri.m_Normal[0]), &(tri.m_Normal[1]), &(tri.m_Normal[2]));
		if (_strcmpi(str1, "facet") != 0 || _strcmpi(str2, "normal") != 0)
			continue;
		fgets(buffer, 133, file);
		lineno++;
		 // 过滤掉 outer loop
		sscanf_s(buffer, "%*s %*s");
		fgets(buffer, 133, file);
		lineno++;
		//读取第一个顶点坐标
		sscanf_s(buffer, "%s %f %f %f", str1, 80, &(tri.m_Vertx1[0]), &(tri.m_Vertx1[1]), &(tri.m_Vertx1[2]));
		fgets(buffer, 133, file);	
		lineno++;
		//读取第二个顶点坐标
		sscanf_s(buffer, "%s %f %f %f", str1, 80, &(tri.m_Vertx2[0]), &(tri.m_Vertx2[1]), &(tri.m_Vertx2[2]));
		fgets(buffer, 133, file);
		lineno++;
		//读取第三个顶点坐标
		sscanf_s(buffer, "%s %f %f %f", str1, 80, &(tri.m_Vertx3[0]), &(tri.m_Vertx3[1]), &(tri.m_Vertx3[2]));

		//过滤掉   endloop 这一行
		fgets(buffer, 133, file);
		lineno++;
		sscanf_s(buffer, "%*s");

		//过滤掉 endfacet  这一行
		fgets(buffer, 133, file);
		lineno++;
		sscanf_s(buffer, "%*s");
		
		//记录最大值和最小值,方便平移和缩放 让模型在视图正中间显示
		{
			
			if (Triangle_num == 0)/如果是第一个三角形,给最大最小点赋初值
			{
				for (int i = 0; i < 3; i++)
					m_MaxPos[i] = m_MinPos[i] = tri.m_Vertx1[i];//赋初始值
			}
			Triangle_num += 1;//三角形个数加1
			for (int i = 0; i < 3; i++)
			{
				//比较 并 记录最大值
				if (m_MaxPos[i] < tri.m_Vertx1[i])m_MaxPos[i] = tri.m_Vertx1[i];
				if (m_MaxPos[i] < tri.m_Vertx2[i])m_MaxPos[i] = tri.m_Vertx2[i];
				if (m_MaxPos[i] < tri.m_Vertx3[i])m_MaxPos[i] = tri.m_Vertx3[i];
				//比较 并 记录最小值
				if (m_MinPos[i] > tri.m_Vertx1[i])m_MinPos[i] = tri.m_Vertx1[i];
				if (m_MinPos[i] > tri.m_Vertx2[i])m_MinPos[i] = tri.m_Vertx2[i];
				if (m_MinPos[i] > tri.m_Vertx3[i])m_MinPos[i] = tri.m_Vertx3[i];
			}
		}
	
		std::cout << "第" << Triangle_num << "个三角形:" << std::endl;//显示三角形个数
		m_MeshTris.push_back(tri);//把三角形数据保存到容器中
	}
	fclose(file);//关闭文件
	return true;
}
3.3 编写绘制显示STL模型函数【Draw】,代码如下
void STLModel::Draw(int model)//绘制模型
{
	glEnable(GL_NORMALIZE);//重新计算法线坐标
	glPushMatrix();//压栈
	float  m_scale =100;//缩放倍数
	m_scale = m_scale < (1.0 / (m_MaxPos[0] - m_MinPos[0])) ? m_scale : (1.0 / (m_MaxPos[0] - m_MinPos[0]));//比较长缩放倍数 ,记录小的倍数值
	m_scale = m_scale < (1.0 / (m_MaxPos[1] - m_MinPos[1])) ? m_scale : (1.0 / (m_MaxPos[1] - m_MinPos[1]));//比较宽缩放倍数 ,记录小的倍数值
	m_scale = m_scale < (1.0 / (m_MaxPos[2] - m_MinPos[2])) ? m_scale : (1.0 / (m_MaxPos[2] - m_MinPos[2]));//比较高缩放倍数 ,记录小的倍数值
	glScalef(m_scale,m_scale,m_scale);//缩放 让长宽高最大是 1
	glTranslatef(-(m_MinPos[0] + m_MaxPos[0]) / 2.0, -(m_MinPos[1] + m_MaxPos[1]) / 2.0, -(m_MinPos[2] + m_MaxPos[2]) / 2.0);//平移,让模型中心在原点
	glBegin(GL_TRIANGLES);//绘制三角形
	if (model == 0)
	{
		for (size_t i = 0; i < m_MeshTris.size(); i++)//循环所有三角形
		{
            //第一个点数据
			glNormal3f(m_MeshTris[i].m_Normal[0], m_MeshTris[i].m_Normal[1], m_MeshTris[i].m_Normal[2]);
			glVertex3f(m_MeshTris[i].m_Vertx1[0], m_MeshTris[i].m_Vertx1[1], m_MeshTris[i].m_Vertx1[2]);
			//第二个点数据
			glNormal3f(m_MeshTris[i].m_Normal[0], m_MeshTris[i].m_Normal[1], m_MeshTris[i].m_Normal[2]);
			glVertex3f(m_MeshTris[i].m_Vertx2[0], m_MeshTris[i].m_Vertx2[1], m_MeshTris[i].m_Vertx2[2]);
			//第三个点数据
			glNormal3f(m_MeshTris[i].m_Normal[0], m_MeshTris[i].m_Normal[1], m_MeshTris[i].m_Normal[2]);
			glVertex3f(m_MeshTris[i].m_Vertx3[0], m_MeshTris[i].m_Vertx3[1], m_MeshTris[i].m_Vertx3[2]);
		}
	}
	glEnd();//结束绘制
	glPopMatrix();//出栈
}
3.5 大功告成,STL牙齿模型显示 旋转+平移+缩放+菜单+光照 效果图如下所示:

opengl加载和绘制STL模型+旋转 平移 缩放 光照_第2张图片

四、整个工程代码下载

你可能感兴趣的:(Opengl)