qt+opengl 着色器VAO、VBO、EBO(四)

news2024/11/20 18:31:10

文章目录

  • 一、顶点着色器和片段着色器代码分析
    • 1. 着色器1
    • 2. 顶点着色器2
  • 二、使用步骤
    • 1. 使用着色器1
    • 2. 使用着色器2
    • 3. 在着色器2中使用EBO
  • 三、完整代码


一、顶点着色器和片段着色器代码分析

1. 着色器1

用到的坐标矩阵, 四个四边形顶点坐标

float vertices_data[36] = {
    // 所有的值是在[-1, 1]之间的
    -0.5f, 0.4f, 0.0f,
    -0.5f, 0.2f, 0.0f,
    -0.3f, 0.2f, 0.0f,
    -0.3f, 0.4f, 0.0f,

    -0.1f, 0.4f, 0.0f,
    -0.1f, 0.2f, 0.0f,
     0.1f, 0.2f, 0.0f,
     0.1f, 0.4f, 0.0f,

     0.3f, 0.4f, 0.0f,
     0.3f, 0.2f, 0.0f,
     0.5f, 0.2f, 0.0f,
     0.5f, 0.4f, 0.0f
};

// 顶点着色器1

//! 1. "#version 450 core": 版本号; GLSL版本号要和OpenGL版本号匹配
//! 2. "layout(location = 0) in vec3 aPos;": 顶点属性;
//!     (1) glsl中,layout修饰符,(location = 0) 把顶点属性和特定缓冲区关联起来,并且顶点属性的识别号为0(创建顶点着色器处对比说明),
//!     (2) in:输入顶点属性
//!     (3) out:输出顶点属性
//!     (4) vec3是数据类型,三元组
//!     (5) aPos是定义的变量。即定义一个vec3类型变量aPos,将缓冲区0位置的顶点属性输入
//! 3. "gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0f);": 是GLSL的内建变量,会成为该顶点着色器的输出; 是一个4维向量(vec4),它包含了3个空间坐标(x, y, z)和齐次坐标(w), 通常将w设置为1.0
//! 4. "out vec4 FragColor":定义一个vec4变量FragColor, 并输出
//!
//! gl_Position设置的值会成为该顶点着色器的输出。由于我们的输入是一个3分量的向量,我们必须把它转换为4分量的向量(vec4)
//! 因为GLSL中没有一个3分量的向量的构造函数可以直接把一个vec3作为参数,所以我们必须先把vec3转换为vec4,然后再把它赋值给gl_Position
//! gl_Position 是一个vec4类型的输出变量,它会被转换为裁剪空间坐标
//! vec4是一个4维向量,它包含了3个空间坐标(x, y, z),还有一个齐次坐标(w), 通常将w设置为1.0
//! 裁剪空间坐标会被转换为屏幕空间坐标,同时也会被进行透视除法(Perspective Division) 除以w分量
//! 透视除法会将裁剪空间坐标的xyz分量分别除以w分量,最终得到的结果就是标准化设备坐标(Normalized Device Coordinates)
const char *vertexShaderSource =
    "#version 450 core\n"
    "layout(location = 0) in vec3 aPos;\n"
    "void main()\n"
    "{\n"
    "gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0f);\n"
    "}\n\0";

//片段着色器1

const char *fragmentShaderSource =
    "#version 450 core\n"
    "out vec4 FragColor;\n"
    "void main()\n"
    "{\n"
    "FragColor = vec4(0.4f, 0.6f, 0.8f, 1.0f);\n"
    "}\n\0";

2. 顶点着色器2

用到的矩阵:坐标矩阵(包含坐标和颜色)、索引矩阵

float vertices_data2[72] = {
    // 所有的值是在[-1, 1]之间的
    //坐标               //颜色
    -0.5f, -0.2f, 0.0f, 0.0f, 0.0f, 0.0f,
    -0.5f, -0.4f, 0.0f, 1.0f, 0.0f, 0.0f,
    -0.3f, -0.4f, 0.0f, 0.0f, 1.0f, 0.0f,
    -0.3f, -0.2f, 0.0f, 1.1f, 1.0f, 0.0f,

    -0.1f, -0.2f, 0.0f, 0.0f, 0.0f, 1.0f,
    -0.1f, -0.4f, 0.0f, 1.0f, 0.0f, 1.0f,
     0.1f, -0.4f, 0.0f, 0.0f, 1.0f, 1.0f,
     0.1f, -0.2f, 0.0f, 1.0f, 1.0f, 1.0f,

     0.3f, -0.2f, 0.0f, 0.0f, 0.0f, 0.0f,
     0.3f, -0.4f, 0.0f, 1.0f, 0.0f, 0.0f,
     0.5f, -0.4f, 0.0f, 0.0f, 1.0f, 0.0f,
     0.5f, -0.2f, 0.0f, 1.0f, 1.0f, 0.0f
};

