OpenGL自制3D游戏引擎入门III

绘制三角形

一、概述

  1. 先来记三个单词
    • 顶点数组对象:Vertex Array Object,VAO
    • 顶点缓冲对象:Vertex Buffer Object,VBO
    • 索引缓冲对象:Element Buffer Object,EBO或Index Buffer Object,IBO
  2. 图形渲染管线可以被划分为两个主要部分:第一部分把3D坐标转换为2D,第二部分是把2D坐标转变为实际有颜色的像素(Tip:2D坐标和像素也是不同的,2D坐标精确表示一个点在2D空间中的位置,而2D像素是这个点的近似值,2D像素受到你的屏幕/窗口分辨率的限制)
  3. 我们以数组的形式传递3个3D坐标作为图形渲染管线的输入,用来表示一个三角形,这个数组叫做 顶点数据(Vertex Data) ;顶点数据是一系列点的集合,一个顶点是一个3D坐标的数据集合,顶点数据是用 顶点属性(Vertex Attribute) 表示的,它可以包含任何我们相用的数据
  4. 图元(Primitive): 给OpenGL一些提示,让其知道需要使用哪些图像原型
    • GL_POINTS(点)
    • GL_TRIANGLES(三角形)
    • GL_LINE_STRIP(直线)

二、顶点输入

  1. 标准化设备坐标:OpenGL仅当3D坐标在3个轴上都为-1.0f到1.0f范围内时才处理它

  2. 简单给出一个例子,可以看到一个简单的图形中顶点数据包括了顶点坐标,法线,顶点与面(法线)的关系等等众多信息

    工具软件给出一个.obj文件,CPU提出其中的信息数组并传递到GPU中缓存起来(VBO),根据规则形成索引(VAO),进入顶点着色器

  3. 使用glGenBuffers函数和一个缓冲ID生成一个VBO对象(VAO对象类似)。
    OpenGL有很多缓冲对象类型,顶点缓冲对象的缓冲类型是GL_ARRAY_BUFFER,我们可以使用glBindBuffer函数把新创建的缓冲绑定到GL_ARRAY_BUFFER目标上。
    绑定完成后调用glBufferData函数,把之前定义的顶点数据复制到缓冲的内存中。

      float vertices[] = {
         -0.5f, -0.5f, 0.0f,
         0.5f, -0.5f, 0.0f,
         0.0f,  0.5f, 0.0f
      };
    
      unsigned int VAO;
      glGenVertexArrays(1, &VAO);
      glBindVertexArray(VAO);
    
      unsigned int VBO;
      glGenBuffers(1, &VBO);
      glBindBuffer(GL_ARRAY_BUFFER, VBO);
    
      glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    
  4. 这里提一句glBufferData函数中的第四个参数指定了我们希望显卡如何管理给定的数据,三角形的位置数据不会改变,每次渲染调用时都保持原样,所以它的使用类型最好是GL_STATIC_DRAW。如果,比如说一个缓冲中的数据将频繁被改变,那么使用的类型就是GL_DYNAMIC_DRAW或GL_STREAM_DRAW,这样就能确保显卡把数据放在能够高速写入的内存部分

    • GL_STATIC_DRAW :数据不会或几乎不会改变
    • GL_DYNAMIC_DRAW:数据会被改变很多
    • GL_STREAM_DRAW :数据每次绘制时都会改变

