OpenGL with QtWidgets:纹理

(本文是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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龚建波

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值