QT With OpenGL(Texture篇)

一、Texture2D

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

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

1.纹理加载

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

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

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

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类

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);
texture.setMinMagFilters(QOpenGLTexture::LinearMipMapLinear,QOpenGLTexture::Linear);

3.纹理绑定到GPU

glActiveTexture(GL_TEXTURE0);
texture1->bind();
glActiveTexture(GL_TEXTURE1);
texture2->bind();

4.纹理与shader中的sampler2D绑定

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

5.析构

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

二、立方体贴图

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

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

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

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());

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

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

第二步、设置纹理格式Format

将纹理格式设置为RGB格式

pTexture->setFormat(QOpenGLTexture::RGBFormat);

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

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

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

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

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

void QOpenGLTexture::setData(	int mipLevel, 
								int layer, 
								QOpenGLTexture::CubeMapFace cubeFace,
								QOpenGLTexture::PixelFormat sourceFormat, 
								QOpenGLTexture::PixelType sourceType, 
								const void *data, 
								const QOpenGLPixelTransferOptions *const options = nullptr)

上传纹理对象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());

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

pTexture->setMinificationFilter(QOpenGLTexture::Linear);   
pTexture->setMagnificationFilter(QOpenGLTexture::Linear);
pTexture->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::ClampToEdge);  
pTexture->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::ClampToEdge); 

这样就成功得到了立方体贴图的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
    };

绑定数据到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();

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;
}

注意:这里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);
}

注意这里是samplerCube

shader链接

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

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);

5. 渲染

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

glDepthFunc(GL_LEQUAL);

立方体贴图纹理绑定绘制

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

运行结果

在这里插入图片描述

### 如何在 Qt 中使用 OpenGL 实现视频渲染 #### 创建自定义的 `QOpenGLWidget` 类 为了实现基于 OpenGL 的视频渲染,创建一个继承于 `QOpenGLWidget` 并保护继承 `QOpenGLFunctions` 的类是一个好的起点[^2]。 ```cpp class VideoRenderer : public QOpenGLWidget, protected QOpenGLFunctions { Q_OBJECT public: explicit VideoRenderer(QWidget *parent = nullptr); protected: void initializeGL(); void resizeGL(int w, int h); void paintGL(); private: GLuint m_texture; }; ``` #### 初始化 OpenGL 资源 (`initializeGL`) 在这个函数里初始化所有的 OpenGL 资源,比如编译着色器程序并链接它们到一起形成可执行的 GLSL 程序对象;还可以在这里配置好 VAOs (顶点数组对象),VBOs (缓冲区对象) 或者 EBOs (元素缓冲区对象)[^1]。 ```cpp void VideoRenderer::initializeGL() { initializeOpenGLFunctions(); // Initialize GLEW or similar library functions. // Setup shaders and other resources here... } ``` #### 处理窗口大小改变事件 (`resizeGL`) 每当窗口尺寸发生变化时都会调用此方法,在这里更新视口参数以适应新的屏幕分辨率。 ```cpp void VideoRenderer::resizeGL(int width, int height) { glViewport(0, 0, width, qMax(height, 1)); } ``` #### 渲染帧 (`paintGL`) 这是最重要的部分之一,负责每一帧的数据处理与绘制工作。对于视频播放来说,通常意味着要不断获取新一帧图像数据,并将其上传至 GPU 上作为纹理供片段着色器采样输出最终画面[^4]。 ```cpp void VideoRenderer::paintGL() { glBindFramebuffer(GL_FRAMEBUFFER, 0); // Bind default framebuffer // Clear screen with black color before drawing anything new on top of it glClearColor(0.f, 0.f, 0.f, 1.f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Assuming we have a texture containing the current video frame data, // bind this texture so that our fragment shader can sample from it during rendering. if(m_texture){ glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_texture); // Draw quad covering entire window using previously set up vertex/index buffers etc., // which will cause fragments within covered area to be processed by active program's fs code // Unbind texture after use glBindTexture(GL_TEXTURE_2D, 0); } } ``` 通过上述方式可以在 Qt 应用中利用 OpenGL 技术高效地完成视频内容的呈现任务。需要注意的是实际开发过程中还需要考虑更多细节问题如同步机制、错误检测等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Elsa的迷弟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值