GL01-08:OpenGL顶点属性修改函数

本文主要使用顶点属性与GLSL输入参数的说明,同时给出了一个OpenGL2.0种顶点属性的疑惑。
  1. OpenGL2.1 下为什么修改顶点属性值后,需要启动多个顶点属性才有效果?难道OpenGL有默认着色器?
  2. OpenGL4.1下的顶点数组、顶点属性与着色器得使用代码。


一、OpenGL 2.1 绘制图元代码

#include 
#include 
#include 
#include 
#include 

GLFWwindow * contextInit();         // 上下文初始化
void loadOpenGL();                  // 加载OpenGL库 
GLuint bufferData();                // 顶点数据
GLuint bufferIndices();             // 顶点索引数据
void render();                      // 图元绘制

double oldTime;
int main(int argc, const char** argv) {
    GLFWwindow *win =contextInit();
    loadOpenGL();
    bufferData();
    bufferIndices();
    oldTime = glfwGetTime();
    while(!glfwWindowShouldClose(win)){
        if(glfwGetTime() - oldTime > 0.1){
            render();
            glfwSwapBuffers(win);
            oldTime = glfwGetTime();
        }
        // glfwWaitEvents();
        glfwPollEvents();
    }
    glfwDestroyWindow(win);
    glfwTerminate();
    return 0;
}

GLFWwindow * contextInit(){
    glfwInit();         // 初始化GLFW
    GLFWwindow *window = glfwCreateWindow(600,400,"OpenGL图元", NULL, NULL); // 创建窗体,也是创建上下文
    glfwMakeContextCurrent(window);  // 射设置当前上下文
    return window;
}
void loadOpenGL(){
    glewInit();
    // glEnable(GL_PROGRAM_POINT_SIZE);
    // glPointSize(5.0f);
    // glLineWidth(4.0f);
}
GLuint bufferData(){
    float vertices[] = {            // 5边形(顺时针:注意点的顺序)
        -0.5f,  0.5f, -0.5f,         // 第一个顶点  
        -0.5f,  0.5f,  0.5f,         // 第二个顶点
         0.5f,  0.5f,  0.5f,         // 第三个顶点
         0.5f,  0.5f, -0.5f,         // 第四个顶点
         0.5f, -0.5f, -0.5f,         // 第五个顶点
        -0.5f, -0.5f, -0.5f,         // 第六个顶点
        -0.5f, -0.5f,  0.5f,         // 第七个顶点
         0.5f, -0.5f,  0.5f,         // 第八个顶点
    }; 
    
    GLuint vertexBuffer;
    glGenBuffers(1, &vertexBuffer);  // 顶点缓冲区
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); // 指定缓冲区类型
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);  // 拷贝数据到顶点缓冲区

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);  // 修改
    // int idx;
    // glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &idx);  // 顶点属性最大个数:16
    // printf("::%d\n",idx);
    // int num;
    // glGetVertexAttribiv(1,  GL_VERTEX_ATTRIB_ARRAY_SIZE, &num);
    // printf("num:%d\n", num);
    // 开启顶点缓冲区与属性
    glEnableVertexAttribArray(0);  // 位置顶点属性
    glEnableVertexAttribArray(1);  // 颜色位置属性  // 不开启,等于颜色属性没有!
 
    
    return  vertexBuffer;   // 返回是因为便于关闭与释放(这里结束应用就直接释放)
}
GLuint bufferIndices(){
    unsigned int indices[] = { // 正方体的12条边的索引
         0,1, 1,2, 2,3, 3,0, 0,5, 5,6, 6,7, 7,4, 4,5, 4,3, 2,7, 1,6
    };
    GLuint indexBuffer;
    glGenBuffers(1, &indexBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);  // 指定索引缓冲的类型
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);   // 拷贝索引数据
    return  indexBuffer;
}
void render(){
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    glRotatef(3.14f/4, 1.0f, 1.0f, 0.0f);
    glColor3f(1.0f, 1.0f, 0.0f);
    glViewport(0,0,600,400);   // 设置
    glDrawElements(GL_LINES, 24, GL_UNSIGNED_INT, 0);
    glViewport(600,400,600,400);
    glDrawElements(GL_LINES, 24, GL_UNSIGNED_INT, 0);
     

}
// 编译命令:g++ -omain  gl06_geometries_cube.cpp  -lglfw -lglew -framework opengl

1. 问题

  • 代码中只修改了一个顶点属性,旦需要启动两个顶点属性才有效果(这里没有使用GLSL着色器)
    1. 修改顶点属性
      • glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    2. 启用顶点属性
      • glEnableVertexAttribArray(0);
      • glEnableVertexAttribArray(1);

2. 运行效果

GL01-08:OpenGL顶点属性修改函数_第1张图片
OpenGL2.0没有使用GLSL的绘制

二、着色器的使用

1. 环境初始化的封装

  1. h文件common.h
#ifndef COMMON_H

#define COMMON_H
#include 
#include 
#include 
#include 
#include 

GLFWwindow* initContext();  // 上下文初始化
void destroyConext();
GLboolean initOpenGL();     // OpenGL初始化与加载
////////////////////////////////