//控制绘制顺序
GLuint vertex_index[20] = {
    0, 1, 2, 3,
    2, 3, 4, 5,
    4, 5, 6, 7,
    6, 7, 8, 9,
    8, 9, 10,11
};//!

// 顶点着色器2
//! 顶点着色器从矩阵中读取颜色,通过"out vec3 mcolor;"输出给片段着色器
注意此处的layout(location = 0)和layout(location = 1),对应下文解析数据时的参数

const char *vertexShaderSource2 =
    "#version 450 core\n"
    "layout(location = 0) in vec3 aPos;\n"
    "layout(location = 1) in vec3 aColor;\n"
    "out vec3 mcolor;\n"
    "void main()\n"
    "{\n"
    "gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0f);\n"
    "mcolor = aColor;\n"
    "}\n\0";

//片段着色器2 接收顶点着色器输出的颜色

const char *fragmentShaderSource2 =
    "#version 450 core\n"
    "out vec4 FragColor;\n"
    "in vec3 mcolor;\n"
    "void main()\n"
    "{\n"
        "FragColor = vec4(mcolor, 1.0f);\n"
    "}\n\0";

二、使用步骤

1. 使用着色器1

void MOpenGLWidgetShader::initializeGL()
{
    initializeOpenGLFunctions();
    glClearColor(0.0, 0.2, 0.3, 1.0);

    // ------------------------一、VAO和VBO------------------------
    // 1.创建VAO和VBO对象,并赋予ID(使用Gen)
    glGenVertexArrays(1, &VAO_id);
    glGenBuffers(1, &VBO_id);

    // 2.绑定VAO,开始记录属性相关
    glBindVertexArray(VAO_id);

    // 3.绑定VBO(一定是先绑定VAO再绑定VBO)
    glBindBuffer(GL_ARRAY_BUFFER, VBO_id);

    // 4.把数据放进VBO
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_data), vertices_data, GL_STATIC_DRAW);

    // 5.解析数据
    //! glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer)
    //! index 指定要配置的顶点属性的编号。
    //! size 指定每个顶点属性的分量数(1、2、3 或 4,就像向量的维度一样)。
    //! type 指定每个分量的数据类型,可以是 GL_BYTE、GL_UNSIGNED_BYTE、GL_SHORT、GL_UNSIGNED_SHORT、GL_INT、GL_UNSIGNED_INT、GL_FLOAT 或 GL_DOUBLE。
    //! normalized1 指定是否将数据归一化到 [0,1] 或 [-1,1] 范围内。
    //! stride (步长)指定连续两个顶点属性间的字节数。如果为 0,则表示顶点属性是紧密排列的。
    //! pointer 指向缓冲对象中第一个顶点属性的第一个分量的地址。(offset的作用)
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);

    // 6.开启location = 0的属性解析(就是上面顶点着色器处未说明的)
    glEnableVertexAttribArray(0);
    // 7.解绑VBO和VAO;  绑定一个ID为0,也就是不存在的ID,相当于接绑定(绑定新的ID,之前绑定的也就解除了)
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
    // -----------------------------end---------------------------


    // ------------------------二、着色器相关------------------------
    // 1.创建顶点着色器
    unsigned int vertexShader_id = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader_id, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader_id);

    // 2.创建片段着色器
    unsigned int fragmentShader_id = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader_id, 1, &fragmentShaderSource, NULL);
    //编译代码
    glCompileShader(fragmentShader_id);
    // -----------------------------end---------------------------

    // -----------------------三、着色器程序相关----------------------
    shaderProgram1_id = glCreateProgram();
    glAttachShader(shaderProgram1_id, vertexShader_id);
    glAttachShader(shaderProgram1_id, fragmentShader_id);
    glLinkProgram(shaderProgram1_id);
    // -----------------------------end---------------------------

    // 删除顶点着色器和片段着色器(不能将着色器程序也给delete掉)
    glDeleteShader(vertexShader_id);
    glDeleteShader(fragmentShader_id);
}
void MOpenGLWidgetShader::paintGL()
{
    //清除屏幕
    glClear(GL_COLOR_BUFFER_BIT);

    // 【画一个图形时需要说使用哪个着色器】
    glUseProgram(shaderProgram1_id);
    // 使用时还需要再绑定一次
    glBindVertexArray(VAO_id);

    // 开始绘制
    //! GL_APICALL void GL_APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count);
    //! first,从数组缓存中的哪一位开始绘制,一般为0。
    //! count,数组中顶点的数量。
    glPointSize(8);
    glDrawArrays(GL_POINTS, 0, 12);
    glDrawArrays(GL_QUADS, 0, 12);
}

结果如图

在这里插入图片描述

2. 使用着色器2

