cube旋轉立方體(Qt-OpenGL)
主窗口類MainWidget
MainWidget : public QOpenGLWidget, protected QOpenGLFunctions
- 鼠標事件,更新四元數用於旋轉
void MainWidget::mousePressEvent(QMouseEvent *e)
{
// Save mouse press position
mousePressPosition = QVector2D(e->localPos());
}
//! @brief 釋放鼠標,更新四元數
void MainWidget::mouseReleaseEvent(QMouseEvent *e)
{
// Mouse release position - mouse press position
QVector2D diff = QVector2D(e->localPos()) - mousePressPosition;
// Rotation axis is perpendicular to the mouse position difference
// vector
//--旋轉軸(矢量) 垂直於鼠標位置差
QVector3D n = QVector3D(diff.y(), diff.x(), 0.0).normalized();
// Accelerate angular speed relative to the length of the mouse sweep
//--角加速度與鼠標掃過的長度關聯
qreal acc = diff.length() / 100.0;
// Calculate new rotation axis as weighted sum
//--計算新的 四元數的旋轉軸矢量 爲加權和
//-- 旋轉軸矢量 = 原來的旋轉軸矢量 + 新的旋轉軸矢量
rotationAxis = (rotationAxis * angularSpeed + n * acc).normalized();
// Increase angular speed
//--增加角速度(四元數的偏轉角度)
angularSpeed += acc;
}
- 定時器事件: 減少偏轉角,更新四元數,更新UI
//! @brief 定時觸發減少偏轉角,更新四元數,更新UI
void MainWidget::timerEvent(QTimerEvent *)
{
// Decrease angular speed (friction)
//--降低角速度(摩擦)
angularSpeed *= 0.99;
// Stop rotation when speed goes below threshold
//--當速度低於閾值時停止旋轉
if (angularSpeed < 0.01) {
angularSpeed = 0.0;
} else {
// Update rotation
//--更新旋轉(四元數)
rotation = QQuaternion::fromAxisAndAngle(rotationAxis, angularSpeed) * rotation;
// Request an update
//--更新UI
update();
}
}
- 重寫函數 initializeGL()
初始化OpenGL
創建立方體引擎GeometryEngine
啓動定時器事件
void MainWidget::initializeGL()
{
//--初始化OpenGL
initializeOpenGLFunctions();
//--清除顏色\透明度
glClearColor(0, 0, 0, 1);
initShaders();
initTextures();
//! [2]
// Enable depth buffer
//--啓用深度緩衝
glEnable(GL_DEPTH_TEST);
// Enable back face culling
//--啓用背面剔除
glEnable(GL_CULL_FACE);
//! [2]
geometries = new GeometryEngine;
// Use QBasicTimer because its faster than QTimer
timer.start(12, this);
}
- 重寫函數 resizeGL() ,窗體尺寸改變時刷新
void MainWidget::resizeGL(int w, int h)
{
// Calculate aspect ratio
//--計算縱橫比
qreal aspect = qreal(w) / qreal(h ? h : 1);
// Set near plane to 3.0, far plane to 7.0, field of view 45 degrees
//--設置近平面爲3.0,遠平面爲7.0,視野45度
const qreal zNear = 3.0, zFar = 7.0, fov = 45.0;
// Reset projection
//--重置投影
projection.setToIdentity();
// Set perspective projection
//--設置透視投影
projection.perspective(fov, aspect, zNear, zFar);
}
- 重寫函數 paintGL(),畫出cube立方體,並且啓用四元數旋轉
void MainWidget::paintGL()
{
// Clear color and depth buffer
//--清除顏色和深度緩衝區
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//--將此紋理綁定到當前活動的紋理單元
texture->bind();
// Calculate model view transformation
//--計算 模型視圖轉換 MVP
QMatrix4x4 matrix;
matrix.translate(0.0, 0.0, -5.0);
//--按照給定的四元數旋轉
matrix.rotate(rotation);
// Set modelview-projection matrix
//--設置MVP矩陣
//--MVP矩陣按照四元數旋轉
program.setUniformValue("mvp_matrix", projection * matrix);
// Use texture unit 0 which contains cube.png
//--使用包含cube.png的紋理單元0
program.setUniformValue("texture", 0);
// Draw cube geometry
//--畫立方體幾何
geometries->drawCubeGeometry(&program);
}
- 初始化紋理
/*!
* \brief 初始化紋理
*/
void MainWidget::initTextures()
{
// Load cube.png image
//--加載紋理
texture = new QOpenGLTexture(QImage(":/cube.png").mirrored());
// Set nearest filtering mode for texture minification
//--設置 紋理縮小 爲 最近濾波模式
texture->setMinificationFilter(QOpenGLTexture::Nearest);
// Set bilinear filtering mode for texture magnification
//--設置 紋理放大 爲 雙線性濾波模式
texture->setMagnificationFilter(QOpenGLTexture::Linear);
// Wrap texture coordinates by repeating
// f.ex. texture coordinate (1.1, 1.2) is same as (0.1, 0.2)
//--重複封裝紋理座標
texture->setWrapMode(QOpenGLTexture::Repeat);
}
- 初始化着色器
/*!
* \brief 初始化着色器
*/
void MainWidget::initShaders()
{
// Compile vertex shader
//--源碼編譯頂點着色器
if (!program.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/vshader.glsl"))
close();
// Compile fragment shader
//--源碼編譯片段着色器
if (!program.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/fshader.glsl"))
close();
// Link shader pipeline
//--鏈接着色器管道
if (!program.link())
close();
// Bind shader pipeline for use
//--綁定着色器管道以供使用
if (!program.bind())
close();
}
立方體引擎GeometryEngine
GeometryEngine : protected QOpenGLFunctions
- 頂點結構體
/*!
* \brief 頂點數據
*/
struct VertexData
{
//--頂點位置
QVector3D position;
//--紋理座標
QVector2D texCoord;
};
- 初始化 立方體幾何 並將其傳輸到 緩衝區對象
void GeometryEngine::initCubeGeometry()
{
// For cube we would need only 8 vertices but we have to
// duplicate vertex for each face because texture coordinate
// is different.
//--對於正方體我們只需要8個頂點,但是我們必須爲每個面複製頂點,因爲紋理座標是不同的。
//--6個面對應24個頂點
VertexData vertices[] = {
// Vertex data for face 0
{QVector3D(-1.0f, -1.0f, 1.0f), QVector2D(0.0f, 0.0f)}, // v0
{QVector3D( 1.0f, -1.0f, 1.0f), QVector2D(0.33f, 0.0f)}, // v1
{QVector3D(-1.0f, 1.0f, 1.0f), QVector2D(0.0f, 0.5f)}, // v2
{QVector3D( 1.0f, 1.0f, 1.0f), QVector2D(0.33f, 0.5f)}, // v3
// Vertex data for face 1
{QVector3D( 1.0f, -1.0f, 1.0f), QVector2D( 0.0f, 0.5f)}, // v4
{QVector3D( 1.0f, -1.0f, -1.0f), QVector2D(0.33f, 0.5f)}, // v5
{QVector3D( 1.0f, 1.0f, 1.0f), QVector2D(0.0f, 1.0f)}, // v6
{QVector3D( 1.0f, 1.0f, -1.0f), QVector2D(0.33f, 1.0f)}, // v7
// Vertex data for face 2
{QVector3D( 1.0f, -1.0f, -1.0f), QVector2D(0.66f, 0.5f)}, // v8
{QVector3D(-1.0f, -1.0f, -1.0f), QVector2D(1.0f, 0.5f)}, // v9
{QVector3D( 1.0f, 1.0f, -1.0f), QVector2D(0.66f, 1.0f)}, // v10
{QVector3D(-1.0f, 1.0f, -1.0f), QVector2D(1.0f, 1.0f)}, // v11
// Vertex data for face 3
{QVector3D(-1.0f, -1.0f, -1.0f), QVector2D(0.66f, 0.0f)}, // v12
{QVector3D(-1.0f, -1.0f, 1.0f), QVector2D(1.0f, 0.0f)}, // v13
{QVector3D(-1.0f, 1.0f, -1.0f), QVector2D(0.66f, 0.5f)}, // v14
{QVector3D(-1.0f, 1.0f, 1.0f), QVector2D(1.0f, 0.5f)}, // v15
// Vertex data for face 4
{QVector3D(-1.0f, -1.0f, -1.0f), QVector2D(0.33f, 0.0f)}, // v16
{QVector3D( 1.0f, -1.0f, -1.0f), QVector2D(0.66f, 0.0f)}, // v17
{QVector3D(-1.0f, -1.0f, 1.0f), QVector2D(0.33f, 0.5f)}, // v18
{QVector3D( 1.0f, -1.0f, 1.0f), QVector2D(0.66f, 0.5f)}, // v19
// Vertex data for face 5
{QVector3D(-1.0f, 1.0f, 1.0f), QVector2D(0.33f, 0.5f)}, // v20
{QVector3D( 1.0f, 1.0f, 1.0f), QVector2D(0.66f, 0.5f)}, // v21
{QVector3D(-1.0f, 1.0f, -1.0f), QVector2D(0.33f, 1.0f)}, // v22
{QVector3D( 1.0f, 1.0f, -1.0f), QVector2D(0.66f, 1.0f)} // v23
};
// Indices for drawing cube faces using triangle strips.
// Triangle strips can be connected by duplicating indices
// between the strips. If connecting strips have opposite
// vertex order then last index of the first strip and first
// index of the second strip needs to be duplicated. If
// connecting strips have same vertex order then only last
// index of the first strip needs to be duplicated.
//--使用 三角形條方式 繪製正方體的索引。
//在條帶之間三角形方式可以通過複製索引來連接。
//如果連接條 相反頂點順序,然後是第一個條帶的最後一個索引和第二條帶的第一個索引,索引需要複製。
//如果連接條 有相同的頂點順序,然後只有最後需要複製第一個條帶的索引。
GLushort indices[] = {
0, 1, 2, 3, 3, // Face 0 - triangle strip ( v0, v1, v2, v3)
4, 4, 5, 6, 7, 7, // Face 1 - triangle strip ( v4, v5, v6, v7)
8, 8, 9, 10, 11, 11, // Face 2 - triangle strip ( v8, v9, v10, v11)
12, 12, 13, 14, 15, 15, // Face 3 - triangle strip (v12, v13, v14, v15)
16, 16, 17, 18, 19, 19, // Face 4 - triangle strip (v16, v17, v18, v19)
20, 20, 21, 22, 23 // Face 5 - triangle strip (v20, v21, v22, v23)
};
// Transfer vertex data to VBO 0
//--將頂點數據傳輸到VBO 0
arrayBuf.bind();
arrayBuf.allocate(vertices, 24 * sizeof(VertexData));
// Transfer index data to VBO 1
//--將索引數據傳輸到VBO 1
indexBuf.bind();
indexBuf.allocate(indices, 34 * sizeof(GLushort));
}
- 外部調用
OpenGL在緩衝區vbo中定位數據,提供着色器程序獲取數據值,然後繪製圖形
void GeometryEngine::drawCubeGeometry(QOpenGLShaderProgram *program)
{
// Tell OpenGL which VBOs to use
//--告訴OpenGL使用哪個vbo
arrayBuf.bind();
indexBuf.bind();
//-------------------------------
// 接着告訴OpenGL在緩衝區vbo中定位數據
//-------------------------------
// Offset for position
quintptr offset = 0;
// Tell OpenGL programmable pipeline how to locate vertex position data
//--告訴OpenGL可編程管道如何定位VBO中的頂點位置數據
//--設置 vshader.glsl 文件中的 a_position 爲VBO中的頂點值
int vertexLocation = program->attributeLocation("a_position");
program->enableAttributeArray(vertexLocation);
/*!
* @brief
* 從當前綁定的頂點緩衝區中的特定偏移量開始,
* 設置着色器程序的位置屬性爲頂點數組值。
* 步長表示頂點之間的字節數。
* 默認的步長值爲0表示值數組中的頂點被密集地填充。
*/
program->setAttributeBuffer(vertexLocation, GL_FLOAT, offset, 3, sizeof(VertexData));
// Offset for texture coordinate
offset += sizeof(QVector3D);
// Tell OpenGL programmable pipeline how to locate vertex texture coordinate data
//--告訴OpenGL可編程管道如何定位VBO中的紋理座標數據
//--設置 vshader.glsl 文件中的 a_texcoord 爲VBO中的紋理座標值
int texcoordLocation = program->attributeLocation("a_texcoord");
program->enableAttributeArray(texcoordLocation);
program->setAttributeBuffer(texcoordLocation, GL_FLOAT, offset, 2, sizeof(VertexData));
// Draw cube geometry using indices from VBO 1
//--使用VBO 1中的索引繪製立方體幾何
/*!
* @brief
* @param mode GL_TRIANGLE_STRIP
* @param count 34個indices(索引數量)
* @param type GL_UNSIGNED_SHORT
* @param indices 0
* @return
*/
glDrawElements(GL_TRIANGLE_STRIP, 34, GL_UNSIGNED_SHORT, 0);
}