(本文是LearnOpenGL的学习笔记,教程中文翻译地址https://learnopengl-cn.github.io/(备用地址https://learnopengl-cn.readthedocs.io/zh/latest/),写于 2020-1-30 ,并在 2021-8-10 进行了更新)
0.前言
上一篇笔记记录了OpenGL在QtWidgets中的使用:https://blog.csdn.net/gongjianbo1992/article/details/104113006 ,本文将学习纹理(Texture)的使用。
1.知识点
根据教程所述,OpenGL中我们可以使用纹理(Texture)来展现细节,纹理是一个2D图片(甚至也有1D和3D的纹理)。
要把纹理映射到绘制区域,如三角形,我们需要指定三角形的每个顶点各自对应纹理的哪个部分。这样每个顶点就会关联着一个纹理坐标(Texture Coordinate),用来标明该从纹理图像的哪个部分采样(采集片段颜色)。之后在图形的其它片段上进行片段插值(Fragment Interpolation),插值方式我们可以设置(即纹理过滤设置,可以根据临近值,或是线性插值)。
纹理坐标在x和y轴上,范围为0到1之间(注意我们使用的是2D纹理图像)。使用纹理坐标获取纹理颜色叫做采样(Sampling)。纹理坐标起始于(0, 0),也就是纹理图片的左下角,终始于(1, 1),即纹理图片的右上角。下面的图片展示了我们是如何把纹理坐标映射到三角形上的。
纹理坐标的范围通常是从(0, 0)到(1, 1),如果我们把纹理坐标设置在范围之外(如小于0,或者大于1),OpenGL默认的行为是重复这个纹理图像,这也是可以设置的(即纹理环绕设置)。
此外,教程还提到了多级渐远纹理和纹理单元。
假设我们有一个包含着上千物体的大房间,每个物体上都有纹理。有些物体会很远,但其纹理会拥有与近处物体同样高的分辨率。由于远处的物体可能只产生很少的片段,OpenGL从高分辨率纹理中为这些片段获取正确的颜色值就很困难,因为它需要对一个跨过纹理很大部分的片段只拾取一个纹理颜色。OpenGL使用一种叫做多级渐远纹理(Mipmap)的概念来解决这个问题,它简单来说就是一系列的纹理图像,后一个纹理图像是前一个的二分之一。多级渐远纹理背后的理念很简单:距观察者的距离超过一定的阈值,OpenGL会使用不同的多级渐远纹理,即最适合物体的距离的那个。由于距离远,解析度不高也不会被用户注意到。同时,多级渐远纹理另一加分之处是它的性能非常好。OpenGL有一个glGenerateMipmaps函数,在创建完一个纹理后调用它OpenGL就会承担接下来的所有工作了。
使用glUniform1i,我们可以给纹理采样器分配一个位置值,这样的话我们能够在一个片段着色器中设置多个纹理。一个纹理的位置值通常称为一个纹理单元(Texture Unit)。一个纹理的默认纹理单元是0,它是默认的激活纹理单元。纹理单元的主要目的是让我们在着色器中可以使用多于一个的纹理。通过把纹理单元赋值给采样器,我们可以一次绑定多个纹理,只要我们首先激活对应的纹理单元。就像glBindTexture一样,我们可以使用glActiveTexture激活纹理单元,传入我们需要使用的纹理单元:
glActiveTexture(GL_TEXTURE0); // 在绑定纹理之前先激活纹理单元
glBindTexture(GL_TEXTURE_2D, texture);
激活纹理单元之后,接下来的glBindTexture函数调用会绑定这个纹理到当前激活的纹理单元。OpenGL至少保证有16个纹理单元供你使用。
2.如何实现
使用OpenGL先继承QOpenGLWidget类并实现对应接口,我写了两个Demo:
第一个Demo(GLTexture 单个纹理贴图),我把教程的着色器程序类替换为了Qt封装的QOpenGLShaderProgram,剩下的直接复制粘贴教程代码改动很小。在图片加载上,我也使用了Qt的QImage类。有三点要注意,
一是因为OpenGL要求纹理坐标原点在图片下方,所以默认加载的图片是上下颠倒的,我在顶点着色器里把纹理顶点反了一下(即1-xx);
二是设置纹理的时候,记得根据图片格式来修改glTexImage2D对应的参数,参数枚举可以查OpenGL API:https://www.khronos.org/opengl/wiki/GLAPI/glTexImage2D ;
三是如果图片是半透明的,需要进行设置,不然没效果:
//深度测试发生于混合之前
//当我们需要绘制透明图片时,就需要关闭GL_DEPTH_TEST并且打开混合glEnable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
//指定混合函数
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
第二个Demo(GLTextureUnit 使用纹理单元多个纹理贴图),我把大部分操作都替换为了Qt提供的封装类。相较于直接使用OpenGL的函数,Qt提供的类操作更简洁,比如纹理图片数据的加载,直接构造函数里填上路径就可以加载了。一些对象的创建有create()函数,绑定有bind()函数,释放有destroy()函数,等等。
在析构函数中,因为要释放VAO、VBO等,Qt示例里是把释放操作放在了makeCurrent();和doneCurrent();两句代码之间。makecurrent()通过使相应的上下文成为当前上下文并在该上下文中绑定framebuffer对象,为渲染此小部件的OpenGL内容做准备。在大多数情况下,不必调用此函数,因为在调用paintGL()之前会自动调用该函数。doneCurrent()释放上下文。(为什么Qt没有做一个析构时自动释放的封装)
3.实现代码
(项目git链接:https://github.com/gongjianbo/OpenGLwithQtWidgets.git)
我的GLTexture类和GLTextureUnit类效果图:
(教程中的示意图箱子和笑脸 mix 后,黑边依然可见,但运行教程源码后也是没有显示黑边的)
GLTexture类代码:
#pragma once
#include <QOpenGLWidget>
#include <QOpenGLFunctions_4_5_Compatibility>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
#include <QOpenGLTexture>
//纹理渲染
//QOpenGLWidget窗口上下文
//QOpenGLFunctions访问OpenGL接口,可以不继承作为成员变量使用
class GLTexture
: public QOpenGLWidget
, protected QOpenGLFunctions_4_5_Compatibility
{
public:
explicit GLTexture(QWidget *parent = nullptr);
~GLTexture();
protected:
//【】继承QOpenGLWidget后重写这三个虚函数
//设置OpenGL资源和状态。在第一次调用resizeGL或paintGL之前被调用一次
void initializeGL() override;
//渲染OpenGL场景,每当需要更新小部件时使用
void paintGL() override;
//设置OpenGL视口、投影等,每当尺寸大小改变时调用
void resizeGL(int width, int height) override;
private:
void checkShaderError(GLuint id, const QString &type);
private:
//使用原生接口操作
GLuint shaderProgram = 0;
GLuint vao = 0;
GLuint vbo = 0;
GLuint ebo = 0;
GLuint texture = 0;
};
#include "GLTexture.h"
#include <QImage>
#include <QDebug>
GLTexture::GLTexture(QWidget *parent)
: QOpenGLWidget(parent)
{
}
GLTexture::~GLTexture()
{
//initializeGL在显示时才调用,释放未初始化的会异常
if(!isValid())
return;
//QOpenGLWidget
//三个虚函数不需要makeCurrent,对应的操作已由框架完成
//但是释放时需要设置当前上下文
makeCurrent();
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &vbo);
glDeleteBuffers(1, &ebo);
glDeleteTextures(1, &texture);
glDeleteProgram(shaderProgram);
doneCurrent();
}
void GLTexture::initializeGL()
{
//QOpenGLFunctions
//为当前上下文初始化opengl函数解析
initializeOpenGLFunctions();
//着色器代码
//in输入,out输出,uniform从cpu向gpu发送
//OpenGL要求y轴0.0坐标是在图片的底部的,但是图片的y轴0.0坐标通常在顶部
//--这里对纹理坐标的y进行的取反
const char *vertex_str=R"(#version 450
layout(location = 0) in vec3 inPos;
layout(location = 1) in vec3 inColor;
layout(location = 2) in vec2 inTexCoord;
out vec3 theColor;
out vec2 texCoord;
void main() {
gl_Position = vec4(inPos,1.0);
theColor = inColor;
texCoord = vec2(inTexCoord.x, 1-inTexCoord.y);
})";
const char *fragment_str=R"(#version 450
uniform sampler2D theTexture;
in vec3 theColor;
in vec2 texCoord;
out vec4 fragColor;
void main() {
fragColor = texture(theTexture, texCoord) * vec4(theColor, 1.0);
})";
//顶点着色器
//创建着色器对象
//GLuint glCreateShader(GLenum shaderType);
GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
//设置着色器对象中的的代码
//void glShaderSource(GLuint shader, GLsizei count,
// const GLchar **string, const GLint *length);
//参数1指定着色器对象
//参数2指定字符串个数
//参数3指定字符串二维数组指针
//参数4指定字符串长度数组,为NULL则以'\0'为字符串终止符
glShaderSource(vertex_shader, 1, &vertex_str, NULL);
//编译着色器对象
//void glCompileShader(GLuint shader);
glCompileShader(vertex_shader);
//检测着色器是否异常
checkShaderError(vertex_shader, "vertex");
//片段着色器
GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, 1, &fragment_str, NULL);
glCompileShader(fragment_shader);
checkShaderError(fragment_shader,"fragment");
//着色器程序
//创建一个空的程序对象并返回一个可以被引用的非零值
//GLuint glCreateProgram(void);
shaderProgram = glCreateProgram();
//将着色器对象附加到程序对象
//void glAttachShader(GLuint program, GLuint shader);
glAttachShader(shaderProgram, vertex_shader);
glAttachShader(shaderProgram, fragment_shader);
//链接程序对象
//void glLinkProgram(GLuint program);
glLinkProgram(shaderProgram);
checkShaderError(shaderProgram, "program");
//删除着色器,它们已经链接到我们的着色器程序对象了
//void glDeleteShader(GLuint shader);
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
//从LearnOpenGL移植过来
float vertices[] = {
// positions // colors // texture coords
0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, // top right
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom left
-0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f // top left
};
unsigned int indices[] = {
0, 1, 3, // first triangle
1, 2, 3 // second triangle
};
//生成顶点数组对象
//void glCreateVertexArrays(GLsizei n, GLuint *arrays);
glCreateVertexArrays(1, &vao);
//生成顶点缓冲对象
//void glCreateBuffers(GLsizei n, GLuint *buffers);
glCreateBuffers(1, &vbo);
//分配size个存储单元存储数据或索引
//glNamedBufferStorage(GLuint buffer, GLsizeiptr size,
// const void *data, GLbitfield flags);
//参数1缓冲区对象
//参数2数据块大小
//参数3如果为NULL则是size个为初始化的数据,否则以data拷贝初始化
//参数4数据相关的用途
glNamedBufferStorage(vbo, sizeof(vertices), vertices, GL_DYNAMIC_STORAGE_BIT);
//vbo绑定到vao
//glVertexArrayVertexBuffer(GLuint vaobj, GLuint bindingindex,
// GLuint buffer, GLintptr offset, GLsizei stride);
//参数1顶点数组对象
//参数2vbo在vao的索引
//参数3顶点缓冲对象
//参数4缓冲区第一个元素的偏移
//参数5缓冲区顶点步进,三角形一个点3个float
glVertexArrayVertexBuffer(vao, 0, vbo, 0, 8*sizeof(float));
//启用通用顶点 attribute 数组的 index 索引,对应layout location
//glEnableVertexArrayAttrib(GLuint vaobj, GLuint index);
glEnableVertexArrayAttrib(vao, 0);
glEnableVertexArrayAttrib(vao, 1);
glEnableVertexArrayAttrib(vao, 2);
//指定顶点数组的组织
//glVertexArrayAttribFormat(GLuint vaobj, GLuint attribindex,
// GLint size, GLenum type,
// GLboolean normalized, GLuint relativeoffset);
//参数1顶点数组对象
//参数2通用顶点 attribute 数组,对应layout location
//参数3每个顶点几个数据
//参数4存储类型
//参数5是否归一化
//参数6顶点步进
glVertexArrayAttribFormat(vao, 0, 3, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribFormat(vao, 1, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float));
glVertexArrayAttribFormat(vao, 2, 2, GL_FLOAT, GL_FALSE, 6*sizeof(float));
//关联顶点 attribute 属性和顶点缓冲区的绑定
//glVertexArrayAttribBinding(GLuint vaobj, GLuint attribindex,
// GLuint bindingindex);
//参数1顶点数组对象
//参数2属性 attribute 索引,对应layout location
//参数3vbo在vao的索引
glVertexArrayAttribBinding(vao, 0, 0);
glVertexArrayAttribBinding(vao, 1, 0);
glVertexArrayAttribBinding(vao, 2, 0);
//生成索引缓冲对象
glCreateBuffers(1, &ebo);
//分配size个存储单元存储数据或索引
glNamedBufferStorage(ebo, sizeof(indices), indices, GL_DYNAMIC_STORAGE_BIT);
//ebo绑定到vao
//void glVertexArrayElementBuffer(GLuint vaobj, GLuint buffer);
glVertexArrayElementBuffer(vao, ebo);
//图片读取
QImage img(":/awesomeface.png");
img.convertTo(QImage::Format_RGBA8888);
//创建纹理对象
//void glCreateTextures(GLenum target, GLsizei n, GLuint *textures);
glCreateTextures(GL_TEXTURE_2D,1,&texture);
//设置纹理参数
//void glTextureParameteri(GLuint texture, GLenum pname, GLint param);
//GL_TEXTURE_WRAP设置纹理环绕方式,即超出纹理坐标怎么处理
//s\t\r对应x\y\z
//GL_REPEAT重复,GL_MIRRORED_REPEAT镜像重复,GL_CLAMP_TO_EDGE拉伸,GL_CLAMP_TO_BORDER无重复
glTextureParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTextureParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
//纹理过滤,怎样将纹理像素(Texture Pixel)映射到纹理坐标
//MIN对应Minify缩小,MAG对应Magnify放大
//GL_NEAREST临近值,GL_LINEAR线性插值
glTextureParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//定义贴图的使用内存大小
//void glTextureStorage2D(GLuint texture, GLsizei levels,
// GLenum internalformat, GLsizei width, GLsizei height);
//参数1纹理对象
//参数2levels和多级渐远相关
//参数3存储格式
//参数4宽度
//参数5高度
glTextureStorage2D(texture,1,GL_RGBA8,img.width(),img.height());
//加载纹理数据
//void glTextureSubImage2D(GLuint texture, GLint level,
// GLint xoffset, GLint yoffset,
// GLsizei width, GLsizei height, GLenum format,
// GLenum type, const void *pixels);
//参数1纹理对象
//参数2levels和多级渐远相关
//参数3纹理阵列中x方向上的纹理偏移
//参数4纹理阵列中y方向上的纹理偏移
//参数5纹理图像宽
//参数6纹理图像高
//参数7图像格式
//参数8像素数据类型
//参数9数据指针
glTextureSubImage2D(texture,0,0,0,img.width(),img.height(),GL_RGBA,GL_UNSIGNED_BYTE,img.bits());
}
void GLTexture::paintGL()
{
//清屏设置
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//深度测试发生于混合之前
//当我们需要绘制透明图片时,就需要关闭GL_DEPTH_TEST并且打开混合glEnable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
//指定混合函数
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//安装所指定的程序对象程序作为当前再现状态的一部分
glUseProgram(shaderProgram);
//激活纹理
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
//根据索引渲染两个三角
glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}
void GLTexture::resizeGL(int width, int height)
{
glViewport(0,0,width,height);
}
void GLTexture::checkShaderError(GLuint id, const QString &type)
{
int check_flag;
char check_info[1024];
if(type != "program"){
glGetShaderiv(id, GL_COMPILE_STATUS, &check_flag);
if(!check_flag){
glGetShaderInfoLog(id, 1024, NULL, check_info);
qDebug() << type << " error:" << check_info;
}
}else{
glGetShaderiv(id, GL_LINK_STATUS, &check_flag);
if(!check_flag){
glGetProgramInfoLog(id, 1024, NULL, check_info);
qDebug() << type << " error:" << check_info ;
}
}
}
GLTextureUnit类代码:
#pragma once
#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
#include <QOpenGLTexture>
//纹理单元
//QOpenGLWidget窗口上下文
//QOpenGLFunctions访问OpenGL接口,可以不继承作为成员变量使用
class GLTextureUnit
: public QOpenGLWidget
, protected QOpenGLFunctions_3_3_Core
{
public:
explicit GLTextureUnit(QWidget *parent = nullptr);
~GLTextureUnit();
protected:
//【】继承QOpenGLWidget后重写这三个虚函数
//设置OpenGL资源和状态。在第一次调用resizeGL或paintGL之前被调用一次
void initializeGL() override;
//渲染OpenGL场景,每当需要更新小部件时使用
void paintGL() override;
//设置OpenGL视口、投影等,每当尺寸大小改变时调用
void resizeGL(int width, int height) override;
private:
//着色器程序
QOpenGLShaderProgram shaderProgram;
//顶点数组对象
QOpenGLVertexArrayObject vao;
//顶点缓冲
QOpenGLBuffer vbo;
//索引缓冲
QOpenGLBuffer ebo;
//纹理(因为不能赋值,所以只能声明为指针)
QOpenGLTexture *texture1 = nullptr;
QOpenGLTexture *texture2 = nullptr;
};
#include "GLTextureUnit.h"
#include <QDebug>
GLTextureUnit::GLTextureUnit(QWidget *parent)
: QOpenGLWidget(parent)
{
}
GLTextureUnit::~GLTextureUnit()
{
//initializeGL在显示时才调用,释放未初始化的会异常
if(!isValid())
return;
//QOpenGLWidget
//三个虚函数不需要makeCurrent,对应的操作已由框架完成
//但是释放时需要设置当前上下文
makeCurrent();
vbo.destroy();
ebo.destroy();
vao.destroy();
delete texture1;
delete texture2;
doneCurrent();
}
void GLTextureUnit::initializeGL()
{
//为当前上下文初始化OpenGL函数解析
initializeOpenGLFunctions();
//着色器代码
//in输入,out输出,uniform从cpu向gpu发送
//因为OpenGL纹理颠倒过来的,所以取反vec2(aTexCoord.x, 1-aTexCoord.y);
const char *vertex_str=R"(#version 330 core
layout (location = 0) in vec3 inPos;
layout (location = 1) in vec2 inTexCoord;
out vec2 texCoord;
void main()
{
gl_Position = vec4(inPos, 1.0);
texCoord = vec2(inTexCoord.x, 1-inTexCoord.y);
})";
const char *fragment_str=R"(#version 330 core
uniform sampler2D texture1;
uniform sampler2D texture2;
in vec2 texCoord;
out vec4 fragColor;
void main()
{
fragColor = mix(texture(texture1, texCoord),
texture(texture2, texCoord), 0.2);
})";
//将source编译为指定类型的着色器,并添加到此着色器程序
if(!shaderProgram.addCacheableShaderFromSourceCode(
QOpenGLShader::Vertex,vertex_str)){
qDebug()<<"compiler vertex error"<<shaderProgram.log();
}
if(!shaderProgram.addCacheableShaderFromSourceCode(
QOpenGLShader::Fragment,fragment_str)){
qDebug()<<"compiler fragment error"<<shaderProgram.log();
}
//使用addShader()将添加到该程序的着色器链接在一起。
if(!shaderProgram.link()){
qDebug()<<"link shaderprogram error"<<shaderProgram.log();
}
//顶点数据
float vertices[] = {
// positions // texture coords
0.5f, 0.5f, 0.0f, 1.0f, 1.0f, // top right
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, // bottom left
-0.5f, 0.5f, 0.0f, 0.0f, 1.0f // top left
};
//索引
unsigned int indices[] = {
0, 1, 3, // first Triangle
1, 2, 3 // second Triangle
};
vao.create();
vao.bind();
//QOpenGLVertexArrayObject::Binder vaoBind(&vao);
vbo=QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
vbo.create();
vbo.bind();
vbo.allocate(vertices,sizeof(vertices));
ebo=QOpenGLBuffer(QOpenGLBuffer::IndexBuffer);
ebo.create();
ebo.bind();
ebo.allocate(indices,sizeof(indices));
// position attribute
int attr = -1;
attr = shaderProgram.attributeLocation("inPos");
shaderProgram.setAttributeBuffer(attr, GL_FLOAT, 0, 3, sizeof(GLfloat) * 5);
shaderProgram.enableAttributeArray(attr);
// texture coord attribute
attr = shaderProgram.attributeLocation("inTexCoord");
shaderProgram.setAttributeBuffer(attr, GL_FLOAT, sizeof(GLfloat) * 3, 2, sizeof(GLfloat) * 5);
shaderProgram.enableAttributeArray(attr);
// texture 1
//直接生成绑定一个2d纹理, 并生成多级纹理MipMaps
texture1 = new QOpenGLTexture(QImage(":/container.jpg"), QOpenGLTexture::GenerateMipMaps);
if(!texture1->isCreated()){
qDebug() << "Failed to load texture";
}
//texture1->setFormat(QOpenGLTexture::RGB8U);
// set the texture wrapping parameters
// 等于glTexParameteri(GLtexture_2D, GLtexture_WRAP_S, GL_REPEAT);
texture1->setWrapMode(QOpenGLTexture::Repeat);
//texture1->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::Repeat);
//texture1->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::Repeat);
// set texture filtering parameters
//等价于glTexParameteri(GLtexture_2D, GLtexture_MIN_FILTER, GL_LINEAR);
texture1->setMinificationFilter(QOpenGLTexture::Linear);
texture1->setMagnificationFilter(QOpenGLTexture::Linear);
// texture 2
//直接生成绑定一个2d纹理, 并生成多级纹理MipMaps
texture2 = new QOpenGLTexture(QImage(":/awesomeface.png"), QOpenGLTexture::GenerateMipMaps);
if(!texture2->isCreated()){
qDebug() << "Failed to load texture";
}
//texture2->setFormat(QOpenGLTexture::RGBA8U);
// set the texture wrapping parameters
// 等于glTexParameteri(GLtexture_2D, GLtexture_WRAP_S, GL_REPEAT);
texture2->setWrapMode(QOpenGLTexture::Repeat);
//texture2->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::Repeat);
//texture2->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::Repeat);
// set texture filtering parameters
//等价于glTexParameteri(GLtexture_2D, GLtexture_MIN_FILTER, GL_LINEAR);
texture2->setMinificationFilter(QOpenGLTexture::Linear);
texture2->setMagnificationFilter(QOpenGLTexture::Linear);
shaderProgram.bind();
shaderProgram.setUniformValue("texture1", 0);
shaderProgram.setUniformValue("texture2", 1);
shaderProgram.release();
}
void GLTextureUnit::paintGL()
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//深度测试发生于混合之前
//当我们需要绘制透明图片时,就需要关闭GL_DEPTH_TEST并且打开混合glEnable(GL_BLEND);
//glDisable(GL_DEPTH_TEST);
//glEnable(GL_BLEND);
//指定混合函数
//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//纹理单元的应用
glActiveTexture(GL_TEXTURE0);
texture1->bind();
glActiveTexture(GL_TEXTURE1);
texture2->bind();
shaderProgram.bind();
//绘制
//QOpenGLVertexArrayObject::Binder vaoBind(&vao);
vao.bind();
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
shaderProgram.release();
}
void GLTextureUnit::resizeGL(int width, int height)
{
glViewport(0, 0, width, height);
}
4.参考
教程:https://learnopengl-cn.github.io/01%20Getting%20started/06%20Textures/
博客(Qt+OpenGL):https://www.jianshu.com/p/273b7f960f3d
博客(纹理颠倒):https://blog.csdn.net/narutojzm1/article/details/51940817