void MOpenGLWidgetShader::initializeGL()
{
    initializeOpenGLFunctions();
    glClearColor(0.0, 0.2, 0.3, 1.0);
    
    //***********************************************************************
    //***********************************************************************
    //!                          VAO2、VBO2、EBO2
    //***********************************************************************
    //***********************************************************************
    glGenVertexArrays(1, &VAO2_id);
    glBindVertexArray(VAO2_id);

    glGenBuffers(1, &VBO2_id);
    glBindBuffer(GL_ARRAY_BUFFER, VBO2_id);

//    glGenBuffers(1, &EBO2_id);
//    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO2_id);

    //读取矩阵数据到VBO2
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_data2), vertices_data2, GL_STATIC_DRAW);
    //! 告诉OpenGL该如何解析顶点数据(应用到逐个顶点属性上)。
    //! 参数1:要配置的顶点属性(layout(location = 0),故0),
    //! 参数2:指定顶点属性的大小(vec3,故3),
    //! 参数3:指定数据的类型,
    //! 参数4:是否希望数据被标准化,
    //! 参数5:在连续的顶点属性组之间的间隔,6*sizeof(float)
    //! 参数6:表示位置数据在缓冲中起始位置的偏移量(Offset),0*sizeof(float)。
    // 解析数据:按layout(location = 0)解析,每6位一组,每组偏移0处读取3位
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    //! 参数1:要配置的顶点属性(layout(location = 1),故1)
    //! 参数2:指定顶点属性的大小(vec3,故3),
    //! 参数3:指定数据的类型,
    //! 参数4:是否希望数据被标准化,
    //! 参数5:在连续的顶点属性组之间的间隔,
    //! 参数6:表示位置数据在缓冲中起始位置的偏移量(Offset), 3*sizeof(float)。
    // 解析数据:按layout(location = 1)解析,每6位一组,每组偏移3处读取3位
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3*sizeof(float)));
    glEnableVertexAttribArray(1);

//    //加载索引矩阵数据; GL_ELEMENT_ARRAY_BUFFER
//    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(vertex_index), vertex_index, GL_STATIC_DRAW);

    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
//    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);


    // ------------------------二、着色器相关------------------------
    // 创建顶点着色器
    unsigned int vertexShader2_id = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader2_id, 1, &vertexShaderSource2, NULL);
    glCompileShader(vertexShader2_id);

    // 创建片段着色器
    unsigned int fragmentShader2_id = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader2_id, 1, &fragmentShaderSource2, NULL);
    glCompileShader(fragmentShader2_id);

    //判断着顶点色器编译是否成功
    int success;
    char infoLog[512];
    glGetShaderiv(vertexShader2_id, GL_COMPILE_STATUS, &success);
    if(!success)
    {
        glGetShaderInfoLog(vertexShader2_id, 512, NULL, infoLog);
        qDebug() << "ERROR::VERTEX::COMPILATION_FAILED\n" << infoLog;
    };

    //判断着片段色器编译是否成功
    glGetShaderiv(fragmentShader2_id, GL_COMPILE_STATUS, &success);
    if(!success)
    {
        glGetShaderInfoLog(fragmentShader2_id, 512, NULL, infoLog);
        qDebug() << "ERROR::SHADER::COMPILATION_FAILED\n" << infoLog;
    };
    // -----------------------------end---------------------------

    // -----------------------三、着色器程序相关----------------------
    shaderProgram2_id = glCreateProgram();
    glAttachShader(shaderProgram2_id, vertexShader2_id);
    glAttachShader(shaderProgram2_id, fragmentShader2_id);
    glLinkProgram(shaderProgram2_id);

    //判断着片段色器编译是否成功
    glGetShaderiv(shaderProgram2_id, GL_LINK_STATUS, &success);
    if(!success)
    {
        glGetShaderInfoLog(shaderProgram2_id, 512, NULL, infoLog);
        qDebug() << "ERROR::PROGRAM::LINKING_FAILED\n" << infoLog;
    };
    // -----------------------------end---------------------------

    // 删除顶点着色器和片段着色器(不能将着色器程序也给delete掉)
    glDeleteShader(vertexShader2_id);
    glDeleteShader(fragmentShader2_id);
}
void MOpenGLWidgetShader::paintGL()
{
    //清除屏幕
    glClear(GL_COLOR_BUFFER_BIT);

    // 【画一个图形时需要说使用哪个着色器】
    glUseProgram(shaderProgram2_id);
    // 使用时还需要再绑定一次
    glBindVertexArray(VAO2_id);

    // 开始绘制
    //! GL_APICALL void GL_APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count);
    //! first,从数组缓存中的哪一位开始绘制,一般为0。
    //! count,数组中顶点的数量。
    glPointSize(8);
    glDrawArrays(GL_POINTS, 0, 12);
    glDrawArrays(GL_QUADS, 0, 12);
//    glDrawElements(GL_POINTS, 20, GL_UNSIGNED_INT, 0);
//    glDrawElements(GL_QUADS, 20, GL_UNSIGNED_INT, 0);
}

结果如图:

在这里插入图片描述

3. 在着色器2中使用EBO

