本文主要使用顶点属性与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着色器)
- 修改顶点属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
- 启用顶点属性
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
- 修改顶点属性
2. 运行效果
二、着色器的使用
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
- 实现文件
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;
}
- 说明
- 代码中加载的是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]); // 切换到刚创建的顶点数据操作