QT下使用OpenGL渲染Mesh的框架

说说我的QT下使用OpenGL渲染Mesh的框架

http://www.xpc-yx.com/2015/03/13/%E8%AF%B4%E8%AF%B4%E6%88%91%E7%9A%84qt%E4%B8%8B%E4%BD%BF%E7%94%A8opengl%E6%B8%B2%E6%9F%93mesh%E7%9A%84%E6%A1%86%E6%9E%B6/
作者: 远行  分类: CGAL, OpenGL, QT, UI框架, 三维模型处理, 图形学  发布时间: 2015-03-13 21:22  ė11,254 次阅读 65条评论

我终于把界面换成qt的了,即使我从大二开始就用mfc,一直用到去年上半年。也终于到了,我实在受不了mfc代码混乱的时候了,qt那么方便的东西,我为什么不早点用了。这里就不吐槽mfc的混乱,qt的方便了。在mfc里面建立一个复杂的界面,比如说有dock窗口,或者tab页面的,代码多,而且混乱麻烦,更无语的是,每次我都必须去搜索下,解决各种各样的问题,有的还可以理解,有的就是莫名其妙了。更别说什么代码清晰,跨平台之类的,何止是天方夜谭。

下面,说说我自己逐渐搭建的这个框架吧。其实,这个框架是从我上一个项目里,使用mfc的单文档单视图,主窗口里面有几个dockpane,视图里面放置了渲染OpenGL的子窗口,过渡来的。在qt的框架里面,mfc所有的这些恶心的东西都没有了,只剩下一个MainWindow类。在这个类里面,创建菜单,工具条,状态条,主窗口,dock窗口,tab窗口。代码简洁方便。渲染opengl的窗口设置为MainWindow的主widget。渲染窗口只是一个普通的widget而已,可以任意放置。

这样的做法能够把界面和渲染窗口独立出来,渲染窗口对应一个独立的类(COglWidget),所以方便创建不同类型的复杂界面。我觉得我应该先用viso画个类图,才能说清楚。下图是我的界面。中间是COglWidget。右侧是一个DockWidget,里面放置了个设置Mesh属性的widget。

通过主窗口的构造函数,可以看看如何创建这个界面,虽然在主窗口里面做这么的事情不是个好的习惯。

1 CMainWindow::CMainWindow(QWidget *parent)
2         : QMainWindow(parent)
3     {
4         //setWindowFlags(this->windowFlags() & ~Qt::WindowMaximizeButtonHint);//隐藏最大化按钮
5         setWindowTitle(tr("Skeleton Extract"));
6  
7         m_pSEMesh = new CSEMesh();
8         m_pOglForSE = new COglWidget(this, m_pSEMesh);
9         m_pSEMesh->SetOglWidget(m_pOglForSE);
10         setCentralWidget((QWidget*)m_pOglForSE);//设置中间是渲染OpenGL的widget     
11  
12         m_pPCMesh = new CPointCloudMesh();
13         m_pOglForPC = new COglWidget(this, m_pPCMesh);
14         //m_pOglForPC->SetEye(0.0, 0.0, 1.0);
15         m_pOglForPC->setFixedSize(960, 720);
16         m_pOglForPC->setWindowTitle(tr("Point Cloud For Geodesic Matrix Eigen Vector"));
17         m_pOglForPC->move((QApplication::desktop()->width() - m_pOglForPC->width()) / 2,
18             (QApplication::desktop()->height() - m_pOglForPC->height())/2);
19         m_pOglForPC->hide();
20         m_pOglForPC->setWindowFlags(Qt::Window);
21  
22         CreateActions();
23         CreateMenus();
24         CreateToolBars();
25         CreateStatusBar();
26         CreateDockWidgets();
27  
28         setMinimumSize(960, 720);
29         setWindowState(Qt::WindowMaximized);
30  
31         std::string strName = "MeshData\\bunny_3k.m";
32         m_pSEMesh->LoadModel(strName.c_str());
33         m_pMeshNameLabel->setText(QString(strName.c_str()));
34     }

再来看一个使用tabwidget的界面,记得在mfc中使用tab页面应该是比较麻烦,我也没用过。不过,在qt里面,唯一需要改变的是把tabwidget设置为central的widget,把其它widget加入到tab页面里面就行了。效果如图:

这个界面的创建代码如下,这里我把菜单,工具条,状态栏都去掉了。

