QOpenGL入门教程09:纹理贴图

系列教程索引:QOpenGL入门教程索引

上一篇:QOpenGL入门教程08:立方体

之前的文章介绍了基本的显示方法,但是显示的是颜色,开发的时候一般显示的是风景之类的图片,所以本文介绍OpenGL相关的图片加载操作。

在OpenGLut系列中,图片数据加载用的是stb_image库,但是Qt中提供了自带的QImage用于操作图片。

显示图片

先看一下如何显示一张图片。

渲染语言

顶点

1
2
3
4
5
6
7
8
9
#version 330 core

in vec2 position;
in vec2 texCoord;
out vec4 texc;
void main(){
gl_Position = vec4(position,0.0,1.0);
texc = vec4(texCoord,0.0,1.0);
}

1
2
3
4
5
6
7
8
#version 330 core

uniform sampler2D texture;
in vec4 texc;
out vec4 fragColor;
void main(){
gl_FragColor = texture2D(texture,texc.st);
}

图片数据加载

1
img = QImage(":/resources/bird.jpg");

初始化

获取并绑定顶点位置

1
2
3
4
5
6
7
8
9
10
program = new QOpenGLShaderProgram;
program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex,":/resources/vsrc.glsl");
program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment,":/resources/fsrc.glsl");
program->bindAttributeLocation("position",0);
program->bindAttributeLocation("texCoord",1);
program->link();//激活Program对象
program->bind();

vao.create();
vao.bind();

设置顶点数据,并创建纹理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
float vertex[]={
-1.0, 1.0, 0.0, 0.0,
1.0, 1.0, 1.0, 0.0,
1.0, -1.0, 1.0, 1.0,
-1.0, -1.0, 0.0, 1.0
};

vbo.create();
vbo.bind();
vbo.allocate(vertex,sizeof(vertex)*sizeof(float));

program->enableAttributeArray(0);
program->enableAttributeArray(1);
program->setAttributeBuffer(0, GL_FLOAT, 0, 2, 4*sizeof(GLfloat));
program->setAttributeBuffer(1, GL_FLOAT, 2*sizeof(GLfloat), 2, 4*sizeof(GLfloat));

texture = new QOpenGLTexture(img);
texture->setMinificationFilter(QOpenGLTexture::Nearest);
texture->setMagnificationFilter(QOpenGLTexture::Linear);
texture->setWrapMode(QOpenGLTexture::Repeat);

vao.release();
vbo.release();
program->release();

绘图部分

1
2
3
4
5
6
7
8
if(img.isNull()) return;

program->bind();
vao.bind();
if(texture->isCreated()) texture->bind();
glDrawArrays(GL_TRIANGLE_FAN,0,4);
vao.release();
program->release();

GL_TRIANGLE是一个三角形一个三角形的绘制,而GL_TRIANGLE_FAN是将两个三角形绘制为一个四边形,GL_QUADS是直接绘制一个四边形。

效果为:

image

当前只有一个纹理,就只需要bind()一次就够了。

如果是多个纹理就需要绑定uniform

1
2
3
4
program->setUniformValue("texture",0);
texture->bind();
program->setUniformValue("texture1",1);
texture1->bind();

显示图片2

图片加载,渲染语言和上面一样。

初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
float vertex[]={
-1.0, 1.0, 0.0, 0.0,
1.0, 1.0, 1.0, 0.0,
1.0, -1.0, 1.0, 1.0,
-1.0, -1.0, 0.0, 1.0
};

vbo.create();
vbo.bind();
vbo.allocate(vertex,sizeof(vertex)*sizeof(float));

GLuint ids[1];
glGenTextures(1,ids);
idRGB = ids[0];

设置顶点数组数据,并分配内存

绘图函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
if(img.isNull()) return;

program->bind();
//vao.bind();
vbo.bind();

program->enableAttributeArray(0);
program->enableAttributeArray(1);
program->setAttributeBuffer(0, GL_FLOAT, 0, 2, 4*sizeof(GLfloat));
program->setAttributeBuffer(1, GL_FLOAT, 2*sizeof(GLfloat), 2, 4*sizeof(GLfloat));

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,idRGB);
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,img.width(),img.height(),0,GL_RGBA,GL_UNSIGNED_BYTE,img.bits());
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

program->setUniformValue("texture",0);
glDrawArrays(GL_TRIANGLE_FAN,0,4);
program->disableAttributeArray(0);
program->disableAttributeArray(1);
program->release();

可以看到绑定数组数据的代码移到绘图函数里面了,也就是说流程就是这样,位置在哪无所谓。

也可以这样写

初始化函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
...
vbo.create();
vbo.bind();
vbo.allocate(vertices,sizeof(vertices)*sizeof(float));

program->enableAttributeArray(0);
program->enableAttributeArray(1);
program->setAttributeBuffer(0, GL_FLOAT, 0, 2, 4*sizeof(GLfloat));
program->setAttributeBuffer(1, GL_FLOAT, 2*sizeof(GLfloat), 2, 4*sizeof(GLfloat));

GLuint ids[1];
glGenBuffers(1,ids);
idRGB = ids[0];

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,idRGB);
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,img.width(),img.height(),0,GL_RGBA,GL_UNSIGNED_BYTE,img.bits());
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

