本篇博客展示4个例子,都是关于旋转变换、平移变换和QMatrix的操作。
在QT的帮助中,提到QMatrix是行主序。
而opengl的矩阵是列主序的。乍一看,将QMatrix传入shader之前,qmatrix需要做转置处理。但是在实际的尝试中,我发现矩阵不应转置。
我们从前面的博客
出发,在那篇博客里,我们贴出来一个纹理,并没有做其他任何操作。现在我们在其基础上添加旋转操作。
例子1.
先看最简单的情形:让物体旋转45度
h文件:
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QOpenGLWidget> #include <QOpenGLFunctions> #include <QOpenGLTexture> #include <QOpenGLShader> #include <QOpenGLShaderProgram> class MainWindow : public QOpenGLWidget, protected QOpenGLFunctions { Q_OBJECT public: MainWindow(QWidget *parent = 0); ~MainWindow(); GLuint m_uiVertLoc; GLuint m_uiTexNum; QOpenGLTexture * m_pTextures; QOpenGLShaderProgram * m_pProgram; GLfloat * m_pVertices; protected: void initializeGL(); void paintGL(); void resizeGL(int w, int h); }; #endif // MAINWINDOW_Hcpp文件:
#include "mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QOpenGLWidget(parent) { } MainWindow::~MainWindow() { m_pTextures->release(); delete m_pTextures; delete m_pProgram; delete [] m_pVertices; } void MainWindow::initializeGL() { initializeOpenGLFunctions(); m_uiTexNum = 0; m_pVertices = new GLfloat[18]; //给顶点赋值 GLfloat arrVertices[18] = {0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0}; m_pVertices = new GLfloat[18]; memcpy(m_pVertices, arrVertices, 18 * sizeof(GLfloat)); QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this); const char *vsrc = "#version 330\n" "in vec3 pos;\n" "out vec2 texCoord;\n" "uniform mat4 mat4MVP;\n" "void main()\n" "{\n" " gl_Position = mat4MVP * vec4(pos, 1.0);\n" " texCoord = pos.xy;\n" "}\n"; vshader->compileSourceCode(vsrc); QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this); const char *fsrc = "#version 330\n" "out vec4 color;\n" "in vec2 texCoord;\n" "uniform sampler2D Tex\n;" "void main()\n" "{\n" " color = texture(Tex, texCoord);\n" //" color = vec4(1.0, 0.0, 0.0, 0.0);\n" "}\n"; fshader->compileSourceCode(fsrc); m_pProgram = new QOpenGLShaderProgram; m_pProgram->addShader(vshader); m_pProgram->addShader(fshader); m_pProgram->link(); m_pProgram->bind(); m_uiVertLoc = m_pProgram->attributeLocation("pos"); m_pProgram->enableAttributeArray(m_uiVertLoc); m_pProgram->setAttributeArray(m_uiVertLoc, m_pVertices, 3, 0); m_pTextures = new QOpenGLTexture(QImage(QString("earth.bmp")).mirrored()); m_pTextures->setMinificationFilter(QOpenGLTexture::Nearest); m_pTextures->setMagnificationFilter(QOpenGLTexture::Linear); m_pTextures->setWrapMode(QOpenGLTexture::Repeat); m_pProgram->setUniformValue("Tex", m_uiTexNum); glEnable(GL_DEPTH_TEST); glClearColor(0,0,0,1); } void MainWindow::paintGL() { //QMatrix4x4在声明时被默认为单位矩阵 QMatrix4x4 m1, m2, m3, m; //m1.viewport(0,0,m_iWidth, m_iHeight,-15,15);//useless //m1.ortho(-1.0f, +1.0f, -1.0f, 1.0f, 0.0f, 25.0f);//right//generate projection matrix //m2.lookAt(QVector3D(20,0,10), QVector3D(0,0,0), QVector3D(0,1,0));//generate view matrix, right //qDebug()<<m2; //m3.translate(0,-0.707,0.0);//right, generate model matrices m3.rotate(45, 0.0f, 0.0f, 1.0f);//right, generate model matrices qDebug()<<m3; m = m1 * m2 * m3; m_pProgram->setUniformValue("mat4MVP", m); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); m_pTextures->bind(m_uiTexNum); glDrawArrays(GL_TRIANGLES, 0, 6); m_pTextures->release(); } void MainWindow::resizeGL(int w, int h) { glViewport(0,0,w,h); }效果:
可见,图片的确绕Z轴旋转了45度。同时注意qDebug()打印出的矩阵。
按照https://en.wikibooks.org/wiki/GLSL_Programming/Vertex_Transformations的说法:
将alpha = 45 ,x = 0, y = 0, z = 1代入公式,不难发现,qDebug打印的结果与公式相符。
这说明我们在例子里对m3.rotate()函数的运用是正确的。
例子2. 旋转与平移结合
这个程序与例子1只变化了一处:增加了m3.translate(0,-0.5,0)
这里有两处要注意:
1)尽管先调用rotate函数,再调用了translate函数,但是纹理表现出来的效果是先平移,再旋转。因为纹理左下角不再位于窗口的中轴线x=0上了。
2)假如先进行旋转,再平移,则qDebug打印的结果应该是
0.707 -0.707 0 0
0.707 0.707 0 -0.5
0 0 1 0
0 0 0 1
与实际结果不符。
例子3. 将translate和rotate函数交换
此时显示效果与qDebug输出结果都正确了。
分析:QMatrix声明时,m3被默认赋值为单位矩阵。调用translate函数后,translate矩阵右乘单位矩阵m3.随后调用rotate函数,旋转矩阵再一次右乘m3得到最终的结果。前一篇博客
中,我们提到,一串矩阵相乘,最先起作用的其实是最右面的矩阵。因此,尽管translate先调用,但是反而后起作用。
例子4.
为了验证例子3的分析,我们用两个独立的矩阵各自代表旋转和平移操作,然后把两者相乘:
可见,结果与例子3一样。这也就验证了例子三的猜测:translate函数与rotate函数的调用次序与实际的操作次序相反。