Cherno OpenGL 教程
目录
- OpenGL工作原理
- 设置OpenGL和在C++中创建一个窗口
- 在C++中使用现代OpenGL
- 顶点缓冲区和在现代OpenGL中画一个三角形
- 在OpenGL中顶点的属性和布局
- 着色器
- 在OpenGL中着色器的原理
- 在OpenGL中写一个着色器
- 在OpenGL中我是如何处理着色器的
- 在OpenGL中的索引缓冲区
- 在OpenGL中处理错误
- OpenGL中的统一变量(uniforms)
- OpenGL中的顶点数组
- 抽象顶点索引缓冲区成类
- OpenGL中的缓冲区和布局的抽象
- 在OpenGL中抽象着色器
- 在OpenGL中写一个基础的渲染器类
- 在OpenGL中的纹理
- 在OpenGL中的混合
OpenGL工作原理
- OpenGL工作原理:我们为显卡提供数据,在显卡上存储一些内存,它包含了我们想要绘制的所有数据。然后使用一个着色器,一个在显卡上执行的程序来读取数据,并且完全显示在屏幕上。
- 通常我们画几何图形的方式就是使用一个顶点缓冲区,这基本上是存储在显卡上的内存缓冲区。所以,当对着着色器编程时,实际上是从读取顶点缓冲区开始的,它需要知道缓冲区的布局。这个缓冲区实际上包含的就是一堆浮点数,它们指定了每个顶点的位置,也有纹理坐标,法线等。所以我们需要告诉GPU,内存中是什么,是如何布局的。
- 从着色器的角度来说,你也需要匹配你在CPU端的布局。
设置OpenGL和在C++中创建一个窗口
网站 :docs.GL
#include <GLFW/glfw3.h>int main(void)
{GLFWwindow* window;/* Initialize the library */if (!glfwInit())return -1;/* Create a windowed mode window and its OpenGL context */window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);if (!window){glfwTerminate();return -1;}/* Make the window's context current */glfwMakeContextCurrent(window);/* Loop until the user closes the window */while (!glfwWindowShouldClose(window)){/* Render here */glClear(GL_COLOR_BUFFER_BIT);glBegin(GL_TRIANGLES);glVertex2f(-0.5f, -0.5f);glVertex2f(0.0f, 0.5f);glVertex2f(0.5f, -0.5f);glEnd();/* Swap front and back buffers */glfwSwapBuffers(window);/* Poll for and process events */glfwPollEvents();}glfwTerminate();return 0;
}
在C++中使用现代OpenGL
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>int main(void)
{GLFWwindow* window;/* Initialize the library */if (!glfwInit())return -1;/* Create a windowed mode window and its OpenGL context */window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);if (!window){glfwTerminate();return -1;}/* Make the window's context current */glfwMakeContextCurrent(window);if (glewInit() != GLEW_OK) {std::cout << "Error!" << std::endl;}/* Loop until the user closes the window */while (!glfwWindowShouldClose(window)){/* Render here */glClear(GL_COLOR_BUFFER_BIT);glBegin(GL_TRIANGLES);glVertex2f(-0.5f, -0.5f);glVertex2f(0.0f, 0.5f);glVertex2f(0.5f, -0.5f);glEnd();/* Swap front and back buffers */glfwSwapBuffers(window);/* Poll for and process events */glfwPollEvents();}glfwTerminate();return 0;
}
顶点缓冲区和在现代OpenGL中画一个三角形
- 顶点缓冲区 : OpenGL中的内存缓冲区,它实际上是在显卡上,在VRAM(显存)中,Video RAM。
- 顶点缓冲区的家在显存(VRAM) 中。但它是由CPU在系统内存(RAM) 中“生”出来,然后通过“快递”(PCIe总线)送过去的。而CPU/GPU缓存就像是“临时工作台”,数据在被处理时才会放在上面。
- 着色器 : 着色器是一个运行在显卡上的程序,是我们可以编写的可以在显卡上运行的代码,它可以在显存上以一种非常特殊又非常强大的方式运行。
- 状态机(state machine): OpenGL的本质是状态机,不同于函数调用,它已经知道了状态的所有可能。
- OpenGL工作原理:创建顶点缓冲区(包含顶点数据,放入一个缓冲区,传到OpenGL的VRAM),然后发出一个 DrawCall 指令,让GPU根据缓冲区画出图形。
- 唯一标识符 : 在OpenGL中生成的所有东西都会被分配一个唯一标识符——整数,不管是顶点缓冲区,顶点数组,纹理,着色器或其他。0通常是一种无效状态,但不都是。
- glBufferData usage属性:
- STREAM:它被修改一次,并且只能使用几次
- STATIC:数据存储内容将被修改一次,并且可以使用多次
- DYNAMIC: 数据存储的内容会被反复修改,并且多次使用
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>int main(void)
{GLFWwindow* window;/* Initialize the library */if (!glfwInit())return -1;/* Create a windowed mode window and its OpenGL context */window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);if (!window){glfwTerminate();return -1;}/* Make the window's context current */glfwMakeContextCurrent(window);if (glewInit() != GLEW_OK) {std::cout << "Error!" << std::endl;}float positions[6] = {-0.5f, -0.5f,0.0f, 0.5f,0.5f, -0.5f};// create vertex bufferunsigned int buffer;glGenBuffers(1, &buffer);glBindBuffer(GL_ARRAY_BUFFER, buffer);glBufferData(GL_ARRAY_BUFFER, sizeof(positions),positions,GL_STATIC_DRAW);/* Loop until the user closes the window */while (!glfwWindowShouldClose(window)){/* Render here */glClear(GL_COLOR_BUFFER_BIT);glDrawArrays(GL_TRIANGLES, 0, 3);/* Swap front and back buffers */glfwSwapBuffers(window);/* Poll for and process events */glfwPollEvents();}glfwTerminate();return 0;
}
在OpenGL中顶点的属性和布局
- glVertexAttribPointer
parameter | meaning | addition |
---|---|---|
index | 需要再次修改的共享信息顶点属性(位置、颜色、纹理坐标、法线等)的索引 | 着色器读取所有这些的方式就是通过索引, 它是缓冲区中实际属性的索引 |
size | 计数 | 只能是1,2,3,4,它是提供的浮点数的数量 |
type | 数据类型 | |
normalized | 标准化 | 假设我们要指定一个字节在0255之间,它需要被规范化到0-1之间,在我们实际着色器中作为浮点数 |
stride | 连续通用顶点属性之间的字节偏移量 | 每个顶点之间的字节数 |
pointer | 指向实际属性的指针 | int类型 |
- glEnableVertexAttribArray(int pointer):启动pointer号指针
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>int main(void)
{GLFWwindow* window;/* Initialize the library */if (!glfwInit())return -1;/* Create a windowed mode window and its OpenGL context */window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);if (!window){glfwTerminate();return -1;}/* Make the window's context current */glfwMakeContextCurrent(window);if (glewInit() != GLEW_OK) {std::cout << "Error!" << std::endl;}float positions[6] = {-0.5f, -0.5f,0.0f, 0.5f,0.5f, -0.5f};// create vertex bufferunsigned int buffer;glGenBuffers(1, &buffer);glBindBuffer(GL_ARRAY_BUFFER, buffer);glBufferData(GL_ARRAY_BUFFER, sizeof(positions),positions,GL_STATIC_DRAW);glEnableVertexAttribArray(0);glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), 0);glBindBuffer(GL_ARRAY_BUFFER, 0);/* Loop until the user closes the window */while (!glfwWindowShouldClose(window)){/* Render here */glClear(GL_COLOR_BUFFER_BIT);glDrawArrays(GL_TRIANGLES, 0, 3);/* Swap front and back buffers */glfwSwapBuffers(window);/* Poll for and process events */glfwPollEvents();}glfwTerminate();return 0;
}
着色器
在OpenGL中着色器的原理
- 着色器:它是我们可以在计算机上以文本/字符串形式编写的代码,然后给OpenGL,就可以把它发送到显卡上,像编译链接运行其他程序一样编译、链接和运行。
- 着色器的本质:对GPU编程,告诉GPU如何处理我们发送的数据。
- 分类:顶点着色器(vertex shaders)和片段着色器(fragment shaders,像素着色器pixel shaders)等
- 着色器运行过程:顶点着色器和片段着色器是顺着管道的两种不同的着色器类型。当我们真正发出DrawCall指令时,顶点着色器会被调用,然后片段着色器会被调用,然后我们会在屏幕上看到结果。
- 顶点着色器:尝试渲染每个顶点调用,每个顶点会调用一次顶点着色器。顶点着色器主要决定顶点在什么位置。
- 片段着色器:会为每个需要光栅化的像素运行一次。光栅化实际上是画在屏幕上的,类似于给区域填色。它会对需要填充的每个像素调用一次,主要决定这个像素是什么颜色。
在OpenGL中写一个着色器
#version 330 core
//means we’re going to use GLSL
在OpenGL中我是如何处理着色器的
- "Basic.shader“
#shader vertex
#version 330 corelayout(location = 0) in vec4 position; void main()
{gl_Position = position;
};#shader fragment
#version 330 core //means we're going to use GLSLlayout(location = 0) out vec4 color; void main()
{color = vec4(1.0, 0.0, 0.0, 1.0);
};
- “Application.cpp"
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>struct ShaderProgramSource {std::string VertexSource;std::string FragmentSource;
};static ShaderProgramSource ParseShader(const std::string& filepath) {std::ifstream stream(filepath);enum class ShaderType {NONE = -1,VERTEX = 0,FRAGMENT = 1};std::string line;std::stringstream ss[2];ShaderType type = ShaderType::NONE;while (getline(stream, line)) {if (line.find("# shader") != std::string::npos) {if (line.find("vertex") != std::string::npos) {// set mode to vertextype = ShaderType::VERTEX;}else if (line.find("fragment") != std::string::npos) {// set mode to fragmenttype = ShaderType::FRAGMENT;}}else {ss[(int)type] << line << '\n';}}return { ss[0].str(), ss[1].str() };
}static unsigned int CompileShader(unsigned int type, const std::string& source) {unsigned int id = glCreateShader(type);// 因为OpenGL是C库,需要以NULL结尾的char数组const char* src = source.c_str(); //&source[0]glShaderSource(id, 1, &src, nullptr);glCompileShader(id);// TODO : Error handingint result;glGetShaderiv(id, GL_COMPILE_STATUS, &result);if (result == GL_FALSE) {int length;glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);char* message = (char*)alloca(length * sizeof(char)); // alloca allocate on stack dynamicallyglGetShaderInfoLog(id, length, &length, message);std::cout << "Failed to compile " << (type == GL_VERTEX_SHADER ? "vertex" : "fragment")<< "shader !" << std::endl << message << std::endl;glDeleteShader(id);return 0;}return id;
}static int CreateShader(const std::string& vertexShader, const std::string& fragmentShader) {// create a programunsigned int program = glCreateProgram();// create vertex shader & fragment shaderunsigned int vs = CompileShader(GL_VERTEX_SHADER, vertexShader);unsigned int fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShader);// link the shaders to one program to useglAttachShader(program, vs);glAttachShader(program, fs);glLinkProgram(program);glValidateProgram(program);// delete the intermediates because the shaders have already been linked into a programglDeleteShader(vs);glDeleteShader(fs);return program;
}int main(void)
{GLFWwindow* window;/* Initialize the library */if (!glfwInit())return -1;/* Create a windowed mode window and its OpenGL context */window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);if (!window){glfwTerminate();return -1;}/* Make the window's context current */glfwMakeContextCurrent(window);if (glewInit() != GLEW_OK) {std::cout << "Error!" << std::endl;}float positions[] = {-0.5f, -0.5f,0.5f, -0.5f,0.5f, 0.5f,-0.5f, 0.5f,};unsigned int indices[] = {0, 1, 2,2, 3, 0};// create vertex bufferunsigned int buffer;glGenBuffers(1, &buffer);glBindBuffer(GL_ARRAY_BUFFER, buffer);glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);glEnableVertexAttribArray(0);glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), 0);// glBindBuffer(GL_ARRAY_BUFFER, 0);// create index bufferunsigned int ibo; // index buffer objectglGenBuffers(1, &ibo);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);ShaderProgramSource source = ParseShader("res/shaders/Basic.shader");std::string vertexShader = source.VertexSource;std::string fragmentShader = source.FragmentSource;unsigned int shader = CreateShader(vertexShader, fragmentShader);glUseProgram(shader);/* Loop until the user closes the window */while (!glfwWindowShouldClose(window)){/* Render here */glClear(GL_COLOR_BUFFER_BIT);// glDrawArrays(GL_TRIANGLES, 0, 6);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);/* Swap front and back buffers */glfwSwapBuffers(window);/* Poll for and process events */glfwPollEvents();}glfwTerminate();return 0;
}
在OpenGL中的索引缓冲区
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>struct ShaderProgramSource {std::string VertexSource;std::string FragmentSource;
};static ShaderProgramSource ParseShader(const std::string& filepath) {std::ifstream stream(filepath);enum class ShaderType {NONE = -1,VERTEX = 0,FRAGMENT = 1};std::string line;std::stringstream ss[2];ShaderType type = ShaderType::NONE;while (getline(stream, line)) {if (line.find("# shader") != std::string::npos) {if (line.find("vertex") != std::string::npos) {// set mode to vertextype = ShaderType::VERTEX;}else if (line.find("fragment") != std::string::npos) {// set mode to fragmenttype = ShaderType::FRAGMENT;}}else {ss[(int)type] << line << '\n';}}return { ss[0].str(), ss[1].str() };
}static unsigned int CompileShader(unsigned int type, const std::string& source) {unsigned int id = glCreateShader(type);// 因为OpenGL是C库,需要以NULL结尾的char数组const char* src = source.c_str(); //&source[0]glShaderSource(id, 1, &src, nullptr);glCompileShader(id);// TODO : Error handingint result;glGetShaderiv(id, GL_COMPILE_STATUS, &result);if (result == GL_FALSE) {int length;glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);char* message = (char*)alloca(length * sizeof(char)); // alloca allocate on stack dynamicallyglGetShaderInfoLog(id, length, &length, message);std::cout << "Failed to compile " << (type == GL_VERTEX_SHADER ? "vertex" : "fragment")<< "shader !" << std::endl << message << std::endl;glDeleteShader(id);return 0;}return id;
}static int CreateShader(const std::string& vertexShader, const std::string& fragmentShader) {// create a programunsigned int program = glCreateProgram();// create vertex shader & fragment shaderunsigned int vs = CompileShader(GL_VERTEX_SHADER, vertexShader);unsigned int fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShader);// link the shaders to one program to useglAttachShader(program, vs);glAttachShader(program, fs);glLinkProgram(program);glValidateProgram(program);// delete the intermediates because the shaders have already been linked into a programglDeleteShader(vs);glDeleteShader(fs);return program;
}int main(void)
{GLFWwindow* window;/* Initialize the library */if (!glfwInit())return -1;/* Create a windowed mode window and its OpenGL context */window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);if (!window){glfwTerminate();return -1;}/* Make the window's context current */glfwMakeContextCurrent(window);if (glewInit() != GLEW_OK) {std::cout << "Error!" << std::endl;}float positions[] = {-0.5f, -0.5f,0.5f, -0.5f,0.5f, 0.5f,-0.5f, 0.5f,};unsigned int indices[] = {0, 1, 2,2, 3, 0};// create vertex bufferunsigned int buffer;glGenBuffers(1, &buffer);glBindBuffer(GL_ARRAY_BUFFER, buffer);glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);glEnableVertexAttribArray(0);glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), 0);// glBindBuffer(GL_ARRAY_BUFFER, 0);// create index bufferunsigned int ibo; // index buffer objectglGenBuffers(1, &ibo);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);ShaderProgramSource source = ParseShader("res/shaders/Basic.shader");std::string vertexShader = source.VertexSource;std::string fragmentShader = source.FragmentSource;unsigned int shader = CreateShader(vertexShader, fragmentShader);glUseProgram(shader);/* Loop until the user closes the window */while (!glfwWindowShouldClose(window)){/* Render here */glClear(GL_COLOR_BUFFER_BIT);// glDrawArrays(GL_TRIANGLES, 0, 6);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);/* Swap front and back buffers */glfwSwapBuffers(window);/* Poll for and process events */glfwPollEvents();}glfwTerminate();return 0;
}
在OpenGL中处理错误
- glGetError
- glDebugMessageCallback
- 步骤:先清除其他error,再在可能出bug的语句下面输出error。
static void GLClearError() {while (!glGetError());
}static void GLCheckError() {while (GLenum error = glGetError()) {std::cout << "[OpenGL Error](" << error << ")" << std::endl;}
}
/* Loop until the user closes the window */while (!glfwWindowShouldClose(window)){/* Render here */glClear(GL_COLOR_BUFFER_BIT);// glDrawArrays(GL_TRIANGLES, 0, 6);GLClearError();// glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);glDrawElements(GL_TRIANGLES, 6, GL_INT, nullptr); //errorGLCheckError();/* Swap front and back buffers */glfwSwapBuffers(window);/* Poll for and process events */glfwPollEvents();}
- 使用 assert 和 宏
//__表示它是编译器本身的函数(MSVC特有的,在gcc等其他编译器上无效
#define ASSERT(x) if (!x) __debugbreak();
// #表示将其转换为字符串
#define GLCall(x) GLClearError();\x;\ASSERT(GLLogCall(#x, __FILE__, __LINE__));static void GLClearError() {while (!glGetError());
}static bool GLLogCall(const char* function, const char* file, int line) {while (GLenum error = glGetError()) {std::cout << "[OpenGL Error](" << error << "): " <<function << " " << file << ":" << line << std::endl;return false;}return true;
}
GLCall(glDrawElements(GL_TRIANGLES, 6, GL_INT, nullptr));
OpenGL中的统一变量(uniforms)
- 统一变量:一种实际上从CPU端获取数据到显存方式
- 属性和统一变量是两种获取从CPU到显卡绘制的对象数据的两种主要方式
- glUniformnm:n 表示几个数字,m 表示数字的类型。例如glUniform4f
- 步骤:
- 通过实际的着色器 id 编写 glUseProgram 来绑定着色器,获取这个实际颜色变量的位置。得到这个位置时,用这个位置整数和值调用 glUniform,这样就可以在着色器中设置我的数据。
- 而在着色器中,所作的是通过输入 uniform 关键字以及类型和名称来创建统一变量,就像创建其他变量一样,只是实际我是用统一变量来写输出颜色。如果一切顺利,我应该将这个颜色值写入我的实际输出颜色。
- “Basic.shader”
#shader vertex
#version 330 corelayout(location = 0) in vec4 position; void main()
{gl_Position = position;
};#shader fragment
#version 330 core layout(location = 0) out vec4 color; uniform vec4 u_Color;void main()
{color = u_Color;
};
- “Application.cpp”
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>//__表示它是编译器本身的函数(MSVC特有的,在gcc等其他编译器上无效
#define ASSERT(x) if (!x) __debugbreak();
// #表示将其转换为字符串
#define GLCall(x) GLClearError();\x;\ASSERT(GLLogCall(#x, __FILE__, __LINE__))static void GLClearError() {while (!glGetError());
}static bool GLLogCall(const char* function, const char* file, int line) {while (GLenum error = glGetError()) {std::cout << "[OpenGL Error](" << error << "): " <<function << " " << file << ":" << line << std::endl;return false;}return true;
}struct ShaderProgramSource {std::string VertexSource;std::string FragmentSource;
};static ShaderProgramSource ParseShader(const std::string& filepath) {std::ifstream stream(filepath);enum class ShaderType {NONE = -1,VERTEX = 0,FRAGMENT = 1};std::string line;std::stringstream ss[2];ShaderType type = ShaderType::NONE;while (getline(stream, line)) {if (line.find("#shader") != std::string::npos) {if (line.find("vertex") != std::string::npos) {// set mode to vertextype = ShaderType::VERTEX;}else if (line.find("fragment") != std::string::npos) {// set mode to fragmenttype = ShaderType::FRAGMENT;}}else {ss[(int)type] << line << '\n';}}return { ss[0].str(), ss[1].str() };
}static unsigned int CompileShader(unsigned int type, const std::string& source) {unsigned int id = glCreateShader(type);// 因为OpenGL是C库,需要以NULL结尾的char数组const char* src = source.c_str(); //&source[0]glShaderSource(id, 1, &src, nullptr);glCompileShader(id);// TODO : Error handingint result;glGetShaderiv(id, GL_COMPILE_STATUS, &result);if (result == GL_FALSE) {int length;glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);char* message = (char*)alloca(length * sizeof(char)); // alloca allocate on stack dynamicallyglGetShaderInfoLog(id, length, &length, message);std::cout << "Failed to compile " << (type == GL_VERTEX_SHADER ? "vertex" : "fragment")<< "shader !" << std::endl << message << std::endl;glDeleteShader(id);return 0;}return id;
}static int CreateShader(const std::string& vertexShader, const std::string& fragmentShader) {// create a programunsigned int program = glCreateProgram();// create vertex shader & fragment shaderunsigned int vs = CompileShader(GL_VERTEX_SHADER, vertexShader);unsigned int fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShader);// link the shaders to one program to useglAttachShader(program, vs);glAttachShader(program, fs);glLinkProgram(program);glValidateProgram(program);// delete the intermediates because the shaders have already been linked into a programglDeleteShader(vs);glDeleteShader(fs);return program;
}int main(void)
{GLFWwindow* window;/* Initialize the library */if (!glfwInit())return -1;/* Create a windowed mode window and its OpenGL context */window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);if (!window){glfwTerminate();return -1;}/* Make the window's context current */glfwMakeContextCurrent(window);glfwSwapInterval(1);if (glewInit() != GLEW_OK) {std::cout << "Error!" << std::endl;}float positions[] = {-0.5f, -0.5f,0.5f, -0.5f,0.5f, 0.5f,-0.5f, 0.5f,};unsigned int indices[] = {0, 1, 2,2, 3, 0};// create vertex bufferunsigned int buffer;glGenBuffers(1, &buffer);glBindBuffer(GL_ARRAY_BUFFER, buffer);glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);glEnableVertexAttribArray(0);glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), 0);// glBindBuffer(GL_ARRAY_BUFFER, 0);// create index bufferunsigned int ibo; // index buffer objectglGenBuffers(1, &ibo);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);ShaderProgramSource source = ParseShader("res/shaders/Basic.shader");std::string vertexShader = source.VertexSource;std::string fragmentShader = source.FragmentSource;unsigned int shader = CreateShader(vertexShader, fragmentShader);glUseProgram(shader); //在这一步必须有一个实际绑定的着色器int location = glGetUniformLocation(shader, "u_Color");ASSERT(location != -1);glUniform4f(location, 0.8f, 0.3f, 0.8f, 1.0f); //将代码从着色器移到了C++中float r = 0.0f;float increment = 0.05f;/* Loop until the user closes the window */while (!glfwWindowShouldClose(window)){/* Render here */glClear(GL_COLOR_BUFFER_BIT);// glDrawArrays(GL_TRIANGLES, 0, 6);glUniform4f(location, r, 0.3f, 0.8f, 1.0f);GLCall(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr));//GLCall(glDrawElements(GL_TRIANGLES, 6, GL_INT, nullptr));if (r > 1.0f) {increment = -0.05f;}else if (r < 0.0f){increment = 0.05f;}r += increment;/* Swap front and back buffers */glfwSwapBuffers(window);/* Poll for and process events */glfwPollEvents();}glfwTerminate();return 0;
}