这个章节主要考虑通过opengl绘制一个简单的三角形。记录下来以备后面查阅。
OpenGL的处理管线主要有这些:顶点着色器、图元装配、几何着色器、光栅处理、片段着色器、混合测试这六部分。
顶点着色器主要将输入的顶点坐标变换为GPU内部识别的顶点坐标。
图元装配主要将输入的顶点按照用户的意图装配为图元,如点、线、圆等。
几何着色器则负责将根据图元装配的结果,按照用户需求转换为新的图元。
光栅处理,则负责将精确的坐标等使用像素来表述。
片段着色器则对各像素片段上的属性进行运算设置。
最后的混合测试可以对多个片段的关系进行设置。
目前较为常用的是设置顶点着色器和片段着色器。
好,切入正题,绘制我们的三角形。
三角形的顶点我们可以定义如下:
float vertices[] = { 0.5f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
-0.5f, 0.5f, 0.0f
};
接着可以定义顶点缓冲区对象(VBO),它主要对于顶点进行管理,将用户定义的数据直接在GPU上开辟空间处理。一般不管什么缓冲区,都需要如下步骤初始化,这里以VBO为例:
1、生成缓冲区 glGenBuffers(1, &VBO);
2、绑定缓冲区到缓冲区对象 glBindBuffer(GL_ARRAY_BUFFER, VBO);
3、设置缓冲区数据区
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
对于VAO也可这样定义:
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
对于EBO这样定义:
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
最后对于这些数据缓冲区,需要设置其解析方式:
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer的第一个参数,表示设置的是哪个shader变量,在shader中使用layout (location = 0)语句来设置。
glVertexAttribPointer第二个参数代表指定顶点的元素个数,如这里是三维坐标,那么有3个元素
glVertexAttribPointer第三个参数表示每个元素的类型,这里是float类型
glVertexAttribPointer第四个参数是顶点间存储的间距,这里由于顶点紧密排列,所以是顶点大小。
glVertexAttribPointer第四个参数暂时忽略吧
设置好缓冲区后,可以设置着色器熟悉。一般着色器使用需要如下几个步骤:
1、 创建着色器 vertexShader = glCreateShader(GL_VERTEX_SHADER);/*设置顶点着色器*/
2、 附加着色器源码, glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
3、 编译着色器 glCompileShader(vertexShader);
检查着色器是否编译成功glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
4、 创建着色器程序 shaderProgram = glCreateProgram();
5、 添加着色器 glAttachShader(shaderProgram, fragmentShader);
6、 编译着色器 glLinkProgram(shaderProgram);
检测着色器是否成功 glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
7、 使用着色器:glUseProgram(shaderProgram);
8、 删除着色器:glDeleteShader(vertexShader);
这里着色器创建的是顶点着色器,如果是片段着色器,也一样进行创建,只是使用GL_FRAGMENT_SHADER宏来指定。
目前如果只使用VBO是绘制不出图像的,因为OpenGL默认必须要有VAO才可绘制。
使用VAO绘制,则调用glDrawArrays(GL_TRIANGLES, 0, 3);绘制三角形
如果使用EBO绘制,则调用glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);函数绘制三角形。
#include <glad/glad.h> #include <GLFW/glfw3.h> #include <iostream> const unsigned int SCR_WIDTH = 800; const unsigned int SCR_HEIGHT = 600; float vertices[] = { 0.5f, 0.5f, 0.0f, 0.5f, -0.5f, 0.0f, -0.5f, -0.5f, 0.0f, -0.5f, 0.5f, 0.0f }; unsigned int indices[] = { 0, 1, 3, 1, 2, 3 }; const GLchar* vertexShaderSource = "#version 330 core\n" "layout (location = 0) in vec3 position;\n" "void main()\n" "{\n" "gl_Position = vec4(position.x, position.y, position.z, 1.0);\n" "}\0"; const GLchar* fragmentShaderSource = "#version 330 core\n" "out vec4 FragColor;\n" "void main()\n" "{\n" "FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" "}\0 "; void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode) { if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { glfwSetWindowShouldClose(window, GL_TRUE); } } void framebuffer_size_callback(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height); } int main() { unsigned int VBO; unsigned int vertexShader; unsigned int fragmentShader; unsigned int shadeProgram; unsigned int VAO; unsigned int EBO; int success; char infoLog[512]; glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL); if (window == nullptr) { std::cout << "Failed to create GLFW window" << std::endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { std::cout << "Failed to initialize GLAD" << std::endl; return -1; } glfwSetKeyCallback(window, key_callback); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); glGenVertexArrays(1, &VAO); glBindVertexArray(VAO); glGenBuffers(1, &VBO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glGenBuffers(1, &EBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); vertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); glCompileShader(vertexShader); glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); if (success == 0) { glGetShaderInfoLog(vertexShader, 512, NULL, infoLog); std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl; } fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); glCompileShader(fragmentShader); glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); if (success == 0) { glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog); std::cout << "ERROR::SHADER::FRAGMENT:COMPILATION_FAILED\n" << infoLog << std::endl; } shadeProgram = glCreateProgram(); glAttachShader(shadeProgram, vertexShader); glAttachShader(shadeProgram, fragmentShader); glLinkProgram(shadeProgram); glGetProgramiv(shadeProgram, GL_LINK_STATUS, &success); if (success == 0) { glGetProgramInfoLog(shadeProgram, 512, NULL, infoLog); std::cout << "ERROR::SHADER::PROGRAM:LINK_FAILED\n" << infoLog << std::endl; } glDeleteShader(vertexShader); glDeleteShader(fragmentShader); while (!glfwWindowShouldClose(window)) { glfwPollEvents(); glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glUseProgram(shadeProgram); glBindVertexArray(VAO); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); glfwSwapBuffers(window); } glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); glfwTerminate(); return 0; }