OpenGL - Qt:旋转和缩放翻译不稳定

OpenGL - Qt : translate instable with rotation and scaling

我已经实现了一个在 openGL 中平移、旋转和扫描 2D 纹理的程序。 这 3 个转换本身工作得很好,但是当我将翻译与其他转换一起使用时,事情就变得古怪了。

对于缩放,当比例高于 1.0 时一切似乎都很好,但是一旦我低于,纹理就会脱离屏幕。 旋转的同样悲伤的故事。角度小的时候没有明显问题,但是超过20°左右,两边就很奇怪了。

这是我的代码。有趣的功能在最后。

#include "glwidget.h"

#include <GL/glu.h>
#include <QMouseEvent>
#include <QKeyEvent>
#include <tgmath.h>
#include <QtDebug>
#ifdef WIN32
    #include <GL/glext.h>
       PFNGLACTIVETEXTUREPROC pGlActiveTexture = NULL;
    #define glActiveTexture pGlActiveTexture
#endif //WIN32

GlWidget::GlWidget(QWidget *parent) :
    QGLWidget(QGLFormat(), parent),
    scale(0.5),
    angle(0),
    translate{0,0,0},
    keyLock(false)
{
}

GlWidget::~GlWidget(){}

QSize GlWidget::sizeHint() const
{
    return QSize(640,480);
}

void GlWidget::resizeGL(int w, int h){

    h = (h<=0?1:h);

    pMatrix.setToIdentity();
    pMatrix.perspective(60.0,(float)w /(float)h,2,5);

    glViewport(0,0,w,h);
    glScissor(w*0.8,0,w,h);
    glDisable(GL_SCISSOR_TEST);
}

void GlWidget::initializeGL(){

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    #ifdef WIN32
        glActiveTexture = (PFNGLACTIVETEXTUREPROC) wglGetProcAddress((LPCSTR) "glActiveTexture");
    #endif

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);

    qglClearColor(QColor(Qt::black));


    shaderProgram.addShaderFromSourceFile(QGLShader::Vertex, ":/vertexShader.vsh");
    shaderProgram.addShaderFromSourceFile(QGLShader::Fragment, ":/fragmentShader.fsh");
    shaderProgram.link();

    vertices << QVector3D(-1,-1,-2) << QVector3D(1,-1,-2) << QVector3D(-1,1,-2)
             << QVector3D(-1,1,-2) << QVector3D(1,-1,-2) << QVector3D(1,1,-2);

    textureCoordinates << QVector2D(0, 0) << QVector2D(1, 0) << QVector2D(0, 1)
                       << QVector2D(0, 1) << QVector2D(1, 0) << QVector2D(1, 1);

    texture = bindTexture(QPixmap(":/icone.png"));
    //mMatrix.setToIdentity();
    mMatrix.scale(scale,scale);


}

void GlWidget::paintGL(){

    qglClearColor(QColor(Qt::white));
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    QMatrix4x4 vMatrix;
    shaderProgram.bind();
    shaderProgram.setUniformValue("mvpMatrix", pMatrix*vMatrix*mMatrix);
    shaderProgram.setUniformValue("texture", 0);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);
    glActiveTexture(0);

    shaderProgram.setAttributeArray("vertex", vertices.constData());
    shaderProgram.enableAttributeArray("vertex");
    shaderProgram.setAttributeArray("textureCoordinate", textureCoordinates.constData());
    shaderProgram.enableAttributeArray("textureCoordinate");

    glEnable(GL_BLEND);

    glDrawArrays(GL_TRIANGLES,0,vertices.size());

    glDisable(GL_BLEND);

    shaderProgram.disableAttributeArray("vertex");
    shaderProgram.disableAttributeArray("textureCoordinate");
    shaderProgram.release();

    qglClearColor(QColor(Qt::black));
    glEnable(GL_SCISSOR_TEST);
    glClear(GL_COLOR_BUFFER_BIT);
    glDisable(GL_SCISSOR_TEST);

}

void GlWidget::mousePressEvent(QMouseEvent *event){
    if(event->button()==Qt::LeftButton){
        lastPosF = QVector3D(event->pos().x(),event->pos().y(),0);
        lastPos = getModelCoordinates(event->pos());
        //keyLock=true;
    }
    event->accept();
    //update();
}

void GlWidget::mouseMoveEvent(QMouseEvent *event){
    if(!event->buttons().testFlag(Qt::LeftButton)) return;
    currentPosF = QVector3D(event->pos().x(),event->pos().y(),0);
    QVector3D currentPos(getModelCoordinates(event->pos()));

    //qDebug()<<currentPos;

    if(keysPressed.contains(Qt::Key_Control) && !keysPressed.contains(Qt::Key_Shift)){
        resizeTexture(currentPos);
    }
    else if(!keysPressed.contains(Qt::Key_Control) && keysPressed.contains(Qt::Key_Shift)){
        rotateTexture(currentPos);
    }
    else if(!keysPressed.contains(Qt::Key_Control) && !keysPressed.contains(Qt::Key_Shift)){
        dragTexture(currentPos);
    }

    mMatrix.setToIdentity();
    mMatrix.translate(translate);
    mMatrix.scale(scale,scale);
    mMatrix.rotate(angle,0,0,1);

    //lastPos = currentPos;
    update();

}

void GlWidget::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button()==Qt::LeftButton)
        keyLock=false;

}

