QT+OpenGL高级数据和高级GLSL

QT+OpenGL高级数据和高级GLSL

本篇完整工程见gitee:QtOpenGL 对应点的tag,由turbolove提供技术支持,您可以关注博主或者私信博主

高级数据

  • OpenGL中的缓冲区 对象管理特定的GPU内存

  • 在将缓冲区绑定到特定的缓冲区目标时候赋予它意义

  • OpenGL在内部会保存每个目标(缓冲区)的引用,并且根据目标以不同的方式处理缓冲区。

glBufferData 填充缓冲对象所管理的内存

glBufferSubData 填充缓冲的特定区域

glBufferSubData(GL_ARRAY_BUFFER, 24, sizeof(data), &data);

将数据导入缓冲区的另外一种方法:

  • 通过调用glMapBuffer函数,OpenGL会返回当前绑定的缓冲区的内存指针
  • 直接将数据复制到缓冲中 glMapBuffer使用的时候,缓冲区的内存必须已经存在
float data[] = {
  0.5f, 1.0f, -0.35f
  ...
};
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// 获取指针
void *ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
// 复制数据到内存
memcpy(ptr, data, sizeof(data));
// 记得告诉OpenGL我们不再需要这个指针了
glUnmapBuffer(GL_ARRAY_BUFFER);

分批顶点属性

float positions[] = { ... };
float normals[] = { ... };
float tex[] = { ... };
// 填充缓冲
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(positions), &positions);
glBufferSubData(GL_ARRAY_BUFFER, sizeof(positions), sizeof(normals), &normals);
glBufferSubData(GL_ARRAY_BUFFER, sizeof(positions) + sizeof(normals), sizeof(tex), &tex);

使用glVertexAttribPointer,指定顶点数组缓冲区内筒的属性布局。

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);  
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)(sizeof(positions)));  
glVertexAttribPointer(
  2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)(sizeof(positions) + sizeof(normals)));

复制缓冲

当你的缓冲已经填充好数据之后,你可能会想与其它的缓冲共享其中的数据,或者想要将缓冲的内容复制到另一个缓冲当中。glCopyBufferSubData能够让我们相对容易地从一个缓冲中复制数据到另一个缓冲中。这个函数的原型如下:

void glCopyBufferSubData(GLenum readtarget, GLenum writetarget, GLintptr readoffset,
                         GLintptr writeoffset, GLsizeiptr size);

readtargetwritetarget参数需要填入复制源和复制目标的缓冲目标

如果想读写数据的两个不同缓冲都为顶点数组缓冲该怎么办?使用下面的例子:

float vertexData[] = { ... };
glBindBuffer(GL_COPY_READ_BUFFER, vbo1);
glBindBuffer(GL_COPY_WRITE_BUFFER, vbo2);
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, sizeof(vertexData));
float vertexData[] = { ... };
glBindBuffer(GL_COPY_READ_BUFFER, vbo1);
glBindBuffer(GL_COPY_WRITE_BUFFER, vbo2);
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, sizeof(vertexData));

高级GLSL

顶点着色器变量

  • gl_Position : 输出变量,顶点着色器的裁减空间位置向量
  • gl_PointSize :输出变量,是一个float变量,设置点的宽高(像素)
glEnable(GL_PROGRAM_POINT_SIZE); 
  • gl_VertexID :输入变量,储存了正在绘制顶点的当前ID

shader.vert

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
out vec3 Normal;
out vec3 FragPos;
out vec2 TexCoords;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main() 
{
	TexCoords=aTexCoords;
	Normal = mat3(transpose(inverse(model))) * aNormal;
	FragPos=vec3(model * vec4(aPos,1.0));
	gl_Position = projection * view * model * vec4(aPos, 1.0);
	gl_PointSize = gl_Position.z;
}

在这里插入图片描述

片段着色器变量

  • gl_FragCoord :的x和y分量是片段的窗口空间的坐标,其原点为窗口的左下角,z分量等于对应片段的深度值
  • gl_FrontFacing :变量是一个bool如果当前片段是正面向的一部分那么就是true, 否则就是false
  • gl_FragDepth : 着色器内设置片段的深度值

shader.frag

void main() {
    if(gl_FragCoord.x < 400 )
        FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    else
        FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}

在这里插入图片描述

gl_FrontFacing :变量是一个bool如果当前片段是正面向的一部分那么就是true, 否则就是false

#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D frontTexture;
uniform sampler2D backTexture;
void main()
{             
    if(gl_FrontFacing)
        FragColor = texture(frontTexture, TexCoords);
    else
        FragColor = texture(backTexture, TexCoords);
}

gl_FragDepth : 着色器内设置片段的深度值

#version 420 core // 注意GLSL的版本!
out vec4 FragColor;
layout (depth_greater) out float gl_FragDepth;

void main()
{             
    FragColor = vec4(1.0);
    gl_FragDepth = gl_FragCoord.z + 0.1;
}  

layout (depth_<condition>) out float gl_FragDepth;

condition可以为下面的值:

条件描述
any默认值。提前深度测试是禁用的,你会损失很多性能
greater你只能让深度值比gl_FragCoord.z更大
less你只能让深度值比gl_FragCoord.z更小
unchanged如果你要写入gl_FragDepth,你将只能写入gl_FragCoord.z的值

接口块

接口块的声明和struct的声明有点相像,不同的是,现在根据它是一个输入还是输出块(Block),使用in或者out关键字来定义的。

shader.vert

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoords;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

out VS_OUT
{
    vec2 TexCoords;
} vs_out;

void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);    
    vs_out.TexCoords = aTexCoords;
}  

shader.frag

#version 330 core
out vec4 FragColor;

in VS_OUT
{
    vec2 TexCoords;
} fs_in;

