1. 引言

本文基于C++语言,描述OpenGL的绘制流程,这里描述的是OpenGL的核心模式(Core-profile)

本文基于Ubuntu 20.04.3 LTS系统,使用CMake构建程序,OpenGL环境搭建可参考:

笔者这里不过多描述每个名词、函数和细节,更详细的文档可以参考:

2. 流程综述

OpenGL的绘制流程(图形渲染管线,Graphics Pipeline)如下:

  • 顶点着色(vertex shader)阶段将CPU传入的数据进行一定的变换处理
  • 图元装配(shape assembly)阶段的就是上阶段的顶点数据处理成图元(如,三角形)
  • 几何着色(geometry shader)阶段是根据一定规则将输入的图元变更或输出更多的图元(可选)
  • 光栅化(rasterization)阶段的是将上阶段的图元进行计算得到图元占据的屏幕像素列表
  • 片元着色(fragment)阶段是将上阶段生成的片元进行着色处理后
  • 测试与混合阶段计算片元的深度、颜色等从而进行舍弃或保留

绘制流程繁琐,然而,我们能配置的只有三个蓝色的着色器部分。几何着色器可选,一般配置顶点着色器和片段着色器即可,即,以下步骤就是配置顶点着色器和片段着色器

3. 生成顶点数据

生成顶点缓冲对象(Vertex Buffer Objects, VBO)并加载数据:

  1. float vertices[] = {
  2. 0.0f, 0.5f, 0.0f,
  3. -0.5f, -0.5f, 0.0f,
  4. 0.5f, -0.5f, 0.0f
  5. };
  6. unsigned int VBO;
  7. glGenBuffers(1, &VBO);
  8. glBindBuffer(GL_ARRAY_BUFFER, VBO);
  9. glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

4. 链接属性数据

顶点数组对象(Vertex Array Object, VAO)与VBO绑定,用于保存属性数据(先绑定VAO,再创建VBO就会绑定到VAO上):

  1. unsigned int VAO;
  2. glGenVertexArrays(1, &VAO);
  3. glBindVertexArray(VAO);
  4. float vertices[] = {
  5. 0.0f, 0.5f, 0.0f,
  6. -0.5f, -0.5f, 0.0f,
  7. 0.5f, -0.5f, 0.0f
  8. };
  9. unsigned int VBO;
  10. glGenBuffers(1, &VBO);
  11. glBindBuffer(GL_ARRAY_BUFFER, VBO);
  12. glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
  13. glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0);
  14. glEnableVertexAttribArray(0);

5. 创建顶点着色器

创建顶点着色器(Vertex Shader)并编译:

  1. const char *vertexShaderSource =
  2. "#version 330 core\n"
  3. "layout (location = 0) in vec3 aPos;\n"
  4. "void main()\n"
  5. "{\n"
  6. "gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
  7. "}\0";
  8. unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
  9. glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
  10. glCompileShader(vertexShader);

6. 创建片段着色器

创建片段着色器(Fragment Shader)并编译:

  1. const char *fragmentShaderSource =
  2. "#version 330 core\n"
  3. "out vec4 FragColor;"
  4. "void main()\n"
  5. "{\n"
  6. "FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
  7. "}\0";
  8. unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
  9. glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
  10. glCompileShader(fragmentShader);

7. 链接着色器

着色器程序对象(Shader Program Object)是多个着色器合并之后并最终链接完成的版本:

  1. unsigned int shaderProgram = glCreateProgram();
  2. glAttachShader(shaderProgram, vertexShader);
  3. glAttachShader(shaderProgram, fragmentShader);
  4. glLinkProgram(shaderProgram);

8. 绘制

开始(循环)绘制:

  1. glClearColor(0.2, 0.3, 0.3, 1.0);
  2. glClear(GL_COLOR_BUFFER_BIT);
  3. glUseProgram(shaderProgram);
  4. glBindVertexArray(VAO);
  5. glDrawArrays(GL_TRIANGLES, 0, 3);

9. 完整代码

