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)并加载数据:

float vertices[] = {
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
};
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

4. 链接属性数据

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

unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO); float vertices[] = {
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
};
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0);

5. 创建顶点着色器

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

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

6. 创建片段着色器

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

const char *fragmentShaderSource =
"#version 330 core\n"
"out vec4 FragColor;"
"void main()\n"
"{\n"
"FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\0"; unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
glCompileShader(fragmentShader);

7. 链接着色器

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

unsigned int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);

8. 绘制

开始(循环)绘制:

glClearColor(0.2, 0.3, 0.3, 1.0);
glClear(GL_COLOR_BUFFER_BIT); glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);

9. 完整代码

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

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream> void framebuffer_size_callback(GLFWwindow *window, int width, int height);
void process_input(GLFWwindow *window);
unsigned int *renderInit();
void render(unsigned int shaderProgram, unsigned int VAO);
bool checkCompile(unsigned int shader);
bool checkProgram(unsigned int shaderProgram); int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
GLFWwindow *window = glfwCreateWindow(800, 600, "hello triangle", nullptr, nullptr); if (window == nullptr)
{
std::cout << "Faild to create window" << std::endl;
glfwTerminate();
}
glfwMakeContextCurrent(window); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Faild to initialize glad" << std::endl;
return -1;
}
glad_glViewport(0, 0, 800, 600);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); unsigned int *arr = renderInit();
while (!glfwWindowShouldClose(window))
{
process_input(window); // render std::cout << arr[0] << " " << arr[1] << " " << arr[2] << std::endl;
render(arr[0], arr[1]); glfwSwapBuffers(window);
glfwPollEvents();
} glDeleteProgram(arr[0]);
glDeleteVertexArrays(1, &arr[1]);
glDeleteBuffers(1, &arr[2]); glfwTerminate();
return 0;
} void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
glViewport(0, 0, width, height);
} void process_input(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
{
glfwSetWindowShouldClose(window, true);
}
} unsigned int *renderInit()
{
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO); float vertices[] = {
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
};
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0); const char *vertexShaderSource =
"#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
"gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0"; unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
glCompileShader(vertexShader);
checkCompile(vertexShader); const char *fragmentShaderSource =
"#version 330 core\n"
"out vec4 FragColor;"
"void main()\n"
"{\n"
"FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\0"; unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
glCompileShader(fragmentShader);
checkCompile(fragmentShader); unsigned int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
checkProgram(shaderProgram); glDeleteShader(vertexShader);
glDeleteShader(fragmentShader); return new unsigned int[3]{shaderProgram, VAO, VBO};
}
void render(unsigned int shaderProgram, unsigned int VAO)
{
glClearColor(0.2, 0.3, 0.3, 1.0);
glClear(GL_COLOR_BUFFER_BIT); glUseProgram(shaderProgram); glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
} bool checkCompile(unsigned int shader)
{
int success;
char infoLog[512];
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(shader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::COMPILATION_FAILED\n"
<< infoLog << std::endl;
return false;
}
return true;
} bool checkProgram(unsigned int shaderProgram)
{
int success;
char infoLog[512];
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
return false;
}
return true;
}

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

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

使用CMake构建并编译运行:

$ cmake build .
$ make
$ ./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. ArcObjects SDK 012 PageLayout和Page

    1.从ArcMap角度看结构 一个Mxd文件包含一个PageLayout对象和一个多多个Map对象,每个Map会被包成MapFrame,添加到PageLayout中.我们用ArcMap打开一个mxd文 ...

  2. ABP AutoMapper与自定义Mapping

    对象映射 在工作中,需要将相似的对象映射到另一个对象,这样我们来看一个最繁琐的映射方式 例: public class UserAppService : ApplicationService { pr ...

  3. <二>vector向量容器

    底层数据结构:动态开辟的数组,每次以原始空间2倍扩容 vector vec; 增加 vec.push_back(100);容器末尾加元素 时间负责度O(1) 可能导致容器扩容 容器中的,对象的构造析构 ...

  4. uniapp 微信小程序 改变头部的信号、时间、电池显示颜色

    修改前 修改后 修改方法:"navigationBarTextStyle":"white"

  5. C/C++随堂笔记

    注释:行注释 块注释: (1)#if 0 #endif (2)/*     */ <>:表示系统文件 <stdlib.h>+syetem 调用windows中的程序 QT中 c ...

  6. .NET6使用NLog向文件、数据库写数据

    1.Nuget 引入 NLog NLog.Web.AspNetCore NLog.Database(写入数据库使用) 2.创建nlog.config 注意数据库连接字符串需要配置TrustServer ...

  7. mac下 idea 注释快捷键冲突

    你好,我是悦创. 博客首发:https://bornforthis.cn/posts/28.html 打开偏好设置,将冲突的快捷键关掉,就可以解决 idea 使用注释快捷键,会打开 help 页面的问 ...

  8. 微软拼音长句模式恢复工具支持Win10 1803

    4月份就有人留言旧微软拼音恢复工具不支持Win10 1803了,我自己也遇到了,但因为没时间搞,勉为其难使用了词组模式的微软拼音几个月,终于在八月份抽个空研究了下,解决了. 这次是因为傻逼大微软改了 ...

  9. Docker 基础 - 1

    镜像 获取镜像 docker pull 查看镜像信息 docker images docker inspect <images id> # 获取镜像的详细信息 搜寻镜像 docker se ...

  10. prettier+ts+eslint+vscode配置代码保存自动格式化,自动remove unsed declaration,delete no-unused-imports

    每天都要开心(▽)哇: 以这个项目为案例 下面是项目的基本情况 What would you like to build? › App with Quasar CLI, let's go! Proje ...