#endif
  1. 实现文件common.c
#include "common.h"

// 上下文初始化
GLFWwindow* initContext(){
    if(!glfwInit()){
        printf("GLFW初始化失败!\n");
        return NULL;
    }
    // 设置提示
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
    GLFWwindow* win = glfwCreateWindow(600,400, "OpenGL着色器", NULL, NULL);
    if(! win){
        printf("创建窗体失败!\n");
        return NULL;
    }
    // 设置当前调用线程的上下文为win;
    glfwMakeContextCurrent(win);
    return win;
}
void destroyConext(){
    glfwTerminate();
}
// OpenGL初始化与加载
GLboolean initOpenGL(){
    if(glewInit() != GLEW_OK){   // GLEW_OK:#define GLEW_OK 0
        printf("OpenGL加载失败!\n");
        return GL_FALSE;
    }
    return GL_TRUE;
}
  1. 说明
  • 代码中加载的是OpenGL4.1

2. 程序主结构

#include 
#include 
#include 
#include "common.h"

GLuint yqData();        // 数据准备
GLuint yqShader();      // GLSL

int main(int argc, char const *argv[]){
    GLFWwindow *win = initContext(); 
    if(!win){
        return -1;
    }
    if(!initOpenGL()){
        destroyConext();
        return -1;
    }
    GLuint arrayID = yqData();
    GLuint programmID = yqShader();
    while(!glfwWindowShouldClose(win)){
        glBindVertexArray(arrayID);     // 绑定顶点分组
        glUseProgram(programmID);       // 使用Shader
        glDrawArrays(GL_LINE_LOOP, 0, 3);
        glUseProgram(0);                // 解除使用Shader
        glBindVertexArray(0);           // 接触顶点分组
        glfwSwapBuffers(win);
        glfwWaitEvents();
    }
    destroyConext();
    return 0;
}

3. 数据准备

GLuint yqData(){
    // 使用顶点数组对数据分组
    GLuint arrayID;
    glGenVertexArrays(1, &arrayID);  // glCreateVertexArrays是4.5的函数(GenVertexArrays需要预分配空间)
    glBindVertexArray(arrayID);  // 切换到刚创建的顶点数据操作
    
    // 1. 数据
    GLfloat  vertices[] = {
        -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
         0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
         0.0f,  0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f,
    };
    // 2. 创建缓冲ID(官方文档称缓冲对象:用于存放顶点数据)
    GLuint bufferID;
    glGenBuffers(1, &bufferID);
    // 3. 绑定ID到目标(其实就是创建指定目标的数据空间)
    glBindBuffer(GL_ARRAY_BUFFER, bufferID);
    // 4. 拷贝数据到空间
    glBufferData(GL_ARRAY_BUFFER,sizeof(vertices), vertices, GL_STATIC_DRAW);

    // 5. 修改顶点属性
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(GLfloat), NULL);  // 顶点属性(输入):注意location=3,对应的顶点索引也是3
    glEnableVertexAttribArray(0);  // 修改0就开启0(这个0会传递给对应的GPU中GLSL程序)
    glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 7 * sizeof(GLfloat), (const void *)(3 * sizeof(GLfloat)));  // 顶点属性(输入):注意location=3,对应的顶点索引也是3
    // 上面的4不能使用GL_RGBA
    glEnableVertexAttribArray(1);  // 修改0就开启0(这个0会传递给对应的GPU中GLSL程序)

    // 关闭顶点分组的操作
    glBindVertexArray(0); // 要使用再切换回来
    return arrayID;
}

4. 着色器

GLuint yqShader(){
    const char *vertexShaderSource = ""
        "#version 410 core\n"                          // OpenGL版本,核心模式
        "layout (location = 0) in vec3 aPos;\n"        // 顶点属性(输入
        "layout (location = 1) in vec4 aColor;\n"      // 输入颜色
        "out vec4 vColor;\n"         // 申明一个输出变量
        "void main(){\n"
        "   "   //传递颜色到片着色器
        "   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
        "   vColor = aColor;\n"     // 输出到下一个着色器
        "}\0";   // 空字符

    const char *fragmentShaderSource = ""
        "#version 410 core\n"
        "out vec4 FragColor;\n"        // 颜色属性(输出变量)
        "in vec4 vColor;\n"            // 接收上面的输出,变量名与上面保持一致;
        "void main(){\n"
        "   FragColor = vColor;\n"   // 固定颜色输出
        "}\n\0";
    unsigned int vertexShader;
    vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);  

    unsigned int fragmentShader;
    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);

    unsigned int shaderProgram;
    shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    // glUseProgram(shaderProgram);

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
    return shaderProgram;
}

5. 运行效果

GL01-08:OpenGL顶点属性修改函数_第2张图片
使用顶点属性传递数据到着色器

6. 顶点属性的分组

  • 如果想绘制不同的图元,可以使用顶端数组对象来管理切换。

#include 
#include 
#include 
#include "common.h"