三、顶点着色器&片段着色器

  1. 暂时将顶点着色器的源代码硬编码在代码文件顶部的C风格字符串中

      const char* vertexShaderSource =
      "#version 330 core\n"
      "layout(location = 0) in vec3 aPos;\n"
      "void main()\n"
      "{gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);}\n";
    
      const char* fragmentShaderSource =
      "v#version 330 core\n"
      "out vec4 FragColor;\n"
      "void main()\n"
      "{FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);}\n";
    
  2. 创建着色器对象并把着色器源码附加到着色器对象上然后编译

      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);
    
  3. 着色器程序是多个着色器合并之后并最终链接完成的版本。如果要使用刚才编译的着色器我们必须把它们链接(Link)为一个着色器程序对象,然后在渲染对象的时候激活这个着色器程序。已激活着色器程序的着色器将在我们发送渲染调用的时候被使用。

      unsigned int shaderProgram;
      shaderProgram = glCreateProgram();
      glAttachShader(shaderProgram, vertexShader);
      glAttachShader(shaderProgram, fragmentShader);
      glLinkProgram(shaderProgram);
      glLinkProgram(fragmentShader);
    
  4. 链接顶点属性

    • 位置数据被储存为32位(4字节)浮点值

    • 每个位置包含3个这样的值

    • 在这3个值之间没有空隙(或其他值)。这几个值在数组中紧密排列(Tightly Packed)

    • 数据中第一个值在缓冲开始的位置

      glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
      glEnableVertexAttribArray(0);
      

四、绘制三角形

  1. OpenGL给我们提供了glDrawArrays函数,它使用当前激活的着色器,之前定义的顶点属性配置,和VBO的顶点数据(通过VAO间接绑定)来绘制图元

      glUseProgram(shaderProgram);
      glBindVertexArray(VAO);
      glDrawArrays(GL_TRIANGLES, 0, 3);
    
  2. 附上目前为止的全部代码以供参考

      #define GLEW_STATIC
      #include <GL/glew.h>
      #include <GLFW/glfw3.h>
      #include <iostream>
    
      float vertices[] = {
         -0.5f, -0.5f, 0.0f,
         0.5f, -0.5f, 0.0f,
         0.0f,  0.5f, 0.0f
      };
    
      const char* vertexShaderSource =
      "#version 330 core\n"
      "layout(location = 0) in vec3 aPos;\n"
      "void main()\n"
      "{gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);}\n";
    
      const char* fragmentShaderSource =
      "v#version 330 core\n"
      "out vec4 FragColor;\n"
      "void main()\n"
      "{FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);}\n";
    
      void processInput(GLFWwindow* window)
      {
         if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
         {
            glfwSetWindowShouldClose(window, true);
         }
      }
    
      int main()
      {
         glfwInit();
         glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//主版本号
         glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//次版本号
         glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    
         //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 
         //MAC开发需要声明这一行
    
         //	Open GLFW Window
         GLFWwindow* window = glfwCreateWindow(800, 600, "MyWindow", NULL, NULL);
         if (window == NULL)
         {
            std::cout << "Open window failed" << std::endl;
            glfwTerminate();
            return -1;
         }
         glfwMakeContextCurrent(window);
    
         //	Init GLEW
         glewExperimental = true;
         if (glewInit() != GLEW_OK)
         {
            std::cout << "Init GLEW failed" << std::endl;
            glfwTerminate();
            return -1;
         }
    
         glViewport(0, 0, 800, 600);//前俩参数为锚点,后俩为渲染窗口的宽高
    
         unsigned int VAO;
         glGenVertexArrays(1, &VAO);
         glBindVertexArray(VAO);
    
         unsigned int VBO;
         glGenBuffers(1, &VBO);
         glBindBuffer(GL_ARRAY_BUFFER, VBO);
    
         glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    
         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);
         glLinkProgram(fragmentShader);
    
         glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
         glEnableVertexAttribArray(0);
    
         while (!glfwWindowShouldClose(window))
         {
            processInput(window);
    
            glClearColor(0.7f, 0, 0, 1.0f);
            glClear(GL_COLOR_BUFFER_BIT);
    
            glUseProgram(shaderProgram);
            glBindVertexArray(VAO);
            glDrawArrays(GL_TRIANGLES, 0, 3);
    
            glfwSwapBuffers(window);
            glfwPollEvents();
            
         }
    
         glfwTerminate();
    
         return 0;
      }
    
(下接OpenGL入门IV)

r Pid:63888719


OpenGL自制3D游戏引擎入门III
https://baifabaiquan.cn/2021/03/06/OpenGL入门III/
作者
白发败犬
发布于
2021年3月6日
许可协议