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

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


一、OpenGL 2.1 绘制图元代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>

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. 运行效果

OpenGL2.0没有使用GLSL的绘制

二、着色器的使用

1. 环境初始化的封装

  1. h文件common.h
#ifndef COMMON_H

#define COMMON_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>

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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#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. 运行效果

使用顶点属性传递数据到着色器

6. 顶点属性的分组

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

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#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试一下

  • 切换的效果如下:


    使用顶点数组来分组不同得图元
  • glGenVertexArrays(2, arrayIDs); // glCreateVertexArrays是4.5的函数(GenVertexArrays需要预分配空间)

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


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容