将(二、2)中initializeGL()函数中被注释的如下代码取消注释:

glGenBuffers(1, &EBO2_id);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO2_id);

......

//加载索引矩阵数据; GL_ELEMENT_ARRAY_BUFFER
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(vertex_index), vertex_index, GL_STATIC_DRAW);

...
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

将(二、2)中paintGL()函数的如下代码替换注释

    glDrawArrays(GL_POINTS, 0, 12);
    glDrawArrays(GL_QUADS, 0, 12);
    //! 绘图类型 索引矩阵中有20个索引, 索引数据类型是无符号整形
//    glDrawElements(GL_POINTS, 20, GL_UNSIGNED_INT, 0);
//    glDrawElements(GL_QUADS, 20, GL_UNSIGNED_INT, 0);

替换为

//    glDrawArrays(GL_POINTS, 0, 12);
//    glDrawArrays(GL_QUADS, 0, 12);

    //! 绘图类型 索引矩阵中有20个索引, 索引数据类型是无符号整形
    glDrawElements(GL_POINTS, 20, GL_UNSIGNED_INT, 0);
    glDrawElements(GL_QUADS, 20, GL_UNSIGNED_INT, 0);

结果如图

在这里插入图片描述

三、完整代码

#ifndef MOPENGLWIDGETSHADER_H
#define MOPENGLWIDGETSHADER_H

#include <QObject>
#include <QWidget>
#include <GL/glu.h>
#include <QGL>
#include <QtOpenGL>
#include <QGLWidget>
#include <QOpenGLWidget>
#include <QOpenGLFunctions_4_5_Core>
#include <QGridLayout>
#include <QDebug>


class MOpenGLWidgetShader : public QOpenGLWidget, QOpenGLFunctions_4_5_Core
{
    Q_OBJECT
public:
    explicit MOpenGLWidgetShader(QWidget* parent = nullptr);

    // 顶点的数据:没有解析的数据是没有意义的
    // 内存中的数据,关键是如何将内存中的数据给显卡


    float vertices_data[36] = {
        // 所有的值是在[-1, 1]之间的
        -0.5f, 0.4f, 0.0f,
        -0.5f, 0.2f, 0.0f,
        -0.3f, 0.2f, 0.0f,
        -0.3f, 0.4f, 0.0f,

        -0.1f, 0.4f, 0.0f,
        -0.1f, 0.2f, 0.0f,
         0.1f, 0.2f, 0.0f,
         0.1f, 0.4f, 0.0f,

         0.3f, 0.4f, 0.0f,
         0.3f, 0.2f, 0.0f,
         0.5f, 0.2f, 0.0f,
         0.5f, 0.4f, 0.0f
    };

    float vertices_data2[72] = {
        // 所有的值是在[-1, 1]之间的
        //坐标               //颜色
        -0.5f, -0.2f, 0.0f, 0.0f, 0.0f, 0.0f,
        -0.5f, -0.4f, 0.0f, 1.0f, 0.0f, 0.0f,
        -0.3f, -0.4f, 0.0f, 0.0f, 1.0f, 0.0f,
        -0.3f, -0.2f, 0.0f, 1.1f, 1.0f, 0.0f,

        -0.1f, -0.2f, 0.0f, 0.0f, 0.0f, 1.0f,
        -0.1f, -0.4f, 0.0f, 1.0f, 0.0f, 1.0f,
         0.1f, -0.4f, 0.0f, 0.0f, 1.0f, 1.0f,
         0.1f, -0.2f, 0.0f, 1.0f, 1.0f, 1.0f,

         0.3f, -0.2f, 0.0f, 0.0f, 0.0f, 0.0f,
         0.3f, -0.4f, 0.0f, 1.0f, 0.0f, 0.0f,
         0.5f, -0.4f, 0.0f, 0.0f, 1.0f, 0.0f,
         0.5f, -0.2f, 0.0f, 1.0f, 1.0f, 0.0f
    };

    //控制绘制顺序
    GLuint vertex_index[20] = {
        0, 1, 2, 3,
        2, 3, 4, 5,
        4, 5, 6, 7,
        6, 7, 8, 9,
        8, 9, 10,11
    };

    // VAO和VBO变量(无符号整型)
    unsigned int VAO_id,VBO_id;
    // 着色器变量
    unsigned int shaderProgram1_id;

    // VAO和VBO变量(无符号整型)、索引缓冲对象
    unsigned int VAO2_id,VBO2_id, EBO2_id;
    // 着色器变量
    unsigned int shaderProgram2_id;
public:


protected:


protected:
    virtual void initializeGL();
    virtual void resizeGL(int w, int h);
    virtual void paintGL();

protected:
    virtual void mousePressEvent(QMouseEvent *event);
    virtual void mouseReleaseEvent(QMouseEvent *event);
    virtual void mouseMoveEvent(QMouseEvent *event);
    virtual void wheelEvent(QWheelEvent *event);
};


