qt+opengl 实现纹理贴图,平移旋转,绘制三角形,方形-CSDN博客
QT + opengl 让2d贴图动起来-CSDN博客
在前两个文章中我们已经了解了opengl的基本用法和GLSL的语句的基本意思,和纹理位置颜色的坐标位置关系。那么我们现在从2d图形来完成一个3d立方体。
1 首先我们理解几个名词 模型变化,视点变化,从三维物体到二维图象,就如同用相机拍照一样
1.1 、将相机置于三角架上,让它对准三维景物,它相当于OpenGL中调整视点的位置,即视点变换(Viewing Transformation)。
1.2 将三维物体放在场景中的适当位置,它相当于OpenGL中的模型变换(Modeling Transformation),即对模型进行旋转、平移和缩放。
1.3 选择相机镜头并调焦,使三维物体投影在二维胶片上,它相当于OpenGL中把三维模型投影到二维屏幕上的过程,即OpenGL的投影变换(Projection Transformation),OpenGL中投影的方法有两种,即正射投影和透视投影。为了使显示的物体能以合适的位置、大小和方向显示出来,必须要通过投影。有时为了突出图形的一部分,只把图形的某一部分显示出来,这时可以定义一个三维视景体(Viewing Volume)。正射投影时一般是一个长方体的视景体,透视投影时一般是一个棱台似的视景体。只有视景体内的物体能被投影在显示平面上,其他部分则不能。
1.4 冲洗底片,决定二维相片的大小,它相当与OpenGL中的视口变换(Viewport Transformation)(在屏幕窗口内可以定义一个矩形,称为视口(Viewport),视景体投影后的图形就在视口内显示)规定屏幕上显示场景的范围和尺寸。
1.5 投影变换 投影变换的目的就是定义一个视景体,使得视景体外多余的部分裁剪掉,最终进入图像的只是视景体内的有关部分。投影包括透视投影(Perspective Projection)和正视投影(Orthographic Projection)两种。
我们用大白话来说明下:
在用相机拍摄物体时,我们可以保持物体的位置不动,而将相机移离物体,这就相当于视点变换;另外,我们也可以保持相机的固定位置,将物体移离相机,这就相当于模型转换。这样,在OpenGL中,以逆时针旋转物体就相当于以顺时针旋转相机。因此,我们必须把视点转换和模型转换结合在一起考虑,而对这两种转换单独进行考虑是毫无意义的。
下面我们在前两篇文章的基础上再来做出一个立方体,上代码:
#include "glwidget.h"
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QMouseEvent>
static const char *vertexShaderSource =
"#version 330\n"
"layout (location = 0) in vec4 vertex;\n"
"layout (location = 1) in vec4 texCoord;\n"
"out vec4 texc;\n"
"uniform mat4 matrix;\n"
"void main(void)\n"
"{\n"
" gl_Position = matrix * vertex;\n"
" texc = texCoord;\n"
"}\n";
static const char *fragmentShaderSource =
"#version 330\n"
"uniform sampler2D texture;\n"
"in vec4 texc;\n"
"void main(void)\n"
"{\n"
" gl_FragColor = texture2D(texture, texc.st);\n"
"}\n";
GLWidget::GLWidget():QOpenGLWidget()
,m_xRos(0)
,m_yRos(0)
{
timer = new QTimer;
timer->setInterval(20);
connect(timer,&QTimer::timeout,this,[=]{
qDebug()<<"timeout";
// m_xRos+=30;
// m_yRos+=30;
rotateBy(2 * 16, +2 * 16, -1 * 16);
});
timer->start();
}
GLWidget::~GLWidget()
{
makeCurrent();
vbo.destroy();
for (int i = 0; i < 6; ++i)
delete textures[i];
delete program;
doneCurrent();
}
QSize GLWidget::minimumSizeHint() const
{
return QSize(400, 400);
}
QSize GLWidget::sizeHint() const
{
return QSize(400, 400);
}
void GLWidget::rotateBy(int xAngle, int yAngle, int zAngle)
{
xRot += xAngle;
yRot += yAngle;
zRot += zAngle;
update();
}
void GLWidget::setClearColor(const QColor &color)
{
clearColor = color;
update();
}
void GLWidget::initializeGL()
{
vertices = {
// ---- 位置---- - 纹理坐标 - ---- 颜色 ----
0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f,//1
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,//2
0.5f, -0.5f, 0.5f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,//3
-0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 0.0f,//4
0.5f, -0.5f, 0.5f, 1.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f,//5
-0.5f, -0.5f, 0.5f,1.0f, 1.0f,
0.5f, -0.5f, 0.5f,0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
};
initializeOpenGLFunctions();
// makeObject();
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
//#define PROGRAM_VERTEX_ATTRIBUTE 0
//#define PROGRAM_TEXCOORD_ATTRIBUTE 1
program = new QOpenGLShaderProgram;
program->addShaderFromSourceCode(QOpenGLShader::Vertex,vertexShaderSource);
program->addShaderFromSourceCode(QOpenGLShader::Fragment,fragmentShaderSource);
program->link();
program->bind();//激活Program对象
vbo.create();
vbo.bind(); //绑定到当前的OpenGL上下文,
vbo.allocate(vertices.constData(), vertices.count() * sizeof(GLfloat));
//初始化VAO,设置顶点数据状态(顶点,法线,纹理坐标等)
vao.create();
vao.bind();
for (int j = 0; j < 6; ++j)
{
textures[j] = new QOpenGLTexture(QImage(QString(":/%1.png").arg(j + 1)).mirrored());
textures[j]->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear,QOpenGLTexture::Linear);
textures[j]->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::ClampToEdge);
textures[j]->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::ClampToEdge);
}
program->setAttributeBuffer(0, GL_FLOAT, 0, 3, 5 * sizeof(float)); //设置aPos顶点属性
program->setAttributeBuffer(1, GL_FLOAT, 3 * sizeof(float), 2, 5 * sizeof(float)); //设置aColor顶点颜色
program->enableAttributeArray(0); //使能aPos顶点属性
program->enableAttributeArray(1); //使能aColor顶点颜色
program->setUniformValue("texture", 0);
// vao.release();
// vbo.release();
}
void GLWidget::paintGL()
{
//glClearColor(clearColor.redF(), clearColor.greenF(), clearColor.blueF(), clearColor.alphaF());
glClearColor(0.1f,0.5f,0.7f,1.0f); //设置清屏颜色
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
QMatrix4x4 m;
//m.ortho(-0.5f, +0.5f, +0.5f, -0.5f, 4.0f, 15.0f);
m.translate(0.0f, 0.0f, 0.0f);
m.rotate(xRot / 16.0f, 1.0f, 0.0f, 0.0f);
m.rotate(yRot / 16.0f, 0.0f, 1.0f, 0.0f);
m.rotate(zRot / 16.0f, 0.0f, 0.0f, 1.0f);
m.scale(0.5);
program->setUniformValue("matrix", m);
for (int i = 0; i < 6; ++i) {
textures[i]->bind();
glDrawArrays(GL_TRIANGLE_FAN, i * 4, 4);
}
}
void GLWidget::resizeGL(int width, int height)
{
this->glViewport(0,0,width,height); //定义视口区域
}
void GLWidget::mousePressEvent(QMouseEvent *event)
{
lastPos = event->pos();
}
void GLWidget::mouseMoveEvent(QMouseEvent *event)
{
int dx = event->x() - lastPos.x();
int dy = event->y() - lastPos.y();
if (event->buttons() & Qt::LeftButton) {
rotateBy(8 * dy, 8 * dx, 0);
} else if (event->buttons() & Qt::RightButton) {
rotateBy(8 * dy, 0, 8 * dx);
}
lastPos = event->pos();
}
void GLWidget::mouseReleaseEvent(QMouseEvent * /* event */)
{
emit clicked();
}
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLBuffer>
#include <QOpenGLVertexArrayObject>
#include <QTimer>
QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram);
QT_FORWARD_DECLARE_CLASS(QOpenGLTexture)
class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
//using QOpenGLWidget::QOpenGLWidget;
GLWidget();
~GLWidget();
QSize minimumSizeHint() const override;
QSize sizeHint() const override;
void rotateBy(int xAngle, int yAngle, int zAngle);
void setClearColor(const QColor &color);
signals:
void clicked();
protected:
void initializeGL() override;
void paintGL() override;
void resizeGL(int width, int height) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
private:
QColor clearColor = Qt::black;
QPoint lastPos;
int xRot = 0;
int yRot = 0;
int zRot = 0;
QOpenGLShaderProgram *program = nullptr;
QOpenGLBuffer vbo;
QVector<float> vertices;
QOpenGLVertexArrayObject vao;
QTimer* timer;
int m_xRos = 0;
int m_yRos = 0;
};
#endif
运行看看看,是不是就出来一个会动的立方体呢?
和前两篇文章的区别在于,我们贴了6张纹理图,设置了旋转角度。在测试过程里面,有几次我贴图完后,运行出来显示出来少了两个贴图,最后检查是顶点坐标错误造成的。
完整代码在
https://download.csdn.net/download/foxgod/89899780
1 widget w ;w.show 运行出来是一个三角形
2 GLWidget w ;w.show 运行出来是一个立方体
有疑问可以私信
版权归原作者 foxgod 所有, 如有侵权,请联系我们删除。