GLSL矩阵变换详解(二、旋转变换、平移变换以及QMatrix4x4)

xiaoxiao2021-02-28  18

本篇博客展示4个例子,都是关于旋转变换、平移变换和QMatrix的操作。

在QT的帮助中,提到QMatrix是行主序。

而opengl的矩阵是列主序的。乍一看,将QMatrix传入shader之前,qmatrix需要做转置处理。但是在实际的尝试中,我发现矩阵不应转置。

我们从前面的博客 

Qt结合GLSL贴出纹理(二、采用QOpenGLShaderProgram和QOpenGLTexture)

出发,在那篇博客里,我们贴出来一个纹理,并没有做其他任何操作。现在我们在其基础上添加旋转操作。

例子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_H

cpp文件:

#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得到最终的结果。前一篇博客 

GLSL矩阵变换详解(一、总述)

中,我们提到,一串矩阵相乘,最先起作用的其实是最右面的矩阵。因此,尽管translate先调用,但是反而后起作用。

例子4. 

为了验证例子3的分析,我们用两个独立的矩阵各自代表旋转和平移操作,然后把两者相乘:

可见,结果与例子3一样。这也就验证了例子三的猜测:translate函数与rotate函数的调用次序与实际的操作次序相反。

转载请注明原文地址: https://www.6miu.com/read-2800101.html

最新回复(0)