#endif // MOPENGLWIDGETSHADER_H
#include "mopenglwidgetshader.h"


//! 1. "#version 450 core": 版本号; GLSL版本号要和OpenGL版本号匹配
//! 2. "layout(location = 0) in vec3 aPos;": 顶点属性;
//!     (1) glsl中,layout修饰符,(location = 0) 把顶点属性和特定缓冲区关联起来,并且顶点属性的识别号为0(创建顶点着色器处对比说明),
//!     (2) in:输入顶点属性
//!     (3) out:输出顶点属性
//!     (4) vec3是数据类型,三元组
//!     (5) aPos是定义的变量。即定义一个vec3类型变量aPos,将缓冲区0位置的顶点属性输入
//! 3. "gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0f);": 是GLSL的内建变量,会成为该顶点着色器的输出; 是一个4维向量(vec4),它包含了3个空间坐标(x, y, z)和齐次坐标(w), 通常将w设置为1.0
//! 4. "out vec4 FragColor":定义一个vec4变量FragColor, 并输出
//!
//! gl_Position设置的值会成为该顶点着色器的输出。由于我们的输入是一个3分量的向量,我们必须把它转换为4分量的向量(vec4)
//! 因为GLSL中没有一个3分量的向量的构造函数可以直接把一个vec3作为参数,所以我们必须先把vec3转换为vec4,然后再把它赋值给gl_Position
//! gl_Position 是一个vec4类型的输出变量,它会被转换为裁剪空间坐标
//! vec4是一个4维向量,它包含了3个空间坐标(x, y, z),还有一个齐次坐标(w), 通常将w设置为1.0
//! 裁剪空间坐标会被转换为屏幕空间坐标,同时也会被进行透视除法(Perspective Division) 除以w分量
//! 透视除法会将裁剪空间坐标的xyz分量分别除以w分量,最终得到的结果就是标准化设备坐标(Normalized Device Coordinates)

// 顶点着色器1
const char *vertexShaderSource =
    "#version 450 core\n"
    "layout(location = 0) in vec3 aPos;\n"
    "void main()\n"
    "{\n"
    "gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0f);\n"
    "}\n\0";

//片段着色器1
const char *fragmentShaderSource =
    "#version 450 core\n"
    "out vec4 FragColor;\n"
    "void main()\n"
    "{\n"
    "FragColor = vec4(0.4f, 0.6f, 0.8f, 1.0f);\n"
    "}\n\0";

//!
//! 顶点着色器从矩阵中读取颜色,通过"out vec3 mcolor;"输出给片段着色器
//! //!
// 顶点着色器2
const char *vertexShaderSource2 =
    "#version 450 core\n"
    "layout(location = 0) in vec3 aPos;\n"
    "layout(location = 1) in vec3 aColor;\n"
    "out vec3 mcolor;\n"
    "void main()\n"
    "{\n"
    "gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0f);\n"
    "mcolor = aColor;\n"
    "}\n\0";

//片段着色器2
const char *fragmentShaderSource2 =
    "#version 450 core\n"
    "out vec4 FragColor;\n"
    "in vec3 mcolor;\n"
    "void main()\n"
    "{\n"
        "FragColor = vec4(mcolor, 1.0f);\n"
    "}\n\0";





MOpenGLWidgetShader::MOpenGLWidgetShader(QWidget *parent)
    : QOpenGLWidget{parent}
{

}