基于GLFW与GLAD创建OpenGL开发环境,完整代码如下:

  1. #include <glad/glad.h>
  2. #include <GLFW/glfw3.h>
  3. #include <iostream>
  4. void framebuffer_size_callback(GLFWwindow *window, int width, int height);
  5. void process_input(GLFWwindow *window);
  6. unsigned int *renderInit();
  7. void render(unsigned int shaderProgram, unsigned int VAO);
  8. bool checkCompile(unsigned int shader);
  9. bool checkProgram(unsigned int shaderProgram);
  10. int main()
  11. {
  12. glfwInit();
  13. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  14. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  15. GLFWwindow *window = glfwCreateWindow(800, 600, "hello triangle", nullptr, nullptr);
  16. if (window == nullptr)
  17. {
  18. std::cout << "Faild to create window" << std::endl;
  19. glfwTerminate();
  20. }
  21. glfwMakeContextCurrent(window);
  22. if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
  23. {
  24. std::cout << "Faild to initialize glad" << std::endl;
  25. return -1;
  26. }
  27. glad_glViewport(0, 0, 800, 600);
  28. glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
  29. unsigned int *arr = renderInit();
  30. while (!glfwWindowShouldClose(window))
  31. {
  32. process_input(window);
  33. // render
  34. std::cout << arr[0] << " " << arr[1] << " " << arr[2] << std::endl;
  35. render(arr[0], arr[1]);
  36. glfwSwapBuffers(window);
  37. glfwPollEvents();
  38. }
  39. glDeleteProgram(arr[0]);
  40. glDeleteVertexArrays(1, &arr[1]);
  41. glDeleteBuffers(1, &arr[2]);
  42. glfwTerminate();
  43. return 0;
  44. }
  45. void framebuffer_size_callback(GLFWwindow *window, int width, int height)
  46. {
  47. glViewport(0, 0, width, height);
  48. }
  49. void process_input(GLFWwindow *window)
  50. {
  51. if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
  52. {
  53. glfwSetWindowShouldClose(window, true);
  54. }
  55. }
  56. unsigned int *renderInit()
  57. {
  58. unsigned int VAO;
  59. glGenVertexArrays(1, &VAO);
  60. glBindVertexArray(VAO);
  61. float vertices[] = {
  62. 0.0f, 0.5f, 0.0f,
  63. -0.5f, -0.5f, 0.0f,
  64. 0.5f, -0.5f, 0.0f
  65. };
  66. unsigned int VBO;
  67. glGenBuffers(1, &VBO);
  68. glBindBuffer(GL_ARRAY_BUFFER, VBO);
  69. glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
  70. glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0);
  71. glEnableVertexAttribArray(0);
  72. const char *vertexShaderSource =
  73. "#version 330 core\n"
  74. "layout (location = 0) in vec3 aPos;\n"
  75. "void main()\n"
  76. "{\n"
  77. "gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
  78. "}\0";
  79. unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
  80. glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
  81. glCompileShader(vertexShader);
  82. checkCompile(vertexShader);
  83. const char *fragmentShaderSource =
  84. "#version 330 core\n"
  85. "out vec4 FragColor;"
  86. "void main()\n"
  87. "{\n"
  88. "FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
  89. "}\0";
  90. unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
  91. glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
  92. glCompileShader(fragmentShader);
  93. checkCompile(fragmentShader);
  94. unsigned int shaderProgram = glCreateProgram();
  95. glAttachShader(shaderProgram, vertexShader);
  96. glAttachShader(shaderProgram, fragmentShader);
  97. glLinkProgram(shaderProgram);
  98. checkProgram(shaderProgram);
  99. glDeleteShader(vertexShader);
  100. glDeleteShader(fragmentShader);
  101. return new unsigned int[3]{shaderProgram, VAO, VBO};
  102. }
  103. void render(unsigned int shaderProgram, unsigned int VAO)
  104. {
  105. glClearColor(0.2, 0.3, 0.3, 1.0);
  106. glClear(GL_COLOR_BUFFER_BIT);
  107. glUseProgram(shaderProgram);
  108. glBindVertexArray(VAO);
  109. glDrawArrays(GL_TRIANGLES, 0, 3);
  110. }
  111. bool checkCompile(unsigned int shader)
  112. {
  113. int success;
  114. char infoLog[512];
  115. glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
  116. if (!success)
  117. {
  118. glGetShaderInfoLog(shader, 512, NULL, infoLog);
  119. std::cout << "ERROR::SHADER::COMPILATION_FAILED\n"
  120. << infoLog << std::endl;
  121. return false;
  122. }
  123. return true;
  124. }
  125. bool checkProgram(unsigned int shaderProgram)
  126. {
  127. int success;
  128. char infoLog[512];
  129. glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
  130. if (!success)
  131. {
  132. glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
  133. return false;
  134. }
  135. return true;
  136. }