void GlWidget::keyPressEvent(QKeyEvent *event){
    if(!keyLock) keysPressed.append(event->key());

}

void GlWidget::keyReleaseEvent(QKeyEvent *event){
    if(!keyLock) keysPressed.remove(keysPressed.lastIndexOf(event->key()));
}

QVector3D GlWidget::getModelCoordinates(QPoint mousePos){
    QVector3D modelPos;

    double X,Y,Z;
    GLdouble modelMat[16],projMat[16];
    GLint viewport[4]={0,0,this->width(),this->height()};
    //glGetDoublev(GL_MODELVIEW_MATRIX ,modelMat);
    //glGetDoublev(GL_PROJECTION_MATRIX,projMat);
    //glGetIntegerv(GL_VIEWPORT,viewport);
    for(int i=0; i<4;i++){
        modelMat[i*4]=(double)mMatrix.column(i).x();
        modelMat[1+i*4]=(double)mMatrix.column(i).y();
        modelMat[2+i*4]=(double)mMatrix.column(i).z();
        modelMat[3+i*4]=(double)mMatrix.column(i).w();
    }
    for(int i=0; i<4;i++){
        projMat[i*4]=(double)pMatrix.column(i).x();
        projMat[1+i*4]=(double)pMatrix.column(i).y();
        projMat[2+i*4]=(double)pMatrix.column(i).z();
        projMat[3+i*4]=(double)pMatrix.column(i).w();
    }
    //GLdouble z;

    gluUnProject((double)mousePos.x(),(double)viewport[3]-(double)mousePos.y(),0,modelMat,projMat,viewport,&X,&Y,&Z);
    modelPos.setX(fabs(X)<1e-8?1e-8:static_cast<float>(X));
    modelPos.setY(static_cast<float>(Y));
    modelPos.setZ(static_cast<float>(Z));

    qDebug();
    qDebug()<<"first :"<<lastPos<<" - Now :"<<modelPos;


    return modelPos;
}

void GlWidget::resizeTexture(QVector3D currentPos)
{
    float diff = sqrt(pow(currentPos.x(),2)+pow(currentPos.y(),2)) / sqrt(pow(lastPos.x(),2)+pow(lastPos.y(),2));
    //diff=diff>1?1:diff;
    //diff /= sqrt(pow(lastPos.x(),2)+pow(lastPos.y(),2));
    scale*= diff;
    scale=scale<0?-scale:scale;
    qDebug()<<"diff : "<<diff<<"  - scale : "<<scale;
    /*mMatrix.setToIdentity();
    mMatrix.scale(scale,scale);
    mMatrix.rotate(angle,0,0,1);*/
}

void GlWidget::rotateTexture(QVector3D currentPos)
{
    float angle1 = atan((float)currentPos.y()/(float)currentPos.x());
    float angle2 = atan((float)lastPos.y()/(float)lastPos.x());
    float delta =angle1-angle2*(angle1*angle2>=0?1:-1);
    delta*=180/M_PI;
    angle+=delta;
    while(angle>360|| angle < 0){
        angle = (angle>360?angle-360:angle);
        angle = (angle<0?angle+360:angle);
    }
    qDebug()<<"angle 1 : "<<atan((float)lastPos.y()/(float)lastPos.x())
           << "angle 2 : "<<atan((float)currentPos.y()/(float)currentPos.x());
    qDebug()<<"delta : "<<delta<<" - Angle : "<<angle;
    //qDebug()<<angle;
    /*mMatrix.setToIdentity();
    mMatrix.scale(scale,scale);
    mMatrix.rotate(angle,0,0,1);*/
    //lastPos = currentPos;
}

void GlWidget::dragTexture(QVector3D currentPos)
{
    if(lastPos.x() > 1.0 || lastPos.x() < -1.0 || lastPos.y() > 1.0 || lastPos.y() < -1.0)
        return;
    translate += (currentPos-lastPos);
    /*mMatrix.setToIdentity();
    mMatrix.translate(translate);*/
    //translate += currentPosF-lastPosF;
    lastPosF=currentPosF;
    qDebug()<<translate;
}

这是我的顶点和片段着色器:

#version 330

uniform mat4 mvpMatrix;

in vec4 vertex;
in vec2 textureCoordinate;

out vec2 varyingTextureCoordinate;

void main(void)
{
    varyingTextureCoordinate = textureCoordinate;
    gl_Position = mvpMatrix * vertex;
}


#version 330

uniform sampler2D texture;

in vec2 varyingTextureCoordinate;

out vec4 fragColor;

void main(void)
{
    fragColor = texture2D(texture, varyingTextureCoordinate);
}

感谢您的回答。

对于绕 z 轴 15 度的旋转,你的旋转矩阵应该是:

0.965926, -0.258819, 0.0, 0.0
0.258819, 0.965926, 0.0, 0.0
0.0, 0.0, 1.0, 0.0
0.0, 0.0, 0.0, 1.0

这些矩阵都是4×4矩阵。 翻译矩阵看起来像:

1.0, 0.0, 0.0, transX
0.0, 1.0, 0.0, 转Y
0.0, 0.0, 1.0, 转Z
0.0、0.0、0.0、1.0

比例矩阵如下所示:

缩放X, 0.0, 0.0, 0.0
0.0, 比例 Y, 0.0, 0.0
0.0, 0.0, 缩放Z, 0.0
0.0, 0.0, 0.0, 1.0

按照您想要的效果的顺序将这些矩阵相乘,但请记住顺序很重要。