vao.release();
vbo.release();
program->release();

绘图函数中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void GLWidget::paintGL()
{
if(img.isNull()) return;

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕和深度缓存
glLoadIdentity(); // 重置当前的模型观察矩阵

program->bind();
vao.bind();

glBindTexture(GL_TEXTURE_2D,idRGB);
program->setUniformValue("texture",0);

glDrawArrays(GL_TRIANGLE_FAN,0,4);

vao.release();
program->release();
}

最下面的就是解除绑定。

GL_TEXTURE0是此纹理数据所在的位置,他的值为0,setUniformValue绑定的其实是"texture"和GL_TEXTURE0。将纹理位置和渲染语言变量绑定到一起。

查看效果

result

会发现图片篇青色。肯定是哪里有问题。

先用代码获取一下图片格式

1
qDebug()<<img.format();

输出为:

1
QImage::Format_RGB32

那么RGB格式是没错了,但是它有32位,一个通道是8位,就是四个通道,即RGBA。

在图片加载完成后添加代码

1
2
3
img = QImage(":/resources/bird.jpg");
img.convertTo(QImage::Format_RGBA8888);
qDebug()<<img.format();

将图片转换为RGBA格式,这样显示就对了。

image

也可以将图片转换为RGB888,在绘图中将格式改为GL_RGB。

立方体贴图

图像能够正常显示之后,我们就该往立方体上面贴了。

渲染语言

顶点着色器

1
2
3
4
5
6
7
8
9
10
11
12
13
#version 330 core

uniform mat4 mvpMatrix;

in vec4 pos;
in vec2 texCoord;
out vec4 texc;

void main(void)
{
gl_Position = mvpMatrix * pos;
texc = vec4(texCoord,0.0,1.0);
}

片元着色器

1
2
3
4
5
6
7
8
9
#version 330 core

uniform sampler2D texture;
in vec4 texc;

void main(void)
{
gl_FragColor = texture2D(texture,texc.st);
}

初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

float vertices[]={
// Front face
-1.0f, -1.0f, 1.0f,0.0f,0.0f,
1.0f, -1.0f, 1.0f,1.0f,0.0f,
1.0f, 1.0f, 1.0f,1.0f,1.0f,
-1.0f, 1.0f, 1.0f,0.0f,1.0f,

// Back face
-1.0f, -1.0f, -1.0f,1.0f,0.0f,
-1.0f, 1.0f, -1.0f,1.0f,1.0f,
1.0f, 1.0f, -1.0f,0.0f,1.0f,
1.0f, -1.0f, -1.0f,0.0f,0.0f,

// Top face
-1.0f, 1.0f, -1.0f,0.0f,1.0f,
-1.0f, 1.0f, 1.0f,0.0f,0.0f,
1.0f, 1.0f, 1.0f,1.0f,0.0f,
1.0f, 1.0f, -1.0f,1.0f,1.0f,

// Bottom face
-1.0f, -1.0f, -1.0f,1.0f,1.0f,
1.0f, -1.0f, -1.0f,0.0f,1.0f,
1.0f, -1.0f, 1.0f,0.0f,0.0f,
-1.0f, -1.0f, 1.0f,1.0f,0.0f,

// Right face
1.0f, -1.0f, -1.0f,1.0f,0.0f,
1.0f, 1.0f, -1.0f,1.0f,1.0f,
1.0f, 1.0f, 1.0f,0.0f,1.0f,
1.0f, -1.0f, 1.0f,0.0f,0.0f,

// Left face
-1.0f, -1.0f, -1.0f,0.0f,0.0f,
-1.0f, -1.0f, 1.0f,1.0f,0.0f,
-1.0f, 1.0f, 1.0f,1.0f,1.0f,
-1.0f, 1.0f, -1.0f,0.0f,1.0f,
};

vbo.create();
vbo.bind();
vbo.allocate(vertices,sizeof(vertices)*sizeof(float));

program->enableAttributeArray(0);
program->enableAttributeArray(1);
program->setAttributeBuffer(0, GL_FLOAT, 0, 3, 5*sizeof(GLfloat));
program->setAttributeBuffer(1, GL_FLOAT, 3*sizeof(GLfloat), 2, 5*sizeof(GLfloat));

绘图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void GLWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕和深度缓存
glLoadIdentity(); // 重置当前的模型观察矩阵

program->bind();
vbo.bind();

m_modelView.setToIdentity();
m_modelView.translate(0.0f, 0.0f, -5.0f);
m_modelView.rotate(m_xrot, 1.0, 0.0, 0.0);
m_modelView.rotate(m_yrot, 0.0, 1.0, 0.0);
m_modelView.rotate(m_zrot, 0.0, 0.0, 1.0);
program->setUniformValue("mvpMatrix", m_projection * m_modelView);

if(texture->isCreated()) texture->bind();

glDrawArrays(GL_QUADS,0,24);

vbo.release();
program->release();

m_xrot+=0.1f;
m_yrot+=0.1f;
m_zrot+=0.1f;
update();
}

效果为

cube


QOpenGL入门教程09:纹理贴图
https://feater.top/qt/qopengl-image-texture/
作者
JackeyLea
发布于
2020年11月18日
许可协议