本篇文章的复现对应于LearnOpenGL教程的入门变换,坐标系统,摄像机三篇教程。
OpenGL编程中,始终会用到的除了顶点和着色器外,应该就属摄像机了。
在QT封装的OpenGL中,可以通过QMatrix4x4这个类来设置摄像机的各种参数,其中最为基础的两个函数为perspective及lookAt。
perspective函数
用于设置摄像头自身的信息,有四个参数,含义如下:
参数一:视角大小(度数)
参数二:视角的宽长比
参数三:近平面距离
参数四:远平面距离
通过设置视角的大小,我们可以模拟出远近的效果。例如在近距离下,我们能看到的东西会比在远距离少,因此设置视角从大变小就模仿出了由远及近的感觉。
lookAt函数
用于设置摄像头的位置和朝向,有三个三维的参数,含义如下:
参数一:相机位置,上图所示的相机所处的(0,0,2)位置
参数二:观察目标点位置,上图所示的(0,0,0)位置
参数三:相机上向量也就是图3中的灰色那个向量
这里面前两个参数非常好懂,改变第一个参数相当于改变摄像头的观察位置,有规律的改变这个值就能起到从不同角度观察物体的效果。而改变第二个参数则相当于改变世界坐标系的原点,整个世界坐标系上的物体将随之移动。
第三个参数就比较懵圈,加上网上有些一知半解的人乱解释,把这个参数弄的更加的让人迷惑。要弄清楚这个参数到底是个啥,首先我们要分析前面两个参数到底告诉我们什么东西。参数一告诉了我们相机的位置,而参数二告诉了我们相机要观察的目标点位置,因此这两个位置一连线就直接确定了相机的朝向,也就是z轴的向量,如图2所示。这个时候x和y轴还没有确定,它们可以取垂直于z轴且相互之间垂直的任何值。为了将这两个量定下来,OpenGL采用了一个策略,就是让程序员自行给出一个不为0且不与z轴同向的向量(即上向量),然后根据两个向量叉乘得到新的向量将同时垂直于这两个向量这一特性,求得x轴(图3中的红色向量)。随后再由x轴和z轴求得最后图4的y轴向量。第三个参数给出的正是这个不太直观的上向量,理论上我们可以通过设置这个值来改变摄像头的x坐标方向进而最后改变看到的图像,但这个操作比较反直觉,因此在很多教程里面直接把这个值写成(0,1,0)。
rotate函数
除了上面两个函数之外,还有rotate函数可以用来帮助旋转摄像头位置。虽然可以直接通过lookAt函数的第一个参数来做这件事情,但那需要自己去计算,不是特别方便。因此有了rotate函数来帮忙做这件事情。
函数有两个参数,含义如下:
参数一:旋转的度数
参数二:绕着旋转的向量
有了上面的三个函数,我们就可以做出具备动态效果的OpenGL程序啦。具体的代码如下:
MyOpenGLWidget.h
#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLBuffer>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLShaderProgram>
#include <QElapsedTimer>
class MyOpenGLWidget : public QOpenGLWidget
{
Q_OBJECT
public:
MyOpenGLWidget(QWidget *partent);
~MyOpenGLWidget();
protected:
void initializeGL();//初始化函数,在Widget刚加载时被调用
void paintGL();//绘图函数,每一次绘图请求产生,都会进行一次绘图
void resizeGL(int w, int h);//用于处理窗口大小变化的情况
private:
QOpenGLBuffer *m_vbo,*m_cbo;
QOpenGLVertexArrayObject *m_vao;
QOpenGLShaderProgram *m_shader; // 渲染器程序对象
QElapsedTimer *m_timer;
};
#endif // MYOPENGLWIDGET_H
MyOpenGLWidget.cpp
#include <MyOpenGLWidget.h>
MyOpenGLWidget::MyOpenGLWidget(QWidget* parent):
QOpenGLWidget (parent)
{
m_timer = new QElapsedTimer;
m_timer->start();
}
MyOpenGLWidget::~MyOpenGLWidget()
{
}
void MyOpenGLWidget::initializeGL()
{
QOpenGLFunctions *f = this->context()->functions();
f->glEnable(GL_DEPTH_TEST); // 三维绘图的关键!
m_shader = new QOpenGLShaderProgram();
m_shader->addShaderFromSourceFile(QOpenGLShader::Vertex, "../Demo1/Vertex.vert");
m_shader->addShaderFromSourceFile(QOpenGLShader::Fragment, "../Demo1/Fragment.frag");
if (m_shader->link()) {
qDebug("Shaders link success.");
} else {
qDebug("Shaders link failed!");
}
m_vao = new QOpenGLVertexArrayObject();
m_vbo = new QOpenGLBuffer(QOpenGLBuffer::Type::VertexBuffer);
m_cbo = new QOpenGLBuffer(QOpenGLBuffer::Type::VertexBuffer);
m_vao->create();
m_vao->bind();
m_vbo->create();
m_vbo->bind();
const GLfloat VERTEX_INIT_DATA[] = {
//face 1
-0.5f, 0.0f, -0.2887f,
0.5f, 0.0f, -0.2887f,
0.0f, 0.0f, 0.5774f,
//face 2
-0.5f, 0.0f, -0.2887f,
0.5f, 0.0f, -0.2887f,
0.0f, 0.8165f, 0.0f,
//face 3
-0.5f, 0.0f, -0.2887f,
0.0f, 0.0f, 0.5774f,
0.0f, 0.8165f, 0.0f,
//face 4
0.5f, 0.0f, -0.2887f,
0.0f, 0.0f, 0.5774f,
0.0f, 0.8165f, 0.0f,
};
m_vbo->allocate(VERTEX_INIT_DATA, 4 * 3 * 3 * sizeof(GLfloat));
f->glEnableVertexAttribArray(0);
f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,3*sizeof(GLfloat), 0);
m_vbo->release();
m_cbo->create();
m_cbo->bind();
const GLfloat COLOR_INIT_DATA[] = {
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f,
};
m_cbo->allocate(COLOR_INIT_DATA, 4 * 3 * 3 * sizeof(GLfloat));
f->glEnableVertexAttribArray(1);
f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE,3*sizeof(GLfloat), 0);
m_cbo->release();
m_vao->release();
}
void MyOpenGLWidget::paintGL()
{
QOpenGLFunctions *f = this->context()->functions();
f->glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
f->glClearColor(0.0f, 0.2f, 0.0f, 1.0f);
m_vao->bind();
m_shader->bind();
//相机设置
QMatrix4x4 mvp;
//相机自身参数,第一个参数是视角大小(度数),第二个为视角的长宽比,第三个参数是近平面距离,第四个参数是远平面距离
mvp.perspective(45.0f, 4/3.0, 0.1f, 100.0f);
mvp.lookAt(QVector3D(3.0f, 0.0f, 0.0f), QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0f, 1.0f, 0.0f));
float time_in_second = (float)m_timer->elapsed() / 1000;
mvp.rotate(10.0f * time_in_second, QVector3D(1.0f, 0.0f, 0.0f));//绕z轴旋转
// mvp.rotate(1.0f * time_in_second, QVector3D(0.0f, 1.0f, 0.0f));//绕y轴旋转
m_shader->setUniformValue(m_shader->uniformLocation("MVP"), mvp);
f->glDrawArrays(GL_TRIANGLES, 0, 4 * 3);
m_shader->release();
m_vao->release();
this->update();
}
void MyOpenGLWidget::resizeGL(int w, int h)
{
Q_UNUSED(w);
Q_UNUSED(h);
}