void MOpenGLWidgetShader::initializeGL()
{
    initializeOpenGLFunctions();
    glClearColor(0.0, 0.2, 0.3, 1.0);

    // ------------------------一、VAO和VBO------------------------
    // 1.创建VAO和VBO对象,并赋予ID(使用Gen)
    glGenVertexArrays(1, &VAO_id);
    glGenBuffers(1, &VBO_id);

    // 2.绑定VAO,开始记录属性相关
    glBindVertexArray(VAO_id);

    // 3.绑定VBO(一定是先绑定VAO再绑定VBO)
    glBindBuffer(GL_ARRAY_BUFFER, VBO_id);

    // 4.把数据放进VBO
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_data), vertices_data, GL_STATIC_DRAW);

    // 5.解析数据
    //! glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer)
    //! index 指定要配置的顶点属性的编号。
    //! size 指定每个顶点属性的分量数(1、2、3 或 4,就像向量的维度一样)。
    //! type 指定每个分量的数据类型,可以是 GL_BYTE、GL_UNSIGNED_BYTE、GL_SHORT、GL_UNSIGNED_SHORT、GL_INT、GL_UNSIGNED_INT、GL_FLOAT 或 GL_DOUBLE。
    //! normalized1 指定是否将数据归一化到 [0,1] 或 [-1,1] 范围内。
    //! stride (步长)指定连续两个顶点属性间的字节数。如果为 0,则表示顶点属性是紧密排列的。
    //! pointer 指向缓冲对象中第一个顶点属性的第一个分量的地址。(offset的作用)
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);

    // 6.开启location = 0的属性解析(就是上面顶点着色器处未说明的)
    glEnableVertexAttribArray(0);
    // 7.解绑VBO和VAO;  绑定一个ID为0,也就是不存在的ID,相当于接绑定(绑定新的ID,之前绑定的也就解除了)
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
    // -----------------------------end---------------------------


    // ------------------------二、着色器相关------------------------
    // 1.创建顶点着色器
    unsigned int vertexShader_id = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader_id, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader_id);

    // 2.创建片段着色器
    unsigned int fragmentShader_id = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader_id, 1, &fragmentShaderSource, NULL);
    //编译代码
    glCompileShader(fragmentShader_id);
    // -----------------------------end---------------------------

    // -----------------------三、着色器程序相关----------------------
    shaderProgram1_id = glCreateProgram();
    glAttachShader(shaderProgram1_id, vertexShader_id);
    glAttachShader(shaderProgram1_id, fragmentShader_id);
    glLinkProgram(shaderProgram1_id);
    // -----------------------------end---------------------------

    // 删除顶点着色器和片段着色器(不能将着色器程序也给delete掉)
    glDeleteShader(vertexShader_id);
    glDeleteShader(fragmentShader_id);



    //***********************************************************************
    //***********************************************************************
    //!                          VAO2、VBO2、EBO2
    //***********************************************************************
    //***********************************************************************
    glGenVertexArrays(1, &VAO2_id);
    glBindVertexArray(VAO2_id);

    glGenBuffers(1, &VBO2_id);
    glBindBuffer(GL_ARRAY_BUFFER, VBO2_id);

    glGenBuffers(1, &EBO2_id);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO2_id);

    //读取矩阵数据到VBO2
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_data2), vertices_data2, GL_STATIC_DRAW);
    //! 告诉OpenGL该如何解析顶点数据(应用到逐个顶点属性上)。
    //! 参数1:要配置的顶点属性(layout(location = 0),故0),
    //! 参数2:指定顶点属性的大小(vec3,故3),
    //! 参数3:指定数据的类型,
    //! 参数4:是否希望数据被标准化,
    //! 参数5:在连续的顶点属性组之间的间隔,6*sizeof(float)
    //! 参数6:表示位置数据在缓冲中起始位置的偏移量(Offset),0*sizeof(float)。
    // 解析数据:按layout(location = 0)解析,每6位一组,每组偏移0处读取3位
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    //! 参数1:要配置的顶点属性(layout(location = 1),故1)
    //! 参数2:指定顶点属性的大小(vec3,故3),
    //! 参数3:指定数据的类型,
    //! 参数4:是否希望数据被标准化,
    //! 参数5:在连续的顶点属性组之间的间隔,
    //! 参数6:表示位置数据在缓冲中起始位置的偏移量(Offset), 3*sizeof(float)。
    // 解析数据:按layout(location = 1)解析,每6位一组,每组偏移3处读取3位
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3*sizeof(float)));
    glEnableVertexAttribArray(1);

    //加载索引矩阵数据; GL_ELEMENT_ARRAY_BUFFER
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(vertex_index), vertex_index, GL_STATIC_DRAW);

    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);


    // ------------------------二、着色器相关------------------------
    // 创建顶点着色器
    unsigned int vertexShader2_id = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader2_id, 1, &vertexShaderSource2, NULL);
    glCompileShader(vertexShader2_id);

    // 创建片段着色器
    unsigned int fragmentShader2_id = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader2_id, 1, &fragmentShaderSource2, NULL);
    glCompileShader(fragmentShader2_id);

    //判断着顶点色器编译是否成功
    int success;
    char infoLog[512];
    glGetShaderiv(vertexShader2_id, GL_COMPILE_STATUS, &success);
    if(!success)
    {
        glGetShaderInfoLog(vertexShader2_id, 512, NULL, infoLog);
        qDebug() << "ERROR::VERTEX::COMPILATION_FAILED\n" << infoLog;
    };

    //判断着片段色器编译是否成功
    glGetShaderiv(fragmentShader2_id, GL_COMPILE_STATUS, &success);
    if(!success)
    {
        glGetShaderInfoLog(fragmentShader2_id, 512, NULL, infoLog);
        qDebug() << "ERROR::SHADER::COMPILATION_FAILED\n" << infoLog;
    };
    // -----------------------------end---------------------------

    // -----------------------三、着色器程序相关----------------------
    shaderProgram2_id = glCreateProgram();
    glAttachShader(shaderProgram2_id, vertexShader2_id);
    glAttachShader(shaderProgram2_id, fragmentShader2_id);
    glLinkProgram(shaderProgram2_id);

    //判断着片段色器编译是否成功
    glGetShaderiv(shaderProgram2_id, GL_LINK_STATUS, &success);
    if(!success)
    {
        glGetShaderInfoLog(shaderProgram2_id, 512, NULL, infoLog);
        qDebug() << "ERROR::PROGRAM::LINKING_FAILED\n" << infoLog;
    };
    // -----------------------------end---------------------------

    // 删除顶点着色器和片段着色器(不能将着色器程序也给delete掉)
    glDeleteShader(vertexShader2_id);
    glDeleteShader(fragmentShader2_id);

}