void yqData(GLuint *arrayIDs);      // 数据准备
void yqRender(GLuint vao);    // 渲染
void yqShader();
int main(int argc, char const *argv[]){
    GLFWwindow *win = initContext(); 
    if(!win){
        return -1;
    }
    if(!initOpenGL()){
        destroyConext();
        return -1;
    }
    GLuint ids[2];
    yqData(ids);
    yqShader();
    while(!glfwWindowShouldClose(win)){
        yqRender(ids[0]);   // 改变0为1试一下
        glfwSwapBuffers(win);
        glfwWaitEvents();
    }
    destroyConext();
    return 0;
}
void yqData(GLuint *arrayIDs){
    // 使用顶点数组对数据分组
    // GLuint arrayID;
    glGenVertexArrays(2, arrayIDs);  // glCreateVertexArrays是4.5的函数(GenVertexArrays需要预分配空间)
    glBindVertexArray(arrayIDs[0]);  // 切换到刚创建的顶点数据操作
    
    // 1. 数据
    GLfloat  vertices_1[] = {
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f,
        0.0f,  0.5f, 0.0f
    };
    // 2. 创建缓冲ID(官方文档称缓冲对象:用于存放顶点数据)
    GLuint bufferID_1;
    glGenBuffers(1, &bufferID_1);
    // 3. 绑定ID到目标(其实就是创建指定目标的数据空间)
    glBindBuffer(GL_ARRAY_BUFFER, bufferID_1);
    // 4. 拷贝数据到空间
    glBufferData(GL_ARRAY_BUFFER,sizeof(vertices_1), vertices_1, GL_STATIC_DRAW);

    // 5. 修改顶点属性
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);  // 0索引的顶点属性,表示顶点的格式
    glEnableVertexAttribArray(0);  // 修改0就开启0(这个0会传递给对应的GPU中GLSL程序)

    // 关闭顶点分组的操作
    glBindVertexArray(0); // 要使用再切换回来
    //////////////////////////////////////////////////////////
    glBindVertexArray(arrayIDs[1]); 
    GLfloat  vertices[] = {
        -0.5f, 0.5f, 0.0f,
        0.5f,  0.5f, 0.0f,
        0.0f,  - 0.5f, 0.0f
    };
    // 2. 创建缓冲ID(官方文档称缓冲对象:用于存放顶点数据)
    GLuint bufferID_2;
    glGenBuffers(1, &bufferID_2);
    // 3. 绑定ID到目标(其实就是创建指定目标的数据空间)
    glBindBuffer(GL_ARRAY_BUFFER, bufferID_2);
    // 4. 拷贝数据到空间
    glBufferData(GL_ARRAY_BUFFER,sizeof(vertices), vertices, GL_STATIC_DRAW);

    // 5. 修改顶点属性
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);  // 0索引的顶点属性,表示顶点的格式
    glEnableVertexAttribArray(0);  // 修改0就开启0(这个0会传递给对应的GPU中GLSL程序)
    glBindVertexArray(0);
    printf("hello\n");
}
void yqShader(){
    const char *vertexShaderSource = ""
        "#version 410 core\n"                          // OpenGL版本,核心模式
        "layout (location = 0) in vec3 aPos;\n"        // 顶点属性(输入)
        "void main(){\n"
        "   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
        "}\0";   // 空字符

    const char *fragmentShaderSource = ""
        "#version 410 core\n"
        "out vec4 FragColor;\n"        // 颜色属性(输出变量)
        "void main(){\n"
        "   FragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);\n"   // 固定颜色输出
        "}\n\0";
            // ********************************
    // 1. 顶点着色器对象
    unsigned int vertexShader;
    vertexShader = glCreateShader(GL_VERTEX_SHADER);
    // 2. 编译顶点着色器
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);  
    // --------------------------------
    // 1. 片着色器对象
    unsigned int fragmentShader;
    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    // 2. 片着色器
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);
    // --------------------------------
    // 1. 着色器程序对象
    unsigned int shaderProgram;
    shaderProgram = glCreateProgram();
    // 2. 链接着色器程序
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);
    // ---------------------------------
    // 1. 激活着色器程序
    glUseProgram(shaderProgram);
    // 2. 激活后,释放前面分配的内存
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
    // ********************************
}
void yqRender(GLuint va){
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    glBindVertexArray(va);
    glDrawArrays(GL_LINE_LOOP, 0, 3);
    // glBindVertexArray(0);
}
// gcc -o main gl01_vertex_array.c common.c -l glfw -l glew -framework opengl

说明

  • 因为程序中创建两个顶点数组维护两个图元数据(正三角与倒三角),所以可以使用下标控制绘制得哪个图元yqRender(ids[0]); // 改变0为1试一下

  • 切换的效果如下:


    GL01-08:OpenGL顶点属性修改函数_第3张图片
    使用顶点数组来分组不同得图元
  • glGenVertexArrays(2, arrayIDs); // glCreateVertexArrays是4.5的函数(GenVertexArrays需要预分配空间)

  • glBindVertexArray(arrayIDs[0]); // 切换到刚创建的顶点数据操作


你可能感兴趣的:(GL01-08:OpenGL顶点属性修改函数)