代码基于CMake构建,CMakeLists.txt如下:

  1. cmake_minimum_required(VERSION 3.3)
  2. set(CMAKE_C_STANDARD 11)
  3. set(CMAKE_CXX_STANDARD 14)
  4. project(HelloTriangle)
  5. find_package(glfw3 REQUIRED)
  6. find_package( OpenGL REQUIRED )
  7. include_directories( ${OPENGL_INCLUDE_DIRS} )
  8. file(GLOB project_file glad.c main.cpp)
  9. add_executable(${PROJECT_NAME} ${project_file})
  10. target_link_libraries(${PROJECT_NAME} ${OPENGL_LIBRARIES} glfw)
  • main.cpp是上述代码的文件名

使用CMake构建并编译运行:

  1. $ cmake build .
  2. $ make
  3. $ ./HelloTriangle

运行结果如下:

10. 参考资料

[1]你好,三角形 - LearnOpenGL CN (learnopengl-cn.github.io)

[2]【Learn OpenGL笔记】三角形(Triangle) - 知乎 (zhihu.com)

[3]基于Ubuntu搭建OpenGL开发环境 - 当时明月在曾照彩云归 - 博客园 (cnblogs.com)

基于C++的OpenGL 01 之Hello Triangle的更多相关文章

  1. 基于Cocos2d-x学习OpenGL ES 2.0之多纹理

    没想到原文出了那么多错别字,实在对不起观众了.介绍opengl es 2.0的不多.相信介绍基于Cocos2d-x学习OpenGL ES 2.0之多纹理的,我是独此一家吧.~~ 子龙山人出了一个系列: ...

  2. 基于Cocos2d-x学习OpenGL ES 2.0系列——纹理贴图(6)

    在上一篇文章中,我们介绍了如何绘制一个立方体,里面涉及的知识点有VBO(Vertex Buffer Object).IBO(Index Buffer Object)和MVP(Modile-View-P ...

  3. 基于Cocos2d-x学习OpenGL ES 2.0系列——使用VBO索引(4)

    在上一篇文章中,我们介绍了uniform和模型-视图-投影变换,相信大家对于OpenGL ES 2.0应该有一点感觉了.在这篇文章中,我们不再画三角形了,改为画四边形.下篇教程,我们就可以画立方体了, ...

  4. Python基于回溯法解决01背包问题实例

    Python基于回溯法解决01背包问题实例 这篇文章主要介绍了Python基于回溯法解决01背包问题,结合实例形式分析了Python回溯法采用深度优先策略搜索解决01背包问题的相关操作技巧,需要的朋友 ...

  5. 1、基于MFC的OpenGL程序

    首先,使用的库是GLUT以及GLAUX,先下载两者,添加查找路径以及链接   一.单文本文件   工程openGLMFC 1.创建单文本文件   2.添加路径.链接 方法如之前篇章所示, 链接库为op ...

  6. 【游戏开发】基于VS2017的OpenGL开发环境搭建

    一.简介 最近,马三买了两本有关于“计算机图形学”的书籍,准备在工作之余鼓捣鼓捣图形学和OpenGL编程,提升自己的价值(奔着学完能涨一波工资去的).俗话说得好,“工欲善其事,必先利其器”.想学习图形 ...

  7. 基于MFC的OpenGL程序<转>

    原贴地址:https://www.cnblogs.com/pinking/p/6180225.html 首先,使用的库是GLUT以及GLAUX,先下载两者,添加查找路径以及链接   一.单文本文件   ...

  8. 基于Cocos2d-x学习OpenGL ES 2.0系列——你的第一个立方体(5)

    在上篇文章中,我们介绍了VBO索引的使用,使用VBO索引可以有效地减少顶点个数,优化内存,提高程序效率. 本教程将带领大家一起走进3D--绘制一个立方体.其实画立方体本质上和画三角形没什么区别,所有的 ...

  9. 基于Cocos2d-x学习OpenGL ES 2.0系列——你的第一个三角形(1)

    前言 在本系列教程中,我会以当下最流行的2D引擎Cocos2d-x为基础,介绍OpenGL ES 2.0的一些基本用法.本系列教程的宗旨是OpenGL扫盲,让大家在使用Cocos2d-x过程中,知其然 ...

  10. 基于对话框的Opengl框架

    转自:http://blog.csdn.net/longxiaoshi/article/details/8238933 12-11-29 14:55 1198人阅读 评论(6) 收藏 举报  分类: ...

随机推荐

  1. 【论文解读】NIPS 2021-HSWA: Hierarchical Semantic-Visual Adaption for Zero-Shot Learning.(基于层次适应的零样本学习)

    作者:陈使明 华中科技大学

  2. 【实时数仓】Day06-数据可视化接口:接口介绍、Sugar大屏、成交金额、不同维度交易额(品牌、品类、商品spu)、分省的热力图 、新老顾客流量统计、字符云

    一.数据可视化接口介绍 1.设计思路 后把轻度聚合的结果保存到 ClickHouse 中后,提供即时的查询.统计.分析 展现形式:用于数据分析的BI工具[商业智能(Business Intellige ...

  3. view-design table的renderHeader中hover添加checkboxGroup遇到的问题

    示例demo https://codepen.io/sphjy/pen/mdKaQJZ 问题见图 勾选多个复选框 on-change事件返回的数据只有当前点击的这一项,而且设置在checkboxGro ...

  4. Golang Gorm 封装 分页查询 Where Order 查询

    说说为什么写Gorm,因为公司新项目需要,研究了下Go下的gorm.对于一个项目首先考虑的问题,就是封装一些常用的工具方法,例如多参数查询 where or Like 还有order by Limit ...

  5. 用 while 生成猜数字

    import java.util.Random; import java.util.Scanner; public class zy2 { public static void main(String ...

  6. 多线程爬取wallhaven

    前言 最近整理自己的项目时,发现之前自己写的爬取wallhaven网站顿时有来的兴趣决定再写一遍来回顾自己以前学的知识 网站地址:"https://wallhaven.cc/" 1 ...

  7. 【爬虫+数据分析+数据可视化】python数据分析全流程《2021胡润百富榜》榜单数据!

    目录 一.爬虫 1.1 爬取目标 1.2 分析页面 1.3 爬虫代码 1.4 结果数据 二.数据分析 2.1 导入库 2.2 数据概况 2.3 可视化分析 2.3.1 财富分布 2.3.2 年龄分布 ...

  8. vue3+ts 全局事件总线mitt

    Mitt 在vue3中 $ on,$off 和 $once 实例方法已被移除,组件实例不再实现事件触发接口,因此大家熟悉的EventBus便无法使用了.然而我们习惯了使用EventBus,对于这种情况 ...

  9. CVE-2022-26923 Windows域提权漏洞

    前言 Active Directory 域服务,是一种目录服务,提供了存储目录数据信息以及用户相关的一些密码,电话号码等等一些数据信息,且可让用户和管理员使用这些数据,有利于域管理员对用户的数据信息进 ...

  10. 完整数据分析流程:Python中的Pandas如何解决业务问题

    开篇 作为万金油式的胶水语言,Python几乎无所不能,在数据科学领域的作用更是不可取代.数据分析硬实力中,Python是一个非常值得投入学习的工具. 这其中,数据分析师用得最多的模块非Pandas莫 ...