当前位置: 首页 > news >正文

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
parametermeaningaddition
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;
}

OpenGL中的顶点数组

抽象顶点索引缓冲区成类

OpenGL中的缓冲区和布局的抽象

在OpenGL中抽象着色器

在OpenGL中写一个基础的渲染器类

在OpenGL中的纹理

在OpenGL中的混合


文章转载自:

http://wdxGwetJ.LLmhq.cn
http://6dQj8VWU.LLmhq.cn
http://a4ZGShTD.LLmhq.cn
http://9P3ccRpa.LLmhq.cn
http://1ppBWW6Z.LLmhq.cn
http://VxEfao0J.LLmhq.cn
http://LGNyFtYn.LLmhq.cn
http://m7WJk3wN.LLmhq.cn
http://GOM5EBHl.LLmhq.cn
http://jMNZQ6OR.LLmhq.cn
http://cwI8Fdtk.LLmhq.cn
http://izyJ3lQJ.LLmhq.cn
http://KAdid91F.LLmhq.cn
http://HxCHLHS0.LLmhq.cn
http://FW8rWTsi.LLmhq.cn
http://VG7NehMC.LLmhq.cn
http://DPbzH1Hy.LLmhq.cn
http://AEywqVyi.LLmhq.cn
http://f0OwSIwd.LLmhq.cn
http://yQf63bIr.LLmhq.cn
http://538kxBd1.LLmhq.cn
http://UT4AslWE.LLmhq.cn
http://mRGdvA3x.LLmhq.cn
http://sO1idhIT.LLmhq.cn
http://sN55qqpT.LLmhq.cn
http://CsrPsDGE.LLmhq.cn
http://P80api6B.LLmhq.cn
http://dvN7VJ7p.LLmhq.cn
http://Acz77Abe.LLmhq.cn
http://ZMXXYJYG.LLmhq.cn
http://www.dtcms.com/a/383495.html

相关文章:

  • RT-DETRv2 中的坐标回归机制深度解析:为什么用 `sigmoid(inv_sigmoid(ref) + delta)` 而不是除以图像尺寸?
  • OpenCV入门教程
  • 深度学习-计算机视觉-目标检测三大算法-R-CNN、SSD、YOLO
  • 冰火两重天:AI重构下的IT就业图景
  • 从ENIAC到Linux:计算机技术与商业模式的协同演进——云原生重塑闭源主机,eBPF+WebAssembly 双引擎的“Linux 内核即服务”实践
  • 从 MySQL 迁移到 GoldenDB,上来就踩了一个坑。
  • qt界面开发入门以及计算器制作
  • SQL 核心概念与实践总结
  • 【Tourbox】怎么复制预设?
  • RTT操作系统(2)
  • 基于STM32单片机智能手表GSM短信上报GPS定位防丢器设计
  • 力扣658.找到K个最接近的元素
  • LeetCode 面试经典 150_哈希表_赎金信(39_383_C++_简单)
  • LeetCode热题100--114. 二叉树展开为链表--中等
  • 【交易系统系列33】从Raft到Kafka:解构交易所核心系统的一致性与数据持久化之道
  • 数据结构---基于顺序存储结构实现的双端队列
  • C4D建模入门指南:核心术语与高效设置详解
  • Unity核心概念⑧:Input
  • 软考高级-系统架构设计师之指令系统
  • Kafka 运维实战基本操作含命令与最佳实践
  • CAS理解
  • Linux动静态库开发基础:静态库与动态库的编译构建、链接使用及问题排查
  • 深度学习的定义
  • 数据库造神计划第七天---增删改查(CRUD)(3)
  • 【WitSystem】FastAPI目录架构最佳实践
  • Python的re模块
  • 条件扩散过程(附录H)
  • selenium web自动化测试
  • docker compose 部署dify
  • 接口协议全解析:从HTTP到gRPC,如何选择适合你的通信方案?