前言
在Qt中使用OpenGL(一)
在Qt中使用OpenGL(二)
在Qt中使用OpenGL(三)
在Qt中使用OpenGL(四)
在Qt中使用OpenGL(五)
在前面的文章中,我们终于实现了自由查看3D世界的功能,现在无论我们之后要创建什么样的3D世界,我们都有能力自由的在其中探索了。
那么,接下来就是需要大家努力思考的问题了:我们要如何构建一个3D世界?
难不成,我们需要手动的为所有我们想要画出来的东西,编写顶点信息和纹理坐标?哪怕我们要画出来1000头牛也要手动进行?
很显然这是不可能的。
模型的基类
一般情况下,构建一个3D世界我们会使用模型这个概念。
一个模型我们就可以理解为一个物体。
比如,一个茶壶,一个人,一个桌子,一张凳子,它们都可以是一个模型。
那么,我们只要指定这些模型的位置和旋转角度,乃至大小,是不是一个简单的3D世界我们就构建出来了?
那么,根据我们之前对于OpenGL的了解,想要画出一个东西出来,我们至少需要两个东西:顶点和纹理。
其中,顶点应该包含位置坐标和纹理坐标,而纹理则是一张图,不,是至少一张图。
之前我们也说过,在OpenGL的世界中,纹理坐标是基于当前激活的纹理来计算的。这也就意味着,我们只需要在绘制前激活不同的纹理,我们就可以在绘制的时候使用不同的纹理来绘制我们的模型。
简单来说,就是茶壶和桌子可以使用不同的纹理。
我们只需要在绘制茶壶的时候激活茶壶纹理,绘制桌子的时候激活桌子纹理就行了。乃至我们可以在绘制茶壶嘴的时候激活一张贴图,绘制茶壶盖的时候激活另一张贴图都是没什么问题的。
好了,那么让我们来总结一下,一个模型中至少应该有哪些东西。
- 顶点信息(包括位置坐标和纹理坐标)
- 纹理(可以有多个纹理)
- 最终在3D世界中的变换(也就是Shader中需要的模型矩阵,可以用来让模型缩放,旋转,平移)
当然,实际情况的模型是会包含更多的内容的,考虑到目前有些东西我们还没有掌握,所以这里就用我们已知的知识来总结。
除了这些数据上的东西,对于要绘制一个模型,我很显然还需要VAO,VBO和Shader。并且,是每一个模型,都需要一个独立的VAO,VBO与Shader。因为我们目前就是这么做的,那么当我们需要画多个物体的时候,重复多次我们之前的行为是一个很正常的操作。
那么,此时你的脑海中是不是就出现了一个类的基本架构了呢?
struct Vertex
{
QVector3D pos;
QVector2D texture;
};
class Model : public QOpenGLExtraFunctions
{
public:
Model();
~Model();
public:
void setScale(float val) {
m_scale = val; }
void setRotate(const QVector3D &rotate) {
m_rotate = rotate; }
void setPos(const QVector3D &pos) {
m_pos = pos; }
public:
void setVertices(const QVector<Vertex> &vertices) {
m_vertices = vertices; }
void setTexture(QOpenGLTexture *texture, int index = -1);
void setShaderProgram(QOpenGLShaderProgram *program) {
m_program = program; }
public:
QMatrix4x4 model();
public:
virtual void init();
virtual void update();
virtual void paint(const QMatrix4x4 &projection, const QMatrix4x4 &view);
protected:
QVector3D m_pos{
0,0,0 };
QVector3D m_rotate{
0,0,0 };
float m_scale = 1;
QVector<Vertex> m_vertices;
QMap<int, QOpenGLTexture *> m_textures;
QOpenGLVertexArrayObject m_vao;
QOpenGLBuffer m_vbo;
QOpenGLShaderProgram *m_program = nullptr;
};
我们的模型继承QOpenGLExtraFunctions
是为了方便使用OpenGL的函数。
成员函数与变量都至少是protected是因为我们实际上目前无法知道一个模型绘制的细节,所以我们首先制定好一个模型的核心内容,形成一个基类,然后将实现的细节部分交给继承的类来做。
很容易理解的原因就是,我们没有办法确定,我们在初始化与绘制的时候,具体要使用什么样的Shader,怎么绑定数据,也不知道具体要怎么去绘制三角形,是画两个三角形形成一个矩形然后重复呢?还是只画一个三角形然后重复呢?
所以,我们先只顶下一个框架。
此时我们可以做的事情不多,也就是如何保存纹理以及如何生成模型矩阵。
void Model::setTexture(QOpenGLTexture *texture, int index)
{
if (index == -1)
{
if (m_textures.isEmpty())
{
index = 0;
}
else
{
index = m_textures.keys().last() + 1;
}
}
m_textures.insert(index, texture);
}
QMatrix4x4 Model::model()
{
QMatrix4x4 _mat;
_mat.setToIdentity();
_mat.translate(m_pos);
_mat.rotate(m_rotate.x(), 1, 0, 0);
_mat.rotate(m_rotate.y(), 0, 1,