1 CMainWindow::CMainWindow(QWidget *parent)
2         : QMainWindow(parent)
3     {
4         //setWindowFlags(this->windowFlags() & ~Qt::WindowMaximizeButtonHint);//隐藏最大化按钮
5         setWindowTitle(tr("Virtual Box"));
6  
7         m_pMesh = new CCubeMesh();
8         m_pOglWidget = new COglWidget(this, m_pMesh);
9  
10         m_pMainViewMesh = new CCubeMesh();
11         m_pMainViewMesh->DrawStyle(CCubeMesh::MAIN_VIEW);
12         m_pMainViewWidget = new CViewWidget(this, m_pMainViewMesh);
13  
14         m_pTopViewMesh = new CCubeMesh();
15         m_pTopViewMesh->DrawStyle(CCubeMesh::TOP_VIEW);
16         m_pTopViewWidget = new CViewWidget(this, m_pTopViewMesh);
17  
18         m_pSideViewMesh = new CCubeMesh();
19         m_pSideViewMesh->DrawStyle(CCubeMesh::SIDE_VIEW);
20         m_pSideViewWidget = new CViewWidget(this, m_pSideViewMesh);
21  
22         //CreateActions();
23         //CreateMenus();
24         //CreateToolBars();
25         //CreateStatusBar();
26         //CreateDockWidgets();
27  
28         setMinimumSize(960, 720);
29         setWindowState(Qt::WindowMaximized);
30  
31         //std::string strName = "cube.obj";
32         //m_pMesh->LoadModel(strName.c_str());
33         //m_pMeshNameLabel->setText(QString(strName.c_str()));
34         m_pTabWidget = new QTabWidget(this);
35         m_pTabWidget->addTab(m_pOglWidget, "Cube View");
36         m_pTabWidget->addTab(m_pMainViewWidget, "Main View");
37         m_pTabWidget->addTab(m_pTopViewWidget, "Top View");
38         m_pTabWidget->addTab(m_pSideViewWidget, "Side View");
39         setCentralWidget((QWidget*)m_pTabWidget);
40     }

从上面的代码中,可以看到创建一个OglWidget需要一个Mesh指针。在我的框架中,COglWidget内中只使用几个固定的Mesh函数。因此,可以把COglWidget实现为依赖于一个IMesh接口,这样就进一步把OpenGL窗口和Mesh分离开来。不管Mesh类的具体实现如何,OpenGL窗口只需要依赖IMesh接口,因此,第一个界面里面的Mesh类实际上组合了一个CGAL的CGAL::Polyhedron_3对象。而第二个界面的Mesh类是一个简单的渲染长方体的类,不过支持渲染多个视图而已。
下面说说,COglWidget类具有哪些功能。首先,渲染Mesh,包括设置视口,eye,投影,光照,背景等等,其次,处理鼠标按键操作模型,我的实现是把所有的鼠标按键消息都丢给了下层的一个trackball类,这个trackball类支持旋转缩放移动模型,另外还写了几个打开文件,以及选取背景色的操作。
现在到了Mesh的部分了。因为跟OpenGL窗口相关的只是IMesh接口,所以,我可以任意实现实际的Mesh类。
IMesh的定义如下,

1 #ifndef IMESH_H
2 #define IMESH_H
3 #include "libyx/vertex.h"
4  
5 namespace LibYX
6 {
7     struct IMesh
8     {
9         IMesh()
10         {
11             m_pMatMV = new double[16];
12             m_pMatProj = new double[16];
13             m_pViewport = new int[4];
14         }
15  
16         virtual ~IMesh()
17         {
18             FreeModel();
19             delete[] m_pMatMV;
20             delete[] m_pMatProj;
21             delete[] m_pViewport;
22         }
23  
24         virtual bool LoadModel(const char* pFilename) { return true; }
25         virtual void SaveModel(const char* sFilename) {}
26         virtual void UnifyModel() {}
27  
28         virtual bool HasModelLoad() const return true; }
29         virtual void FreeModel() {}
30  
31         virtual void InitData() {}
32         virtual void DestroyData() {}
33  
34         virtual void DrawScene()
35         {
36             glGetDoublev(GL_MODELVIEW_MATRIX, m_pMatMV);
37             glGetDoublev(GL_PROJECTION_MATRIX, m_pMatProj);
38             glGetIntegerv(GL_VIEWPORT, m_pViewport);
39         }
40  
41         void WindowWidth(int nWidth) { m_nWinWidth = nWidth; }
42         int WindowWidth() const return m_nWinWidth; }
43         void WindowHeight(int nHeight) { m_nWinHeight = nHeight; }
44         int WindowHeight() const return m_nWinHeight; }
45  
46     protected:
47         double* m_pMatMV;//模型视图矩阵
48         double* m_pMatProj;//投影矩阵
49         int* m_pViewport;//视口
50         int m_nWinWidth;
51         int m_nWinHeight;
52  
53     protected:
54         v3d m_vMax, m_vMin;         // bounding box of the scene
55     };
56 };
57  
58 #endif