void MOpenGLWidgetShader::resizeGL(int w, int h)
{
    //防止height为0
    if (h == 0) { h = 1; }
    //重置当前的视口
    glViewport(0, 0,(GLint)w, (GLint)h);

}


void MOpenGLWidgetShader::paintGL()
{
    //清除屏幕
    glClear(GL_COLOR_BUFFER_BIT);

    // 【画一个图形时需要说使用哪个着色器】
    glUseProgram(shaderProgram2_id);
    // 使用时还需要再绑定一次
    glBindVertexArray(VAO2_id);

    // 开始绘制
    //! GL_APICALL void GL_APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count);
    //! first,从数组缓存中的哪一位开始绘制,一般为0。
    //! count,数组中顶点的数量。
    glPointSize(8);
//    glDrawArrays(GL_POINTS, 0, 12);
//    glDrawArrays(GL_QUADS, 0, 12);
    //! 绘图类型 索引矩阵中有20个索引, 索引数据类型是无符号整形
    glDrawElements(GL_POINTS, 20, GL_UNSIGNED_INT, 0);
    glDrawElements(GL_QUADS, 20, GL_UNSIGNED_INT, 0);
}

void MOpenGLWidgetShader::wheelEvent(QWheelEvent *event)
{
    QOpenGLWidget::wheelEvent(event);

}

void MOpenGLWidgetShader::mousePressEvent(QMouseEvent *event)
{
    QOpenGLWidget::mousePressEvent(event);

}

void  MOpenGLWidgetShader::mouseReleaseEvent(QMouseEvent *event)
{
    QOpenGLWidget::mouseReleaseEvent(event);
}

