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
按照您想要的效果的顺序将这些矩阵相乘,但请记住顺序很重要。
我已经实现了一个在 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
按照您想要的效果的顺序将这些矩阵相乘,但请记住顺序很重要。