uniform sampler2D texture;

void main()
{             
    FragColor = texture(texture, fs_in.TexCoords);   
}

只要两个接口块的名字一样,对应的输入和输出将会匹配起来。

块名应该是和着色器中一样的(VS_OUT)但是实例名称(vs_out, fs_in)是可以随意的。

Uniform缓冲对象

当使用多于一个的着色器时候,尽管大部分的uniform变量都是相同的,我们还是需要不断的设置它们。解决办法:全局uniform变量。

shader.vert

#version 330 core
layout (location = 0) in vec3 aPos;
layout (std140) uniform Matrices
{
    mat4 projection;
    mat4 view;
};
uniform mat4 model;
void main()
{
    // uniform 块中的变量可以直接访问,不需要加块名称作为前缀。
    gl_Position = projection * view * model * vec4(aPos, 1.0);
}

其中 layout (std140) 设置了Uniform块布局

使用std140布局计算出每个成员的对齐偏移量:

layout (std140) uniform ExampleBlock
{
                     // 基准对齐量       // 对齐偏移量
    float value;     // 4               // 0 
    vec3 vector;     // 16              // 16  (必须是16的倍数,所以 4->16)
    mat4 matrix;     // 16              // 32  (列 0)
                     // 16              // 48  (列 1)
                     // 16              // 64  (列 2)
                     // 16              // 80  (列 3)
    float values[3]; // 16              // 96  (values[0])
                     // 16              // 112 (values[1])
                     // 16              // 128 (values[2])
    bool boolean;    // 4               // 144
    int integer;     // 4               // 148
}; 
类型布局规则
标量,比如int和bool每个标量的基准对齐量为N。
向量2N或者4N。这意味着vec3的基准对齐量为4N。
标量或向量的数组每个元素的基准对齐量与vec4的相同。
矩阵储存为列向量的数组,每个向量的基准对齐量与vec4的相同。
结构体等于所有元素根据规则计算后的大小,但会填充到vec4大小的倍数。

我们已经讨论了如何在着色器中定义Uniform块,并设定它们的内存布局了,但我们还没有讨论该如何使用它们。

首先,我们需要调用glGenBuffers,创建一个Uniform缓冲对象。一旦我们有了一个缓冲对象,我们需要将它绑定到GL_UNIFORM_BUFFER目标,并调用glBufferData,分配足够的内存。

unsigned int uboExampleBlock;
glGenBuffers(1, &uboExampleBlock);
glBindBuffer(GL_UNIFORM_BUFFER, uboExampleBlock);
glBufferData(GL_UNIFORM_BUFFER, 152, NULL, GL_STATIC_DRAW); // 分配152字节的内存
glBindBuffer(GL_UNIFORM_BUFFER, 0);
UBO代码实现

具体实现请参照tag-UBO

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
可以使用Qt自带的QOpenGLWidget来展示3D模型,具体步骤如下: 1. 创建一个继承自QOpenGLWidget的类,重写其initializeGL、resizeGL和paintGL方法。 2. 在initializeGL方法中初始化OpenGL环境,包括设置OpenGL版本、创建VAO、VBO等。 3. 在resizeGL方法中设置视口和投影矩阵。 4. 在paintGL方法中绘制3D模型,包括设置模型矩阵、绑定纹理、绘制VAO等。 5. 创建一个窗口,将上述自定义的QOpenGLWidget添加到窗口中。 6. 在窗口中加载3D模型文件,通过OpenGL绘制出来。 下面是一个简单的示例代码: ``` #include <QOpenGLWidget> #include <QOpenGLFunctions> #include <QOpenGLShaderProgram> #include <QOpenGLBuffer> class MyOpenGLWidget : public QOpenGLWidget, protected QOpenGLFunctions { public: MyOpenGLWidget(QWidget *parent = nullptr); ~MyOpenGLWidget(); protected: void initializeGL() override; void resizeGL(int w, int h) override; void paintGL() override; private: QOpenGLShaderProgram m_program; QOpenGLBuffer m_vbo; }; MyOpenGLWidget::MyOpenGLWidget(QWidget *parent) : QOpenGLWidget(parent) {} MyOpenGLWidget::~MyOpenGLWidget() {} void MyOpenGLWidget::initializeGL() { initializeOpenGLFunctions(); m_program.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/vertexShader.glsl"); m_program.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/fragmentShader.glsl"); m_program.link(); m_program.bind(); // 创建VAO和VBO m_vbo.create(); m_vbo.bind(); m_vbo.allocate(vertices, sizeof(vertices)); m_program.enableAttributeArray(0); m_program.setAttributeBuffer(0, GL_FLOAT, 0, 3, sizeof(Vertex)); } void MyOpenGLWidget::resizeGL(int w, int h) { glViewport(0, 0, w, h); // 设置投影矩阵 QMatrix4x4 projection; projection.perspective(45.0f, GLfloat(w) / h, 0.1f, 100.0f); m_program.setUniformValue("projection", projection); } void MyOpenGLWidget::paintGL() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 设置模型矩阵 QMatrix4x4 model; model.translate(0.0f, 0.0f, -5.0f); model.rotate(45.0f, QVector3D(1.0f, 0.0f, 0.0f)); m_program.setUniformValue("model", model); // 绑定纹理 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_texture); // 绘制VAO m_vao.bind(); glDrawArrays(GL_TRIANGLES, 0, 36); m_vao.release(); } int main(int argc, char *argv[]) { QApplication app(argc, argv); MyOpenGLWidget widget; widget.show(); return app.exec(); } ``` 以上是一个简单的OpenGL绘制3D模型的示例,更完整的代码可以参考Qt官方文档和OpenGL官方文档。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

turbolove

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

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

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

打赏作者

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

抵扣说明:

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

余额充值