当前位置:   article > 正文

QT With OpenGL(Texture篇)

QT With OpenGL(Texture篇)

一、Texture2D

Learn_OpenGL中,加载纹理使用了std_image库,这个库,额~~~~,挺(nan)好(yi)用(yan)的(biao)。
在Qt中,加载纹理可以使用内置库:

  • QOpenGLTexture来进行纹理操作。
  • QImage来进行纹理载入。

1.纹理加载

QImage image=QImage(path).mirrored(false,true);//加载单一纹理代码
  • 1

QImage data(directory.filePath(str.C_Str()));//加载模型纹理代码
  • 1

QImage::mirrored: Returns a mirror of the image, mirrored in the horizontal and/or the vertical direction depending on whether horizontal and vertical are set to true or false

QOpenGLTexture *texture = new QOpenGLTexture(image, QOpenGLTexture::GenerateMipMaps); 
//直接生成绑定一个2d纹理, 并生成多级纹理MipMaps
  • 1
  • 2

QOpenGLTexture *texture = new QOpenGLTexture(image);等同

QOpenGLTexture::QOpenGLTexture(const QImage &image, QOpenGLTexture::MipMapGeneration genMipMaps = GenerateMipMaps)
——————————————————————————————————
Creates a QOpenGLTexture object that can later be bound to the 2D texture target and contains the pixel data contained in image. If you wish to have a chain of mipmaps generated then set genMipMaps to true (this is the default).

QOpenGLTexture * texture = new Texture;
texture.setData(data);//data为Qimage类
  • 1
  • 2

void QOpenGLTexture::setData(const QImage &image, QOpenGLTexture::MipMapGeneration genMipMaps = GenerateMipMaps)
————————————————————————————————————
This overload of setData() will allocate storage for you. The pixel data is contained in image. Mipmaps are generated by default. Set genMipMaps to DontGenerateMipMaps to turn off mipmap generation.

2.设置纹理属性

// set the texture wrapping parameters
texture->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::Repeat);
// 等于glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
texture->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::Repeat);
//    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// set texture filtering parameters
texture->setMinificationFilter(QOpenGLTexture::Linear);   
//等价于glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
texture->setMagnificationFilter(QOpenGLTexture::Linear);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
texture.setMinMagFilters(QOpenGLTexture::LinearMipMapLinear,QOpenGLTexture::Linear);
  • 1

3.纹理绑定到GPU

glActiveTexture(GL_TEXTURE0);
texture1->bind();
glActiveTexture(GL_TEXTURE1);
texture2->bind();
  • 1
  • 2
  • 3
  • 4

4.纹理与shader中的sampler2D绑定

shaderProgram.setUniformValue("texture1",0);
shaderProgram.setUniformValue("texture2",1);
  • 1
  • 2

5.析构

别忘啦delete texture,因为texturenew出来的。

二、立方体贴图

1. 创建立方体贴图类QOpenGLTexture的实例

QOpenGLTexture *pTexture=new QOpenGLTexture(QOpenGLTexture::TargetCubeMap);
  • 1

2.加载图片

默认读取的纹理为32位RGB,不符合CubeMap的要求,必须转为24位RGB。

QImage posX = QImage(paths[0]).convertToFormat(QImage::Format_RGB888); //Right
QImage negX = QImage(paths[1]).convertToFormat(QImage::Format_RGB888); //Left
QImage posY = QImage(paths[2]).convertToFormat(QImage::Format_RGB888); //Top
QImage negY = QImage(paths[3]).convertToFormat(QImage::Format_RGB888); //Bottom
QImage posZ = QImage(paths[4]).convertToFormat(QImage::Format_RGB888); //Front
QImage negZ = QImage(paths[5]).convertToFormat(QImage::Format_RGB888); //Back
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

3.设置为立方体贴图

仔细分析QOpenGLTexture的帮助文档,文档清楚的说明除了void setData(const QImage &image, MipMapGeneration genMipMaps = GenerateMipMaps) 函数在生成2D纹理时会默认自动分配纹理物理内存,其余的setData()重载函数都必须手动分配内存空间。

所以,如果我们需要生成纹理目标为CubeMap的纹理,必须提前手动分配内存空间。

在手动分配内存空间之前,必须确定纹理的尺寸,类型等样式

因此,对于 QOpenGLTexture ,您必须在 allocateStorage 之前设置 SizeFormat . 最后一步是 setData .

第一步、设置纹理尺寸size

pTexture->setSize(posX.width(),posX.height(),posX.depth());
  • 1

posX.depth()图像深度是用于存储单个像素的位数,也称为每像素位(bpp)。
支撑深度为1、8、16、24、32、64。

要求立方体贴图的六张纹理尺寸必须相同。

第二步、设置纹理格式Format

将纹理格式设置为RGB格式

pTexture->setFormat(QOpenGLTexture::RGBFormat);
  • 1

第三步、给服务器端分配内存

为这个纹理对象分配服务器端存储,考虑到格式尺寸mipmap级别数组层立方体贴图面。
一旦分配了存储,就不可能再更改这些属性。
如果受支持,QOpenGLTexture将使用不可变的纹理存储。
一旦为纹理分配了存储,那么像素数据就可以通过setData()重载上传

allocateStorage(pixelFormat,pixelType)//设置像素格式和像素类型

pTexture->allocateStorage(QOpenGLTexture::RGB,QOpenGLTexture::UInt8);
  • 1

第四步、向内存中添加数据

void QOpenGLTexture::setData(	int mipLevel, 
								int layer, 
								QOpenGLTexture::CubeMapFace cubeFace,
								QOpenGLTexture::PixelFormat sourceFormat, 
								QOpenGLTexture::PixelType sourceType, 
								const void *data, 
								const QOpenGLPixelTransferOptions *const options = nullptr)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

