使用OpenGL ES 2.0编写鼠标旋转纹理三维立方体
示例说明
实例说明了如何使用OpenGL ES 2.0和Qt编写鼠标旋转纹理三维立方体。展示了如何有效地处理多边形几何图形,以及如何为可编程图形流水线编写简单的顶点和片段着色器。此外,它还演示了如何使用四元数来表示三维对象的方向。
这个示例是为OpenGLES2.0编写的,但它也适用于桌面OpenGL,因为这个示例非常简单,大多数情况下与桌面OpenGL API都是相同的。它也可以在没有OpenGL支持的情况下进行编译,但是它只显示了一个标签,说明了OpenGL支持是必需的。
代码解析
该示例由两个类组成:
1,MainWidget类继承扩展了QGLWidget,包含OpenGL ES 2.0初始化、绘图以及鼠标和计时器事件处理
2,GeometryEngine类处理多边形几何图形。将多边形几何转换为顶点缓冲区对象,并从顶点缓冲区对象绘制几何图形。
MainWidget类定义
#include "geometryengine.h"
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QMatrix4x4>
#include <QQuaternion>
#include <QVector2D>
#include <QBasicTimer>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
class GeometryEngine;
class MainWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
explicit MainWidget(QWidget *parent = 0);
~MainWidget();
protected:
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void timerEvent(QTimerEvent *e) override;
void initializeGL() override;
void resizeGL(int w, int h) override;
void paintGL() override;
void initShaders();
void initTextures();
private:
QBasicTimer timer;
QOpenGLShaderProgram program;
GeometryEngine *geometries;
QOpenGLTexture *texture;
QMatrix4x4 projection;
QVector2D mousePressPosition;
QVector3D rotationAxis;
qreal angularSpeed;
QQuaternion rotation;
};
MainWidget类实现
由于OpenGLES2.0不再支持固定的图形管道,所以它必须由我们自己来实现。这使得图形管道非常灵活,但同时也变得更加困难,因为用户必须实现图形管道才能运行最简单的示例。它还使图形管道更高效,因为用户可以决定应用程序需要什么样的管道。
首先,我们必须实现顶点着色。以顶点数据和model-view-projection matrix(MVP)为参数.利用MVP矩阵变换顶点位置到屏幕空间,并将纹理坐标传递给破片着色器。纹理坐标将自动插值在多边形表面。
在此之后,我们需要实现第二部分的图形管线-片段着色器。在这个练习中,我们需要实现处理纹理的片段着色器。它将插值的纹理坐标作为参数,并从给定的纹理中查找片段颜色。
使用QGLShaderProgram,我们可以编译、链接和绑定着色器代码到图形管道。此代码使用Qt资源文件访问着色器源代码。
QGLWidget接口实现了将纹理从QImage加载到GL纹理内存的方法。我们仍然需要使用OpenGL提供的功能来指定GL纹理单元和配置纹理筛选选项。
#include "mainwidget.h"
#include <QMouseEvent>
#include <math.h>
MainWidget::MainWidget(QWidget *parent) :
QOpenGLWidget(parent),
geometries(0),
texture(0),
angularSpeed(0)
{
}
MainWidget::~MainWidget()
{
//在删除纹理和缓冲区时,确保上下文是当前的。
makeCurrent();
delete texture;
delete geometries;
doneCurrent();
}
//! [0]
void MainWidget::mousePressEvent(QMouseEvent *e)
{
// 保存鼠标按下位置
mousePressPosition = QVector2D(e->localPos());
}
void MainWidget::mouseReleaseEvent(QMouseEvent *e)
{
// 鼠标释放位置-鼠标按下位置
QVector2D diff = QVector2D(e->localPos()) - mousePressPosition;
//旋转轴垂直于鼠标位置差矢量
QVector3D n = QVector3D(diff.y(), diff.x(), 0.0).normalized();
// 加速角速度相对于鼠标扫掠的长度
qreal acc = diff.length() / 100.0;
// 计算新的旋转轴为加权和
rotationAxis = (rotationAxis * angularSpeed + n * acc).normalized();
// 增加角速度
angularSpeed += acc;
}
void MainWidget::timerEvent(QTimerEvent *)
{
// 降低角速度(摩擦)
angularSpeed *= 0.99;
// 当速度低于阈值时停止旋转
if (angularSpeed < 0.01) {
angularSpeed = 0.0;
} else