文章目录
- OpenMesh官网
- OpenMesh是什么?他能做什么?
- 直接无源码安装
- 测试
- 报错:
- 效果:
- 学习openmesh
- 学习openmesh的流程如下:
- 第一步,了解openmesh库的基本概念
- 第二步,查看openmesh的官方示例,了解简单的接口api
- 练习,自己写一个立方体
- 开启一个小项目,比如:如何加载output.off这个模型,修改和保存
- OpenMesh架构
- openflipper的安装
- openGL库的安装
- 为什么要安装openGL?
- GLFW的安装
- GLAD安装
- 配置GLFW 和 GLAD
- 1) 配置GLFW
- 2) 配置 GLAD
- 3) 测试OpenGL
- openmesh与opengl结合使用
- 一些感悟
- 一些细碎知识点
- 1.OpenGL的状态通常被称为OpenGL上下文(Context)。
- 2.如何快速上手openmesh,照着这个教程,一共十五章,看完基本就是OK了
OpenMesh官网
开放网格 - 计算机图形学和多媒体 (rwth-aachen.de)
OpenMesh是什么?他能做什么?
OpenMesh是一个C++库,OpenFlipper这个软件就是由OpenMesh开发的
直接无源码安装
非常好用,都不用cmake了
测试
-
建一个cpp的空项目,项目名称叫TestOpenMesh
-
将编译模式改为release
- 建一个TestOpenMesh.cpp
- 配置头文件:
打开项目属性-VC++目录-包含目录
,添加包含目录D:\vs2022\openmesh\install\OpenMesh 9.0\include
如下:
- 配置库文件:
打开项目属性-链接器-常规-附加依赖库目录
,添加附加库目录D:\vs2022\openmesh\install\OpenMesh 9.0\lib
如下:
- 配置附加依赖项.lib文件:*
打开项目属性-链接器-输入-附加依赖项
,添加附加库目录OpenMeshCored.lib
和OpenMeshToolsd.lib
如下:
OpenMeshCored.lib
OpenMeshToolsd.lib
- 配置预处理器添加宏:
打开项目属性-C/C++-预处理器
,添加附加宏_USE_MATH_DEFINES
测试代码
#include <OpenMesh/Core/IO/MeshIO.hh>
#include <OpenMesh/Core/Mesh/PolyMesh_ArrayKernelT.hh>
typedef OpenMesh::PolyMesh_ArrayKernelT<> MyMesh;
int main()
{
MyMesh mesh;
// generate vertices
MyMesh::VertexHandle vhandle[8];
vhandle[0] = mesh.add_vertex(MyMesh::Point(-1, -1, 1));
vhandle[1] = mesh.add_vertex(MyMesh::Point(1, -1, 1));
vhandle[2] = mesh.add_vertex(MyMesh::Point(1, 1, 1));
vhandle[3] = mesh.add_vertex(MyMesh::Point(-1, 1, 1));
vhandle[4] = mesh.add_vertex(MyMesh::Point(-1, -1, -1));
vhandle[5] = mesh.add_vertex(MyMesh::Point(1, -1, -1));
vhandle[6] = mesh.add_vertex(MyMesh::Point(1, 1, -1));
vhandle[7] = mesh.add_vertex(MyMesh::Point(-1, 1, -1));
// generate (quadrilateral) faces
std::vector<MyMesh::VertexHandle> face_vhandles;
face_vhandles.clear();
face_vhandles.push_back(vhandle[0]);
face_vhandles.push_back(vhandle[1]);
face_vhandles.push_back(vhandle[2]);
face_vhandles.push_back(vhandle[3]);//逆时针
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[7]);
face_vhandles.push_back(vhandle[6]);
face_vhandles.push_back(vhandle[5]);
face_vhandles.push_back(vhandle[4]);//顺时针
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[1]);
face_vhandles.push_back(vhandle[0]);
face_vhandles.push_back(vhandle[4]);
face_vhandles.push_back(vhandle[5]);//顺时针
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[2]);
face_vhandles.push_back(vhandle[1]);
face_vhandles.push_back(vhandle[5]);
face_vhandles.push_back(vhandle[6]);//逆时针
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[3]);
face_vhandles.push_back(vhandle[2]);
face_vhandles.push_back(vhandle[6]);
face_vhandles.push_back(vhandle[7]);//逆时针
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[0]);
face_vhandles.push_back(vhandle[3]);
face_vhandles.push_back(vhandle[7]);
face_vhandles.push_back(vhandle[4]);//顺时针
mesh.add_face(face_vhandles);
// write mesh to output.obj
try
{
if (!OpenMesh::IO::write_mesh(mesh, "output.off"))
{
std::cerr << "Cannot write mesh to file 'output.off'" << std::endl;
return 1;
}
}
catch (std::exception& x)
{
std::cerr << x.what() << std::endl;
return 1;
}
return 0;
}
报错:
1>OpenMeshCored.lib(BaseProperty.obj) : error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“2”不匹配值“0”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(BaseProperty.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“MD_DynamicRelease”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(ArrayKernel.obj) : error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“2”不匹配值“0”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(ArrayKernel.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“MD_DynamicRelease”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(OMWriter.obj) : error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“2”不匹配值“0”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(OMWriter.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“MD_DynamicRelease”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(PLYReader.obj) : error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“2”不匹配值“0”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(PLYReader.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“MD_DynamicRelease”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(OMReader.obj) : error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“2”不匹配值“0”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(OMReader.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“MD_DynamicRelease”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(VTKWriter.obj) : error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“2”不匹配值“0”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(VTKWriter.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“MD_DynamicRelease”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(PLYWriter.obj) : error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“2”不匹配值“0”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(PLYWriter.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“MD_DynamicRelease”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(OBJWriter.obj) : error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“2”不匹配值“0”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(OBJWriter.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“MD_DynamicRelease”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(STLWriter.obj) : error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“2”不匹配值“0”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(STLWriter.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“MD_DynamicRelease”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(STLReader.obj) : error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“2”不匹配值“0”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(STLReader.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“MD_DynamicRelease”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(IOManager.obj) : error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“2”不匹配值“0”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(IOManager.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“MD_DynamicRelease”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(OFFWriter.obj) : error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“2”不匹配值“0”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(OFFWriter.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“MD_DynamicRelease”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(OBJReader.obj) : error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“2”不匹配值“0”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(OBJReader.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“MD_DynamicRelease”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(OFFReader.obj) : error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“2”不匹配值“0”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(OFFReader.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“MD_DynamicRelease”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(PolyConnectivity.obj) : error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“2”不匹配值“0”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(PolyConnectivity.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“MD_DynamicRelease”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(omstream.obj) : error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“2”不匹配值“0”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(omstream.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“MD_DynamicRelease”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(OMFormat.obj) : error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“2”不匹配值“0”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(OMFormat.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“MD_DynamicRelease”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(BaseWriter.obj) : error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“2”不匹配值“0”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(BaseWriter.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“MD_DynamicRelease”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(BaseReader.obj) : error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“2”不匹配值“0”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(BaseReader.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“MD_DynamicRelease”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(PropertyCreator.obj) : error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“2”不匹配值“0”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(PropertyCreator.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“MD_DynamicRelease”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(BinaryHelper.obj) : error LNK2038: 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“2”不匹配值“0”(TestOpenMesh.obj 中)
1>OpenMeshCored.lib(BinaryHelper.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“MD_DynamicRelease”(TestOpenMesh.obj 中)
1>LINK : warning LNK4098: 默认库“MSVCRTD”与其他库的使用冲突;请使用 /NODEFAULTLIB:library
1>OpenMeshCored.lib(BaseWriter.obj) : error LNK2001: 无法解析的外部符号 __imp__invalid_parameter
1>OpenMeshCored.lib(BaseReader.obj) : error LNK2001: 无法解析的外部符号 __imp__invalid_parameter
1>OpenMeshCored.lib(PropertyCreator.obj) : error LNK2001: 无法解析的外部符号 __imp__invalid_parameter
1>OpenMeshCored.lib(OFFReader.obj) : error LNK2001: 无法解析的外部符号 __imp__invalid_parameter
1>OpenMeshCored.lib(PolyConnectivity.obj) : error LNK2001: 无法解析的外部符号 __imp__invalid_parameter
1>OpenMeshCored.lib(omstream.obj) : error LNK2001: 无法解析的外部符号 __imp__invalid_parameter
1>OpenMeshCored.lib(OMFormat.obj) : error LNK2001: 无法解析的外部符号 __imp__invalid_parameter
1>OpenMeshCored.lib(STLReader.obj) : error LNK2001: 无法解析的外部符号 __imp__invalid_parameter
1>OpenMeshCored.lib(IOManager.obj) : error LNK2001: 无法解析的外部符号 __imp__invalid_parameter
1>OpenMeshCored.lib(OFFWriter.obj) : error LNK2001: 无法解析的外部符号 __imp__invalid_parameter
1>OpenMeshCored.lib(OBJReader.obj) : error LNK2001: 无法解析的外部符号 __imp__invalid_parameter
1>OpenMeshCored.lib(VTKWriter.obj) : error LNK2001: 无法解析的外部符号 __imp__invalid_parameter
1>OpenMeshCored.lib(PLYWriter.obj) : error LNK2001: 无法解析的外部符号 __imp__invalid_parameter
1>OpenMeshCored.lib(OBJWriter.obj) : error LNK2001: 无法解析的外部符号 __imp__invalid_parameter
1>OpenMeshCored.lib(STLWriter.obj) : error LNK2001: 无法解析的外部符号 __imp__invalid_parameter
1>OpenMeshCored.lib(ArrayKernel.obj) : error LNK2001: 无法解析的外部符号 __imp__invalid_parameter
1>OpenMeshCored.lib(OMWriter.obj) : error LNK2001: 无法解析的外部符号 __imp__invalid_parameter
1>OpenMeshCored.lib(PLYReader.obj) : error LNK2001: 无法解析的外部符号 __imp__invalid_parameter
1>OpenMeshCored.lib(OMReader.obj) : error LNK2001: 无法解析的外部符号 __imp__invalid_parameter
1>OpenMeshCored.lib(BaseWriter.obj) : error LNK2001: 无法解析的外部符号 __imp__CrtDbgReport
1>OpenMeshCored.lib(BaseReader.obj) : error LNK2001: 无法解析的外部符号 __imp__CrtDbgReport
1>OpenMeshCored.lib(PropertyCreator.obj) : error LNK2001: 无法解析的外部符号 __imp__CrtDbgReport
1>OpenMeshCored.lib(OFFReader.obj) : error LNK2001: 无法解析的外部符号 __imp__CrtDbgReport
1>OpenMeshCored.lib(PolyConnectivity.obj) : error LNK2001: 无法解析的外部符号 __imp__CrtDbgReport
1>OpenMeshCored.lib(omstream.obj) : error LNK2001: 无法解析的外部符号 __imp__CrtDbgReport
1>OpenMeshCored.lib(OMFormat.obj) : error LNK2001: 无法解析的外部符号 __imp__CrtDbgReport
1>OpenMeshCored.lib(STLReader.obj) : error LNK2001: 无法解析的外部符号 __imp__CrtDbgReport
1>OpenMeshCored.lib(IOManager.obj) : error LNK2001: 无法解析的外部符号 __imp__CrtDbgReport
1>OpenMeshCored.lib(OFFWriter.obj) : error LNK2001: 无法解析的外部符号 __imp__CrtDbgReport
1>OpenMeshCored.lib(OBJReader.obj) : error LNK2001: 无法解析的外部符号 __imp__CrtDbgReport
1>OpenMeshCored.lib(VTKWriter.obj) : error LNK2001: 无法解析的外部符号 __imp__CrtDbgReport
1>OpenMeshCored.lib(PLYWriter.obj) : error LNK2001: 无法解析的外部符号 __imp__CrtDbgReport
1>OpenMeshCored.lib(OBJWriter.obj) : error LNK2001: 无法解析的外部符号 __imp__CrtDbgReport
1>OpenMeshCored.lib(STLWriter.obj) : error LNK2001: 无法解析的外部符号 __imp__CrtDbgReport
1>OpenMeshCored.lib(ArrayKernel.obj) : error LNK2001: 无法解析的外部符号 __imp__CrtDbgReport
1>OpenMeshCored.lib(OMWriter.obj) : error LNK2001: 无法解析的外部符号 __imp__CrtDbgReport
1>OpenMeshCored.lib(PLYReader.obj) : error LNK2001: 无法解析的外部符号 __imp__CrtDbgReport
1>OpenMeshCored.lib(OMReader.obj) : error LNK2001: 无法解析的外部符号 __imp__CrtDbgReport
1>OpenMeshCored.lib(OMWriter.obj) : error LNK2001: 无法解析的外部符号 __imp__calloc_dbg
1>OpenMeshCored.lib(OMReader.obj) : error LNK2001: 无法解析的外部符号 __imp__calloc_dbg
1>OpenMeshCored.lib(OMFormat.obj) : error LNK2001: 无法解析的外部符号 __imp__calloc_dbg
1>D:\vs2022\code\TestOpenMesh\x64\Release\TestOpenMesh.exe : fatal error LNK1120: 3 个无法解析的外部命令
产生这个问题的原因是:当前工程是Release版本,而引用的库文件时Debug版本,所以你需要重复配置一下上面的操作,在Debug模式下!
效果:
学习openmesh
好了,我们现在已经成功搭建了openmesh的运行环境,现在我们来根据一些流程来学习openmesh,
在开启学习openmesh的流程之前,我们需要了解两个重要的资料:
一,openmesh的介绍网址:
https://www.graphics.rwth-aachen.de/software/openmesh/intro/
如下:这个自己去看
二,openmesh的文档网址:
如下:这个自己去看
https://www.graphics.rwth-aachen.de/media/openmesh_static/Documentations/OpenMesh-Doc-Latest/index.html
学习openmesh的流程如下:
第一步,了解openmesh库的基本概念
顶点、边、面
-
顶点 (Vertices):
- 顶点是在三维空间中定义的一个点。在OpenMesh中,每个顶点通常包含一个或多个坐标==(x、y、z)==来表示其位置。
- 顶点是模型的基本构成单元之一,它们可以用来描述物体的各个特征点。
-
边 (Edges):
- 边是两个顶点之间的连接。在OpenMesh中,边也可以具有附加的属性,如权重或方向。
- 一条边连接两个顶点,形成一个线段或曲线。
-
面 (Faces):
- 面是由三个或更多相邻的顶点组成的多边形,通常是一个平面的一部分。在OpenMesh中,常用的面是三角形和四边形。
- 面定义了模型的表面。
第二步,查看openmesh的官方示例,了解简单的接口api
练习,自己写一个立方体
报错:
PolyMeshT::add_face: complex edge
解决方法:
https://www.coder.work/article/1725293
openmesh的顶点顺序要看半边结构的顺序
再来看一下stl文件格式:
我们可以看到:stl文件格式也是遵循半边结构的
openmesh与stl文件的不同点是:如果你没有遵循半边结构,openmesh直接判断你是错的;
stl文件则不会,只是发亮面会反.
开启一个小项目,比如:如何加载output.off这个模型,修改和保存
#if 1
#include <OpenMesh/Core/IO/MeshIO.hh>
#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
typedef OpenMesh::TriMesh_ArrayKernelT<> MyMesh;
int main()
{
MyMesh mesh;
// 从文件中加载模型
if (!OpenMesh::IO::read_mesh(mesh, "triangle.off"))
{
std::cerr << "Error: Cannot read mesh from file 'output.off'" << std::endl;
return 1;
}
// 获取第一个顶点的句柄
MyMesh::VertexHandle vh = *(mesh.vertices_begin());
// 获取顶点的当前位置
MyMesh::Point current_pos = mesh.point(vh);
// 修改顶点的位置
mesh.set_point(vh, MyMesh::Point(current_pos[0] - 1.0, current_pos[1], current_pos[2]));
// 保存模型
if (!OpenMesh::IO::write_mesh(mesh, "modified_output.off"))
{
std::cerr << "Error: Cannot write mesh to file 'modified_output.off'" << std::endl;
return 1;
}
return 0;
}
#else
#include <OpenMesh/Core/IO/MeshIO.hh>
#include <OpenMesh/Core/Mesh/PolyMesh_ArrayKernelT.hh>
typedef OpenMesh::PolyMesh_ArrayKernelT<> MyMesh;
int main()
{
// 创建一个Mesh对象
MyMesh mesh;
// 添加顶点
MyMesh::VertexHandle vhandle[4];
vhandle[0] = mesh.add_vertex(MyMesh::Point(0.0, 0.0, 0.0));//0
vhandle[1] = mesh.add_vertex(MyMesh::Point(1.0, 0.0, 0.0));//1
vhandle[2] = mesh.add_vertex(MyMesh::Point(0.0, 1.0, 0.0));//2
vhandle[3] = mesh.add_vertex(MyMesh::Point(0.0, 0.0, 1.0));//3
// 添加面
std::vector<MyMesh::VertexHandle> face_vhandles;
face_vhandles.clear();
face_vhandles.push_back(vhandle[1]);
face_vhandles.push_back(vhandle[0]);
face_vhandles.push_back(vhandle[2]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[1]);
face_vhandles.push_back(vhandle[2]);
face_vhandles.push_back(vhandle[3]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[0]);
face_vhandles.push_back(vhandle[1]);
face_vhandles.push_back(vhandle[3]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[2]);
face_vhandles.push_back(vhandle[0]);
face_vhandles.push_back(vhandle[3]);
mesh.add_face(face_vhandles);
// 保存网格到文件
OpenMesh::IO::write_mesh(mesh, "triangle.off");
return 0;
}
#endif
也就是说,stl文件格式要求的顺序和openmesh要求的顺序都是一致的,网格结构的顺序!!!
OpenMesh架构
OpenMesh架构允许自定义网格:用户可以以顶点,边和面指定任意特征,也可以从一组预定义的属性中进行选择,然后将这些属性传播到网格内核.内核负责网格元素的内部存储,可以选择使用数组或双链表作为容器类型.由于具有不同属性的网格将导致不同的cpp类型,OPenMesh使用泛型编程来实现这些网格上操作的算法
openflipper的安装
https://www.graphics.rwth-aachen.de/software/openflipper/download/
openGL库的安装
为什么要安装openGL?
在计算机上显示三维模型,通常需要使用OpenGL或其他图形库来实现图形渲染。OpenMesh本身并不包含用于渲染图形的功能。
OpenGL小游戏教程
科普一下:OpenGL本身是一个图形渲染API,并不是一个库。要使用OpenGL,你需要安装GLFW库和GLAD库,来协助你管理窗口和处理OpenGL的扩展。
我一开始以为:OpenGL是一个库
GLFW的安装
https://www.glfw.org/download.html
然后解压就好了,我们应该用的是lib-vc2022(我猜的,具体应该如何后面会有具体操作的)
GLAD安装
https://glad.dav1d.de/
gl要选4.0以上的
配置GLFW 和 GLAD
1) 配置GLFW
1.使用vs2022创建一个空项目testOpenGL,并在项目下新建testOpenGL.cpp文件
2.右击testOpenGL.cpp,打开项目所在文件夹,在openGL的文件夹下创建includes和libs两个文件夹;
3.copy库
- 将glfw解压文件中lib-vc2022文件夹下的==.lib文件拷贝进刚刚创建的libs==下(说明刚才猜的没错),
- 同时将glfw解压文件中include下的==.h文件拷贝进刚刚创建的includes==下
开始拷贝:
4.在项目名处右键->属性->VC++目录->引用目录->编辑:粘贴项目文件夹下的.h文件所在目录地址
5.库目录->编辑:粘贴项目文件夹下的.lib文件所在目录
6.为项目添加依赖项:仍然是属性->链接器->输入->附加依赖项->编辑,将opengl32.lib,glfw3.lib,msvcrt.lib添加进去
opengl32.lib
glfw3.lib
msvcrt.lib
注意!!!这里一定要看!是关于msvcrt.lib的!!!
这里我踩过一个坑:
msvcrt.lib
是Microsoft Visual C++ 运行时库,它是用于处理C++程序的标准运行时库。
在使用 Visual Studio 时,通常会自动链接到相应的运行时库,因此不需要显式地将
msvcrt.lib
添加到附加依赖项中。
如果你手动将
msvcrt.lib
添加到附加依赖项中,可能会导致链接错误,因为这样做可能会引起运行时库的冲突。
所以,如果你的项目没有特殊的需求,最好不要手动添加
msvcrt.lib
到附加依赖项中,让 Visual Studio 自动处理运行时库的链接。如果你对运行时库有特殊的需求,可能需要谨慎处理,以避免引起冲突。所以,不要加msvcrt.lib这个库(既然如此,为什么我上面步骤还写了,这个教程是看其他教程一步一步实操的,我不知道那个人为啥要这么写,一步一步的循序渐进的更好一点),不然你看,把编译器整不会了,你还不知道啥错误,如下:
2) 配置 GLAD
-
将GLAD的include下,
将glad文件夹和KHP文件夹
拷贝进
项目文件夹下的includes下,
并将GLAD的src下的glad.c拷贝进项目文件夹下
-
进入vs,添加源文件->现有项,将glad.c添加进项目
3) 测试OpenGL
//测试代码
#include"glad/glad.h"
#include"includes/glfw3.h"
#include <iostream>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window); // settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
int main() {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
// uncomment this statement to fix compilation on OS X
#endif
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "I love OpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl; glfwTerminate(); return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl; return -1;
}
while (!glfwWindowShouldClose(window))
{
processInput(window);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate(); return 0;
}
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
openmesh与opengl结合使用
#if 1
#include "glad/glad.h"
#include "includes/glfw3.h"
#include <OpenMesh/Core/IO/MeshIO.hh>
#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
typedef OpenMesh::TriMesh_ArrayKernelT<> MyMesh;
int main()
{
MyMesh mesh;
// 从文件中加载模型
if (!OpenMesh::IO::read_mesh(mesh, "triangle.off"))
{
std::cerr << "Error: Cannot read mesh from file 'output.off'" << std::endl;
return 1;
}
// 获取第一个顶点的句柄
MyMesh::VertexHandle vh = *(mesh.vertices_begin());
// 获取顶点的当前位置
MyMesh::Point current_pos = mesh.point(vh);
// 修改顶点的位置
mesh.set_point(vh, MyMesh::Point(current_pos[0] - 1.0, current_pos[1], current_pos[2]));
// 保存模型
/*if (!OpenMesh::IO::write_mesh(mesh, "modified_output.off"))
{
std::cerr << "Error: Cannot write mesh to file 'modified_output.off'" << std::endl;
return 1;
}*/
// 初始化GLFW
if (!glfwInit())
{
return -1;
}
// 创建一个OpenGL窗口
GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL Window", NULL, NULL);
if (!window)
{
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
// 初始化GLAD
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
return -1;
}
// 设置OpenGL渲染环境
glViewport(0, 0, 800, 600);
// ... (设置投影矩阵和视图矩阵,创建VBO,编写着色器等)
// 创建顶点缓冲对象 (VBO)
GLuint vbo;
glGenBuffers(1, &vbo);
// 绑定VBO
glBindBuffer(GL_ARRAY_BUFFER, vbo);
// 将模型的顶点数据传输到VBO
glBufferData(GL_ARRAY_BUFFER, mesh.n_vertices() * sizeof(MyMesh::Point),
&mesh.points()[0], GL_STATIC_DRAW);
// 创建顶点数组对象 (VAO)
GLuint vao;
glGenVertexArrays(1, &vao);
// 绑定VAO
glBindVertexArray(vao);
// 启用顶点属性数组
glEnableVertexAttribArray(0);
// 设置顶点属性指针
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
// 解绑VAO
glBindVertexArray(0);
// 顶点着色器代码
const char* vertexShaderSource = R"(
#version 330 core
layout (location = 0) in vec3 position;
void main()
{
gl_Position = vec4(position, 1.0);
}
)";
// 片段着色器代码
const char* fragmentShaderSource = R"(
#version 330 core
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 红色
}
)";
// 创建着色器程序对象
GLuint shaderProgram = glCreateProgram();
// 创建顶点着色器对象并编译
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
// 创建片段着色器对象并编译
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// 将顶点着色器和片段着色器附加到着色器程序
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
// 链接着色器程序
glLinkProgram(shaderProgram);
// 删除着色器对象(已经链接到程序中)
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// 渲染循环
while (!glfwWindowShouldClose(window))
{
glClear(GL_COLOR_BUFFER_BIT);
// 渲染模型
// 绑定VAO和着色器程序
glUseProgram(shaderProgram);
glBindVertexArray(vao);
// 绘制模型
glDrawArrays(GL_TRIANGLES, 0, mesh.n_vertices());
// 解绑VAO
glBindVertexArray(0);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
#else
#include <OpenMesh/Core/IO/MeshIO.hh>
#include <OpenMesh/Core/Mesh/PolyMesh_ArrayKernelT.hh>
typedef OpenMesh::PolyMesh_ArrayKernelT<> MyMesh;
int main()
{
// 创建一个Mesh对象
MyMesh mesh;
// 添加顶点
MyMesh::VertexHandle vhandle[4];
vhandle[0] = mesh.add_vertex(MyMesh::Point(0.0, 0.0, 0.0));//0
vhandle[1] = mesh.add_vertex(MyMesh::Point(1.0, 0.0, 0.0));//1
vhandle[2] = mesh.add_vertex(MyMesh::Point(0.0, 1.0, 0.0));//2
vhandle[3] = mesh.add_vertex(MyMesh::Point(0.0, 0.0, 1.0));//3
// 添加面
std::vector<MyMesh::VertexHandle> face_vhandles;
face_vhandles.clear();
face_vhandles.push_back(vhandle[1]);
face_vhandles.push_back(vhandle[0]);
face_vhandles.push_back(vhandle[2]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[1]);
face_vhandles.push_back(vhandle[2]);
face_vhandles.push_back(vhandle[3]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[0]);
face_vhandles.push_back(vhandle[1]);
face_vhandles.push_back(vhandle[3]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[2]);
face_vhandles.push_back(vhandle[0]);
face_vhandles.push_back(vhandle[3]);
mesh.add_face(face_vhandles);
// 保存网格到文件
OpenMesh::IO::write_mesh(mesh, "triangle.off");
return 0;
}
#endif
一些感悟
我发现一个很简单但是很实用的规律:在是OpenMesh编辑一个3D图像时,除了底部应该是顺时针,其他面都应该是逆时针。
要验证也很容易验证:在OpenMesh中编辑3D图像时,通常会遵循==“右手法则”==。这意味着当你站在多边形的外侧时,如果你用右手的拇指指向多边形的法线方向(也就是垂直于多边形的方向),那么其他手指的方向就是多边形的边缘顺时针方向。
所以,对于底部而言,它的法线方向应该是朝向多边形内部,所以多边形的边缘顺时针方向是正确的。对于其他面,法线方向应该朝向外部,因此多边形的边缘顺时针方向是逆时针的。
我一开始不管是尝试用stl去写一个3d图像,还是尝试使用OpenMesh来写一个3d图像,总是一次性写不对,总要来回改一下,知道了这个简单的规律就不会再犯错了
一些细碎知识点
1.OpenGL的状态通常被称为OpenGL上下文(Context)。
我常常在一些大佬的代码中看到xxx上下文,我常常为此感到不解,现在看来,上下文就可以理解为状态。
而OpenGL自身是一个巨大的状态机(State Machine)。所以,上下文和状态,状态机的概念相关!