上传纹理对象mipLevel、数组层(layer)和立方体面的像素数据。上传像素数据前必须分配存储空间。setData()的一些重载将设置适当的维度mipmap级别数组层,然后如果它们有足够的信息为您分配存储空间。这将在函数文档中注明。
data所指向的像素数据的结构由sourceFormat和sourceType指定。像素数据上传可以通过选项进行控制。
如果使用了compressed format(),那么应该使用setCompressedData()

setData(mipLevel, 数组层, 立方体贴图位置, 纹理格式, 纹理数据类型, (const void*)数据);
如下:
pTexture->setData(0, 0, QOpenGLTexture::CubeMapPositiveX, QOpenGLTexture::RGB, QOpenGLTexture::UInt8, (const void*)posX.bits());
  • 1
  • 2
  • 3

之后设置纹理的过滤方式,环绕方式。

pTexture->setMinificationFilter(QOpenGLTexture::Linear);   
pTexture->setMagnificationFilter(QOpenGLTexture::Linear);
pTexture->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::ClampToEdge);  
pTexture->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::ClampToEdge); 
  • 1
  • 2
  • 3
  • 4

这样就成功得到了立方体贴图的QOpenGLTexture类数据。

3. 设置立方体VAO,VBO

数据

GLfloat skyboxVertices[] = {
        // Positions
        -1.0f,  1.0f, -1.0f,
        -1.0f, -1.0f, -1.0f,
        1.0f, -1.0f, -1.0f,
        1.0f, -1.0f, -1.0f,
        1.0f,  1.0f, -1.0f,
        -1.0f,  1.0f, -1.0f,

        -1.0f, -1.0f,  1.0f,
        -1.0f, -1.0f, -1.0f,
        -1.0f,  1.0f, -1.0f,
        -1.0f,  1.0f, -1.0f,
        -1.0f,  1.0f,  1.0f,
        -1.0f, -1.0f,  1.0f,

        1.0f, -1.0f, -1.0f,
        1.0f, -1.0f,  1.0f,
        1.0f,  1.0f,  1.0f,
        1.0f,  1.0f,  1.0f,
        1.0f,  1.0f, -1.0f,
        1.0f, -1.0f, -1.0f,

        -1.0f, -1.0f,  1.0f,
        -1.0f,  1.0f,  1.0f,
        1.0f,  1.0f,  1.0f,
        1.0f,  1.0f,  1.0f,
        1.0f, -1.0f,  1.0f,
        -1.0f, -1.0f,  1.0f,

        -1.0f,  1.0f, -1.0f,
        1.0f,  1.0f, -1.0f,
        1.0f,  1.0f,  1.0f,
        1.0f,  1.0f,  1.0f,
        -1.0f,  1.0f,  1.0f,
        -1.0f,  1.0f, -1.0f,

        -1.0f, -1.0f, -1.0f,
        -1.0f, -1.0f,  1.0f,
        1.0f, -1.0f, -1.0f,
        1.0f, -1.0f, -1.0f,
        -1.0f, -1.0f,  1.0f,
        1.0f, -1.0f,  1.0f
    };
  • 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

绑定数据到VAO

QOpenGLVertexArrayObject::Binder skyboxVAOBind(&skyboxVAO);
//创建并绑定,资源获取即初始化,离开作用域自动析构
skyboxVBO.create();
skyboxVBO.bind();
skyboxVBO.allocate(skyboxVertices,sizeof(skyboxVertices));
glEnableVertexAttribArray(0);
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,3*sizeof(float), (void*)0);
skyboxVBO.release();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

4.shader设置

vert

#version 450 core
layout (location = 0) in vec3 aPos;

out vec3 TexCoords;

uniform mat4 projection;
uniform mat4 view;

void main()
{
    TexCoords = aPos;
    vec4 pos = projection * view * vec4(aPos, 1.0);
    gl_Position = pos.xyww;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

注意:这里gl_Position = pos.xyww;将z分量始终设为最大值1,即永远在所有物体之后。

frag

#version 450 core
out vec4 FragColor;

in vec3 TexCoords;

uniform samplerCube skybox;

void main()
{
    FragColor = texture(skybox, TexCoords);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

注意这里是samplerCube

shader链接

skyboxShader.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/skybox.vert");
skyboxShader.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/skybox.frag");
skyboxShader.link();
  • 1
  • 2
  • 3

shader使用

skyboxShader.bind();
QMatrix4x4 view=camera->getViewMatrix();
view.setColumn(3,QVector4D(0,0,0,1.0f));//将第三列(最后一列)设置为(0,0,0,1)
//作用,使摄像机位移不会改变立方体位置
skyboxShader.setUniformValue("view",view);
skyboxShader.setUniformValue("projection",projection);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

5. 渲染

首先设置深度通过方式为小于等于,因为GL_DEPTH_BUFFER_BIT后的深度都为1,为了让天空图替换背景,需要让当深度为1时替换背景颜色。

glDepthFunc(GL_LEQUAL);
  • 1

立方体贴图纹理绑定绘制

QOpenGLVertexArrayObject::Binder skyboxVAOBind(&skyboxVAO);
glActiveTexture(GL_TEXTURE0);
skyboxTexture->bind();
skyboxShader.setUniformValue("skybox",0);
glDrawArrays(GL_TRIANGLES, 0, 36);
  • 1
  • 2
  • 3
  • 4
  • 5

运行结果

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/574235
推荐阅读
相关标签
  

闽ICP备14008679号