纹理贴图是 opengl 极为重要的一个知识,相关知识点在此暂时不做介绍,这里只给出一个可运行的示例,
我对demo进行了一定的改动,其中在构造函数定义了一个PROGRAM_2D的宏,定义的时候为一个2个面的纹理贴图,注释掉该宏则会实现一个3d的贴图。同时给我们的例子添加键盘和鼠标动作,详细可以自行运行体验。
相比于前面几节,本节我们添加resources资源项,并添加图片。
具体代码如下:
//myopenglwidget.h
#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H
#include
#include
#include
#include
#include
class QOpenGLShaderProgram;
class QOpenGLTexture;
class MyOpenGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
explicit MyOpenGLWidget(QWidget *parent = 0);
~MyOpenGLWidget();
QSize minimumSizeHint() const override;
void keyPressEvent(QKeyEvent *event) 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:
void makeObject();
void make2DObject();
QColor clearColor;
QPoint lastPos;
GLfloat translate, xRot, yRot, zRot;
void printContextInformation();
QOpenGLShaderProgram *program;
QOpenGLBuffer vbo;
QOpenGLTexture *textures[6];
};
#endif // MYOPENGLWIDGET_H
//myopenglwidget.cpp
#include "myopenglwidget.h"
#include
#include
#include
MyOpenGLWidget::MyOpenGLWidget(QWidget *parent)
: QOpenGLWidget(parent),
translate(-3),
xRot(0),
yRot(0),
zRot(0)
{
#define PROGRAM_2D 1; //可将该行注释掉观察程序的变化
memset(textures, 0, sizeof(textures));
}
MyOpenGLWidget::~MyOpenGLWidget()
{
makeCurrent();
vbo.destroy();
for (int i = 0; i < 6; ++i)
delete textures[i];
delete program;
doneCurrent();
}
QSize MyOpenGLWidget::minimumSizeHint() const
{
return QSize(200, 200);
}
void MyOpenGLWidget::keyPressEvent(QKeyEvent *event)
{
switch (event->key()) {
case Qt::Key_Up:
xRot += 10;
break;
case Qt::Key_Left:
yRot += 10;
break;
case Qt::Key_Right:
zRot += 10;
break;
case Qt::Key_Down:
translate -= 1;
break;
case Qt::Key_Space:
translate += 1;
break;
default:
break;
}
update();
QOpenGLWidget::keyPressEvent(event);
}
QSize MyOpenGLWidget::sizeHint() const
{
return QSize(500, 500);
}
void MyOpenGLWidget::rotateBy(int xAngle, int yAngle, int zAngle)
{
xRot += xAngle;
yRot += yAngle;
zRot += zAngle;
update();
}
void MyOpenGLWidget::setClearColor(const QColor &color)
{
clearColor = color;
update();
}
void MyOpenGLWidget::initializeGL()
{
// 为当前环境初始化OpenGL函数
initializeOpenGLFunctions();
printContextInformation();
#ifdef PROGRAM_2D
for (int i = 0; i < 2; ++i)
textures[i] = new QOpenGLTexture(QImage(QString("../myopengl/side%1.png")
.arg(i + 1)).mirrored());
#else
makeObject();
#endif
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
#define PROGRAM_VERTEX_ATTRIBUTE 0
#define PROGRAM_TEXCOORD_ATTRIBUTE 1
// 创建顶点着色器
QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this);
const char *vsrc =
"attribute highp vec4 vPosition; \n"
"attribute mediump vec4 vTexCoord; \n"
"varying mediump vec4 texCoord; \n"
"uniform mediump mat4 matrix; \n"
"void main() { \n"
" texCoord = vTexCoord; \n"
" gl_Position = matrix * vPosition; \n"
"} \n";
vshader->compileSourceCode(vsrc);
// 创建片段着色器
QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this);
const char *fsrc =
"uniform sampler2D tex; \n"
"varying mediump vec4 texCoord; \n"
"void main() { \n"
" gl_FragColor = texture2D(tex, texCoord.st); \n"
"} \n";
fshader->compileSourceCode(fsrc);
// vshader->compileSourceFile(":/simple.vert");
// fshader->compileSourceFile(":/simple.frag");
// 创建着色器程序
program = new QOpenGLShaderProgram;
// program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/vert.glsl");
// program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/frag.glsl");
program->addShader(vshader);
program->addShader(fshader);
program->bindAttributeLocation("vPosition", PROGRAM_VERTEX_ATTRIBUTE);
program->bindAttributeLocation("vTexCoord", PROGRAM_TEXCOORD_ATTRIBUTE);
program->link();
program->bind();
program->setUniformValue("texture", 0);
}
void MyOpenGLWidget::resizeGL(int width , int height)
{
int side = qMin(width, height);
glViewport((width - side) / 2, (height - side) / 2, side, side);
}
void MyOpenGLWidget::paintGL()
{
int w = width();
int h = height();
int side = qMin(w, h);
glViewport((w - side) / 2, (h - side) / 2, side, side);
glClearColor(clearColor.redF(), clearColor.greenF(),
clearColor.blueF(), clearColor.alphaF());
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
#ifdef PROGRAM_2D
// 顶点位置
GLfloat vertices[2][4][3] = {
{ {-0.8f, 0.8f, 0.8f}, {-0.8f, -0.8f, 0.8f}, {0.8f, -0.8f, 0.8f}, {0.8f, 0.8f, 0.8f} },
{ {0.8f, 0.8f, 0.8f}, {0.8f, -0.8f, 0.8f}, {0.8f, -0.8f, -0.8f}, {0.8f, 0.8f, -0.8f} }
};
GLfloat vertices1[2][4][5] = {
{ {-0.8f, 0.8f, 0.8f,0.0f, 1.0f}, {-0.8f, -0.8f, 0.8f,0.0f, 0.0f},
{0.8f, -0.8f, 0.8f, 1.0f, 0.0f}, {0.8f, 0.8f, 0.8f, 1.0f, 1.0f} },
{ {0.8f, 0.8f, 0.8f, 0.0f, 1.0f}, {0.8f, -0.8f, 0.8f, 0.0f, 0.0f},
{0.8f, -0.8f, -0.8f, 1.0f, 0.0f}, {0.8f, 0.8f, -0.8f, 1.0f, 1.0f} }
};
vbo.create();
vbo.bind();
vbo.allocate(vertices1, 40*sizeof(GLfloat));
GLuint attr = -1;
attr = program->attributeLocation("vPosition");
// program->setAttributeBuffer(vPosition, GL_FLOAT, 0, 3, 0);
program->setAttributeBuffer(attr, GL_FLOAT, 0, 3, sizeof(GLfloat) * 5);
glEnableVertexAttribArray(attr);
attr = program->attributeLocation("vTexCoord");
program->setAttributeBuffer(attr, GL_FLOAT, 3*sizeof(GLfloat), 2, sizeof(GLfloat) * 5);
glEnableVertexAttribArray(attr);
// GLuint vPosition = program->attributeLocation("vPosition");
// program->setAttributeBuffer(vPosition, GL_FLOAT, 0, 3, 0);
// glEnableVertexAttribArray(vPosition);
// 纹理坐标
GLfloat coords[2][4][2] = {
{ {0.0f, 1.0f}, {0.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f} },
{ {0.0f, 1.0f}, {0.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f} }
};
// vbo.write(24*sizeof(GLfloat), coords, 16*sizeof(GLfloat));
// GLuint vTexCoord = program->attributeLocation("vTexCoord");
// program->setAttributeBuffer(vTexCoord, GL_FLOAT, 24*sizeof(GLfloat), 2, 0);
// glEnableVertexAttribArray(vTexCoord);
program->setUniformValue("tex", 0);
#endif
QMatrix4x4 matrix;
matrix.perspective(45.0f, (GLfloat)w/(GLfloat)h, 0.1f, 100.0f);
matrix.translate(0, 0, translate);
matrix.rotate(-60, 0, 1, 0); //绕Y轴逆时针旋转
matrix.rotate(xRot / 16.0f, 1.0f, 0.0f, 0.0f);
matrix.rotate(yRot / 16.0f, 0.0f, 1.0f, 0.0f);
matrix.rotate(zRot / 16.0f, 0.0f, 0.0f, 1.0f);
program->setUniformValue("matrix", matrix);
#ifdef PROGRAM_2D
#else
program->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE);
program->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE);
program->setAttributeBuffer(PROGRAM_VERTEX_ATTRIBUTE, GL_FLOAT, 0, 3, 5 * sizeof(GLfloat));
program->setAttributeBuffer(PROGRAM_TEXCOORD_ATTRIBUTE, GL_FLOAT, 3 * sizeof(GLfloat), 2, 5 * sizeof(GLfloat));
#endif
// 绘制
#ifdef PROGRAM_2D
for(int i=0; i<2; i++)
#else
for(int i=0; i<6; i++)
#endif
{
textures[i]->bind();
glDrawArrays(GL_TRIANGLE_FAN, i*4, 4);
}
}
void MyOpenGLWidget::mousePressEvent(QMouseEvent *event)
{
lastPos = event->pos();
}
void MyOpenGLWidget::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 MyOpenGLWidget::mouseReleaseEvent(QMouseEvent *event)
{
emit clicked();
}
void MyOpenGLWidget::makeObject()
{
static const int coords[6][4][3] = {
{ { +1, -1, -1 }, { -1, -1, -1 }, { -1, +1, -1 }, { +1, +1, -1 } },
{ { +1, +1, -1 }, { -1, +1, -1 }, { -1, +1, +1 }, { +1, +1, +1 } },
{ { +1, -1, +1 }, { +1, -1, -1 }, { +1, +1, -1 }, { +1, +1, +1 } },
{ { -1, -1, -1 }, { -1, -1, +1 }, { -1, +1, +1 }, { -1, +1, -1 } },
{ { +1, -1, +1 }, { -1, -1, +1 }, { -1, -1, -1 }, { +1, -1, -1 } },
{ { -1, -1, +1 }, { +1, -1, +1 }, { +1, +1, +1 }, { -1, +1, +1 } }
};
for (int j = 0; j < 6; ++j)
textures[j] = new QOpenGLTexture(QImage(QString(":/side%1.png").arg(j + 1)).mirrored());
QVector vertData;
for (int i = 0; i < 6; ++i) {
for (int j = 0; j < 4; ++j) {
// vertex position
vertData.append(0.5 * coords[i][j][0]);
vertData.append(0.5 * coords[i][j][1]);
vertData.append(0.5 * coords[i][j][2]);
// texture coordinate
vertData.append(j == 0 || j == 3);
vertData.append(j == 0 || j == 1);
}
}
vbo.create();
vbo.bind();
vbo.allocate(vertData.constData(), vertData.count() * sizeof(GLfloat));
}
void MyOpenGLWidget::printContextInformation()
{
QString glType;
QString glVersion;
QString glProfile;
// Get Version Information
glType = (context()->isOpenGLES()) ? "OpenGL ES" : "OpenGL";
glVersion = reinterpret_cast(glGetString(GL_VERSION));
// Get Profile Information
#define CASE(c) case QSurfaceFormat::c: glProfile = #c; break
switch (format().profile())
{
CASE(NoProfile);
CASE(CoreProfile);
CASE(CompatibilityProfile);
}
#undef CASE
// qPrintable() will print our QString w/o quotes around it.
qDebug() << qPrintable(glType) << qPrintable(glVersion) << "(" << qPrintable(glProfile) << ")";
}
#include
#include "myopenglwidget.h"
int main(int argc, char *argv[])
{
Q_INIT_RESOURCE(textures);
QApplication app(argc,argv);
QSurfaceFormat format;
format.setDepthBufferSize(24);
QSurfaceFormat::setDefaultFormat(format);
MyOpenGLWidget w;
w.rotateBy(+42 * 16, +42 * 16, -21 * 16);
w.setFormat(format);
w.resize(400, 300);
w.show();
return app.exec();
}
定义宏时的运行结果:
注释掉宏以后的运行效果:一个立方体