其中,DrawScene()是渲染接口,另外还有加载模型的接口等,里面还有些数据。我用CTriMesh继承IMesh接口,同时组合Enriched_Model *m_pModel的指针,创建一个CGAL的多边形网格模型,实际的操作基本都交给m_pModel。这个类的定义太复杂了,就不贴了。这个类里面我集成了基本操作,比如按照点,线,面方式渲染,渲染包围盒,坐标轴,法线,顶点,边,面的索引等,加载和保存模型等等。因此,CTriMesh作为模型的基类,在实际的项目中,再继承CTriMesh,比如第一个界面中的CSEMesh,在CSEMesh中进行具体的处理。
所以,实际的算法实现都在CSEMesh里面。因为,IMesh和CTriMesh,COglWidget可以固化了。能够变化的只是CMainWindow。如果要添加新的代码,只能继承CTriMesh,在子类里面实现具体的算法,也算是对修改关闭对扩展开放吧。

因此,如果再做类似的东西,我就有一个很方便的框架了。。。

谈谈QT和OpenGL

作者: 远行  分类: CGAL, OpenGL, QT, 三维模型处理, 图形学  发布时间: 2013-11-07 17:47  ė11,470 次阅读  6暂无评论

最近用了点时间学习了QT,重新阅读了下OpenGL超级宝典第四版,并且基本阅读完了。坑爹的是看完了第四版居然在网上发现了已经可以下载第五版的中文版了。翻阅了下第五版的目录,发现作者已经把GPU编程融合到整本书里面去了。而不像第四版和第三版那样只是分出几张做些介绍而已。我以前在图书馆借了本OpenGL编程宝典第三版,居然只有第三版(PS:我堂堂大中南的图书馆也该更新下了,真不明白图书馆买那么多不需要的办公室软件书籍作甚,整个图书馆只能索引到3本qt相关的书籍),里面居然还有用汇编语言形式的GPU底层着色操作。这个在第四版就去掉了。第五版我没有仔细阅读。以后有时间的话,还是阅读下第五版的内容吧。接下来,也不打算继续看Qt的东西了,毕竟界面编程这种东西,看多了没意义。我毕竟用了几年的MFC,看qt这种封装的太好的东西跟看手册一样的,所以我还是不需要继续去背书了。。。
所以,接下来可能学习下着色语言的所谓橙皮书。也可能阅读英文原版的,毕竟我还没看过一本英文原版的书籍,也提高一下英语。还有要看看微分几何的书籍了。如果还有时间的话,看看数字图像处理,以及matlab的一些东西。
我还是回到标题吧。。。
前段时间,用MFC做了个交互式分割模型的程序,我用的CSplitWnd切分主窗口为三个视图。做这个界面的过程中,遇到最恶心的问题是,我的一个基于对话框的视图无法很好的自放缩子控件,也就是自动调整控件大小。不要跟我说在OnSize里面处理下就行了。不要告诉我再禁止掉滚动条。总之很难达到想要的结果。而且,代码会乱成一团,本来就不爽mfc框架生成的那么多代码。不要以为我在这里喷MFC,我毕竟是学MFC过来的,用了很多次。我以前的界面99%都是MFC做的。可以说是MFC养大了我,但是我慢慢发现我还是对MFC越来越不满了。
所以,我就去看Qt了。同学介绍有个豆子的空间里面关于qt的教程不错,我就把前面的大部分看了一遍,并且都把代码提取出来,全部整合到一个工程里面实验了一下。总之,发现qt还不错吧。至少对于前面的那个问题,qt里面非常好解决,因为它有水平布局和垂直布局的概念。
至于Qt和Opengl的结合,也非常简单。直接继承QGLWidget,实现几个函数就行了。qt5还提供了增强版本的3d类QGLView。这里我要说的是libQglviewer。这个是在qt上的一个增强opengl渲染C++库。封装了很多默认的功能,非常强大。cgal上面的qt框架的3d显示就是推荐的这个东西。
至于模型加载,我打算试试Assimp,用这个加载模型之后,提取出数据,再来初始化cgal的数据结构。这样就不用处理各种烦人的obj,m格式等等了。。。
估计下一个需要渲染的东西就是用这些东西实现了吧。


你可能感兴趣的:(openGL,C++/C)