void MOpenGLWidgetShader::mouseMoveEvent(QMouseEvent *event)
{
    QOpenGLWidget::mouseMoveEvent(event);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1210065.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【仙逆】王林400年晋升元婴,复仇藤化元杀尽藤姓,高老畏罪自裁

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析国漫资讯。 深度爆料仙逆第10集剧情解析&#xff0c;高启明&#xff0c;缥缈宗的元婴初期老祖&#xff0c;一生潜心研究推演之术&#xff0c;洞察先机&#xff0c;乃是宗门之人的精神支柱。藤化元曾为寻找王林父母所在之…

【数据结构】二叉树经典例题---<你真的掌握二叉树了吗?>(第二弹)

本次选题都为选择题。涉及到二叉树总结点和叶子结点的计算、二叉树的基本性质、根据二叉树的前序/后序和中序遍历画出二叉树、哈夫曼树等等…希望对你有帮助哦~&#x1f61d; 1.若一颗二叉树具有10个度为2的结点&#xff0c;5个度为1的结点&#xff0c;则度为0的结点个数为() A…

高防CDN:构筑网络安全的钢铁长城

在当今数字化的世界里&#xff0c;网络安全问题日益突显&#xff0c;而高防CDN&#xff08;高防御内容分发网络&#xff09;正如一座坚不可摧的钢铁长城&#xff0c;成为互联网安全的不可或缺之物。本文将深入剖析高防CDN在网络安全环境中的关键作用&#xff0c;探讨其如何构筑…

C++二分查找算法:最大为 N 的数字组合

涉及知识点 二分查找 数学 题目 给定一个按 非递减顺序 排列的数字数组 digits 。你可以用任意次数 digits[i] 来写的数字。例如&#xff0c;如果 digits [‘1’,‘3’,‘5’]&#xff0c;我们可以写数字&#xff0c;如 ‘13’, ‘551’, 和 ‘1351315’。 返回 可以生成的…

磁带标签设计:Tape Label Studio 2023.11.0.7 Crack

Tape Label Studio&#xff08;磁带标签设计&#xff09; 为标签创建颜色样式。修改标签中使用的每种颜色&#xff0c;包括背景、条形码、边框、文本和字符颜色。自定义边框样式以适合您正在使用的标签。从实心、虚线或虚线边框中进行选择。轻松调整宽度和宽度。Tape Label St…

网络运维Day17

文章目录 什么是数据库MySQL介绍实验环境准备构建MySQL服务连接数据库修改root密码 数据库基础常用的SQL命令分类SQL命令使用规则MySQL基本操作创建库创建表查看表结构 记录管理命令 数据类型数值类型 数据类型日期时间类型时间函数案例枚举类型 约束条件案例修改表结构添加新字…

Activiti工作流学习笔记(四)——工作流引擎中责任链模式的建立与应用原理

原创/朱季谦 本文需要一定责任链模式的基础与Activiti工作流知识&#xff0c;主要分成三部分讲解&#xff1a; 一、简单理解责任链模式概念二、Activiti工作流里责任链模式的建立三、Activiti工作流里责任链模式的应用 一、简单理解责任链模式概念 网上关于责任链模式的介绍…

关于Chrome中F12调试Console输入多行

在chrome 浏览器中使用console调试的时&#xff0c;如果想在console中输入多行代码&#xff0c;需要进行换行。 这时我们可以使用 [ Shift Enter ] 。也叫&#xff1a; 软回车。

Netty Review - 从BIO到NIO的进化推演

文章目录 BIODEMO 1DEMO 2小结论单线程BIO的缺陷BIO如何处理并发多线程BIO服务器的弊端 NIONIO要解决的问题模拟NIO方案一&#xff1a; &#xff08;等待连接时和等待数据时不阻塞&#xff09;方案二&#xff08;缓存Socket&#xff0c;轮询数据是否准备好&#xff09;方案二存…

【NI-DAQmx入门】触发相关

触发概述 触发采集为用户提供了两个主要好处&#xff1a;它对输入信号相对于触发事件进行计时&#xff0c;因此用户仅捕获感兴趣区域中的信号&#xff0c;从而节省硬件带宽和内存。 模拟触发和数字触发 模拟触发和数字触发的区别在于触发源的不同。数字触发是一种 TTL 信号&am…

C++实现ransac

目录 一、ransac算法原理 1.1、算法概念 1.2、图解 二、c实现ransac 2.1、设置随机样本和离群点 2.2、随机抽取样本 2.3、内点计算 2.4、更新参数 2.2、完整代码 一、ransac算法原理 1.1、算法概念 随机抽样一致性 (RANSAC) 是一种迭代方法&#xff0c;用于根据一组包…

225.用队列实现栈(LeetCode)

思路 思路&#xff1a;用两个队列实现栈后进先出的特性 &#xff0c;两个队列为空时&#xff0c;先将数据都导向其中一个队列。 当要模拟出栈时&#xff0c;将前面的元素都导入另一个空队列&#xff0c;再将最后一个元素移出队列 实现 实现&#xff1a; 因为C语言没有库可以…

【每日一题】—— D. Epic Transformation(Codeforces Round 710 (Div. 3))(找规律+贪心)

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;每日一题 &#x1f48c;其他专栏&#xff1a; &#x1f534; 每日反刍 &#x1f7e1; C跬步积累 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓称…

Linux socket编程(3):利用fork实现服务端与多个客户端建立连接

上一节&#xff0c;我们实现了一个客户端/服务端的Socket通信的代码&#xff0c;在这个例子中&#xff0c;客户端连接上服务端后发送一个字符串&#xff0c;而服务端接收到字符串并打印出来后就关闭所有套接字并退出了。 上一节的代码较为简单&#xff0c;在实际的应用中&…

揭秘Vue中的nextTick:异步更新队列背后的技术原理大揭秘!

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 ⭐ 专栏简介 &#x1f4d8; 文章引言 一、N…

解码方法00

题目链接 解码方法 题目描述 注意点 s 只包含数字&#xff0c;并且可能包含前导零计算并返回 解码 方法的 总数 解答思路 使用动态规划解决本题&#xff0c;其思路为&#xff1a;从后往前遍历字符串&#xff0c;遍历到任一i位置的字符c时&#xff0c;有几种情况&#xff1…

react中间件的理解

一、是什么&#xff1f; 中间件&#xff08;Middleware&#xff09;在计算机中&#xff0c;是介于应用系统和系统软件之间的一类软件&#xff0c;它使用系统软件所提供的基础服务&#xff08;功能&#xff09;&#xff0c;衔接网络应用上的各个部分或不同的应用&#xff0c;能…

ubuntu20安装opencv4和opencv_contrib 多版本共存

openCV 卸载 openCV 安装后的源码尽可能保留&#xff0c;因为可以直接从build文件夹下卸载已经安装的openCV. 参考链接&#xff1a;视觉学习笔记10——opencv的卸载、安装与多版本管理 如果已经安装完openCV,后续想重新装&#xff0c;需要先卸载掉安装的openCV. 在ubuntu终端…

离散卡尔曼滤波器算法详解及重要参数(Q、R、P)的讨论

公开数据集中文版详细描述参考前文&#xff1a;https://editor.csdn.net/md/?not_checkout1&spm1011.2124.3001.6192神经元Spike信号分析参考前文&#xff1a;https://blog.csdn.net/qq_43811536/article/details/134359566?spm1001.2014.3001.5501神经元运动调制分析参考…

安防监控EasyCVR视频汇聚平台使用海康SDK播放出现花屏是什么原因?

视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同&#xff0c;支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。音视频流媒体视频平台EasyCVR拓展性强&#xff0c;视频能力丰富&#xff0c;具体可实现视频监控直播、视频轮播、视频录像、…