0%

Qt+OpenGL 3.类体系优化

由于是初次尝试面向对象的编程,觉得之前搭建的结构有诸多不妥之处。因此进行一次整理优化。

除非程序员做功,否则代码总是朝着熵增的方向进行——热力学第四定律

着色器类

在之前的代码中,初始化着色器、管理着色器的任务是交给glwidget类的,感觉不是非常合理。因为一种模型类只对应于一种着色器,两者之间是紧耦合的,应该由模型负责管理自己的着色器。

另一方面,多个模型类的实例可能使用同一种着色器,为每个实例分配一个着色器对象可能比较浪费,也不便于着色器的统一管理。因此我使用设计模式中的单例模式(Singleton Method),建立一个着色器的单体类。

首先是一个标准的单体类模板,通过将构造函数声明为protected的,防止单体类被外部初始化,而静态成员变量ms_Singleton指向该单体类的唯一实例。GetSingleton()以及GetSingletonPtr()函数是访问该实例的唯一接口。该实例在第一次访问时被创建。

singleton.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#ifndef SINGLETON_H
#define SINGLETON_H
#include <cassert>
template<typename T> class Singleton
{
protected:
static T* ms_Singleton;

protected:
Singleton( void )
{
assert( !ms_Singleton );
ms_Singleton = static_cast<T*>(this);
}
public:
~Singleton( void )
{ assert( ms_Singleton ); ms_Singleton = 0; }

static T& GetSingleton( void )
{ return *(GetSingletonPtr()); }

static T* GetSingletonPtr( void )
{
if(!ms_Singleton)
{
ms_Singleton = new T();
}
return ms_Singleton;
}
};

template<class T> T* Singleton<T>::ms_Singleton = 0;

#endif // SINGLETON_H

之后,创建一个着色器类,并令其继承单体模板类

1
class GLShader:Singleton<GLShader>

将本来在GLWidget类中的着色器对象,交由着色器类管理

1
2
3
4
public:
//着色器列表
QOpenGLShaderProgram shader;
QOpenGLShaderProgram CADshader;

本来在GLWidgetTools命名空间中的着色器初始化函数initShader(),也要移植到GLShader类中。总的代码如下:

glshader.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef GLSHADER_H
#define GLSHADER_H
#include <QOpenGLShaderProgram>
#include <QOpenGLShaderProgram>
#include "singleton.h"
//统一管理所有shader
class GLShader:Singleton<GLShader>
{
public:
static GLShader& GetSingleton(void);
static GLShader* GetSingletonPtr(void);
GLShader();
~GLShader();

public:
//着色器列表
QOpenGLShaderProgram shader;
QOpenGLShaderProgram CADshader;

private:
void initShader(QOpenGLShaderProgram *shader, QString vs, QString fs, QString gs="");
};
#endif // GLSHADER_H
glshader.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include "glshader.h"

GLShader &GLShader::GetSingleton()
{
return Singleton<GLShader>::GetSingleton();
}

GLShader *GLShader::GetSingletonPtr()
{
return Singleton<GLShader>::GetSingletonPtr();
}

GLShader::GLShader()
{
initShader(&shader,":/shader/shader.vs",":/shader/shader.fs");
initShader(&CADshader,":/shader/CADshader.vs",":/shader/CADshader.fs");
}

//加载shader文件
void GLShader::initShader(QOpenGLShaderProgram *shader, QString vs, QString fs, QString gs)
{
if(!shader->addShaderFromSourceFile(QOpenGLShader::Vertex, vs)){
qDebug()<<shader->log();
return;
}
if(gs!=""&&!shader->addShaderFromSourceFile(QOpenGLShader::Geometry, gs)){
qDebug()<<shader->log();
return;
}
if(!shader->addShaderFromSourceFile(QOpenGLShader::Fragment, fs)){
qDebug()<<shader->log();
return;
}

if(!shader->link()){
qDebug()<<shader->log();
return;
}
if(!shader->bind()){
qDebug()<<shader->log();
return;
}
shader->release();
}

将着色器交给模型类

第二步是将着色器从GLWidget类中转移到GLModel类中。首先在GLModel类内创建一个指向着色器的指针,之后要在模型初始化过程中获取着色器对象。同样,此处采用了工厂模式,子类可以方便地改变着色器类型。另外,为了给在运行期间内更换着色器留出接口,创建了setShader()成员函数。

工厂函数在初始化函数setupModel()中调用,默认返回指向最简略的shader的指针。而在CADShader类中,重写了着色器工厂函数,返回指向CADshader的指针。

openglmodel.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
...
class GLModel
{
...
public:
void setShader(QOpenGLShaderProgram* s);
protected:
//着色器工厂
virtual QOpenGLShaderProgram* shaderFactory();
QOpenGLShaderProgram* shader;
}
class CADModel
{
...
protected:
virtual QOpenGLShaderProgram* shaderFactory();
}
openglmodel.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
void GLModel::setShader(QOpenGLShaderProgram *s)
{
shader=s;
}
void GLModel::setupModel()
{
shader=shaderFactory();
...
}
//着色器工厂函数
QOpenGLShaderProgram *GLModel::shaderFactory()
{
return &GLShader::GetSingletonPtr()->shader;
}

QOpenGLShaderProgram *CADModel::shaderFactory()
{
return &GLShader::GetSingletonPtr()->CADshader;
}
...

当然,在GLWidget类中,将着色器相关内容移除了。

抽象模型类

考虑到之后可能会有各种模型需要绘制,并且需要对模型进行一个层次化的管理。于是考虑使用设计方法中的Composite模式,将模型组织成树状结构。


Composite模式的类图[GoF]
首先,要将之前的GLModel类向上抽象一层,形成一个抽象的Model类,对应类图中的Component类。然后要派生出一个CompositeModel类,进行模型的管理与组织。
openglmodel.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class AbstractModel
{
public:
AbstractModel(GLWidget* f);
virtual ~AbstractModel()=0;
virtual void Draw()=0;
virtual void setupModel();
void setCamera(std::shared_ptr<AbstractCamera> c);
protected:
std::shared_ptr<AbstractCamera> myCamera;
GLWidget* father;
};
class CompositeModel:public AbstractModel
{
public:
CompositeModel(GLWidget* f):AbstractModel(f){};
virtual ~CompositeModel();
virtual void Draw();
void addChild(std::shared_ptr<AbstractModel> c);
void removeChile();
protected:
vector<std::shared_ptr<AbstractModel>> children;
};
openglmodel.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
AbstractModel::~AbstractModel()
{
}
void AbstractModel::Draw()
{
}
AbstractModel::AbstractModel(GLWidget *f)
:father(f)
{
}
void AbstractModel::setupModel()
{
}
void AbstractModel::setCamera(std::shared_ptr<AbstractCamera> c)
{
myCamera=c;
}
CompositeModel::~CompositeModel()
{

}
void CompositeModel::Draw()
{
//绘制所有子模型
for(auto &model:children){
model->Draw();
}
}
void CompositeModel::addChild(std::shared_ptr<AbstractModel> c)
{
children.push_back(c);
}
void CompositeModel::removeChile()
{
children.pop_back();
}

之后如果要将模型以不同的类别进行管理,则可以利用CompositeModel类派生出有具体管理功能的类。