【C++】OpenGL:计算机图形学OpenGL基础及环境配置

😏1. OpenGL介绍

OpenGL(Open Graphics Library)是一个跨平台的图形编程接口,用于渲染2D和3D图形。它提供了一组函数和命令,允许开发者通过编程方式控制图形硬件,从而创建高性能的图形应用程序。

以下是一些OpenGL的特点:

1.跨平台:OpenGL 是跨平台的,可以在各种操作系统和设备上运行,包括 Windows、Linux、Mac、iOS 和 Android 等。

2.低级别接口:OpenGL 是一个底层的图形编程接口,提供了对图形硬件的直接访问。它允许开发者直接操作图形渲染管线,控制顶点和像素的处理过程。

3.状态机:OpenGL 是基于状态机的编程模型。开发者通过设置不同的状态(例如颜色、材质、光照等),然后调用相应的绘制命令,来渲染图形对象。

4.二维和三维图形:OpenGL 支持绘制和处理2D和3D图形。它提供了基本的几何图元(如点、线、三角形),以及矩阵变换和投影等功能,使开发者能够创建复杂的图形场景。

5.着色器编程:OpenGL 使用着色器编程来控制图形渲染过程。着色器是运行在图形硬件上的小型程序,用于处理顶点和像素的计算和变换。开发者可以使用 GLSL(OpenGL Shading Language)编写自定义的着色器程序。

6.扩展性:OpenGL 具有可扩展性,支持各种扩展功能和特性。开发者可以利用扩展来实现更高级的图形效果和功能,满足特定的应用需求。

OpenGL 在游戏开发、计算机图形学、科学可视化、虚拟现实(VR)等领域得到广泛应用。它提供了强大的图形处理能力,允许开发者创建出具有高度交互性和视觉效果的应用程序。

官网:https://opengl.org/

学习网站:https://learnopengl-cn.github.io/

OpenGL最流行的几个库有GLUT、SDL、SFML、Vulkan和GLFW等,常见的搭配有glfw+glad+glm,下面主要用GLFW。

OpenGL基础

由于OpenGL是一个图形API,并不是一个独立的平台,它需要一个编程语言来工作,在这里我们使用的是C++。并不需要你是一个C++专家,但至少能写出比一个“Hello World”复杂的程序。

除此之外,我们也将用到一些数学知识(线性代数、几何、三角学),但大部分的功能甚至都不需要你理解这些数学知识,只要你会使用就行。(数学与程序设计的关系)

OpenGL一般被认为是一个API,包含了一系列可以操作图形、图像的函数。然而,OpenGL本身并不是一个API,它仅仅是一个由Khronos组织制定并维护的规范(Specification)。

OpenGL规范严格规定了每个函数该如何执行,以及它们的输出值。至于内部具体每个函数是如何实现(Implement)的,将由OpenGL库的开发者自行决定(实际的OpenGL库的开发者通常是显卡的生产商)。

立即渲染模式与核心模式 早期的OpenGL使用立即渲染模式(Immediate mode),这个模式下绘制图形很方便。OpenGL的大多数功能都被库隐藏起来,开发者很少有控制OpenGL如何进行计算的自由。在这种情况下,从OpenGL3.2开始,规范文档开始废弃立即渲染模式,并鼓励开发者在OpenGL的核心模式(Core-profile)下进行开发。

当使用OpenGL的核心模式时,OpenGL迫使我们使用现代的函数。现代函数要求使用者真正理解OpenGL和图形编程,它有一些难度,然而提供了更多的灵活性,更高的效率,更重要的是可以更深入的理解图形编程。

使用扩展的代码大多看上去如下:

代码语言:javascript
复制
if(GL_ARB_extension_name)
{
    // 使用硬件支持的全新的现代特性
}
else
{
    // 不支持此扩展: 用旧的方式去做
}

状态机 OpenGL自身是一个巨大的状态机(State Machine):一系列的变量描述OpenGL此刻应当如何运行。OpenGL的状态通常被称为OpenGL上下文(Context)。我们通常使用如下途径去更改OpenGL状态:设置选项,操作缓冲。最后,我们使用当前OpenGL上下文来渲染。

对象 OpenGL库是用C语言写的,同时也支持多种语言的派生,但其内核仍是一个C库。由于C的一些语言结构不易被翻译到其它的高级语言,因此OpenGL开发的时候引入了一些抽象层。“对象(Object)”就是其中一个。

在OpenGL中一个对象是指一些选项的集合,它代表OpenGL状态的一个子集。比如,我们可以用一个对象来代表绘图窗口的设置,之后我们就可以设置它的大小、支持的颜色位数等等。可以把对象看做一个C风格的结构体(Struct):

代码语言:javascript
复制
struct object_name {
    float  option1;
    int    option2;
    char[] name;
};

OpenGL函数库 OpenGL相关的函数库有核心库(gl),实用库(glu),辅助库(aux)、实用工具库(glut),窗口库(glx、agl、wgl)和扩展函数库等。gl是核心,glu是对gl的部分封装。glx、agl、wgl 是针对不同窗口系统的函数。glut是为跨平台的OpenGL程序的工具包。扩展函数库是硬件厂商为实现硬件更新利用OpenGL的扩展机制开发的函数。

窗口管理

GLUT:glut或者freegult主要是OpenGL 1.0的基本函数功能,前面几节主要用的这个库。 GLFW:glfw的开发目的是用于替代glut的。它是一个轻量级的,开源的,跨平台的library。中文学习网站用的这个库。

函数加载

GLEW:glew是使用OpenGL 2.0之后的一个工具函数。中文学习网站用的这个库。 GLAD:glad是继gl3w,glew之后,当前最新的用来访问OpenGL规范接口的第三方库。简单说glad是glew的升级版。

部件工具

Qt wxWidgets Imgui

了解了OpenGL的基础知识后,下面就开始创建一些很酷的图形吧。

😊2. 环境安装与配置

主要包括glfw、glad、imgui等库,包含vs、cmake配置。

windows+vs+msvc

Windows + Visual Studio 2017 可以通过安装nupengl程序包的方式。首先,新建一个VS空项目,我这里命名opengl_demo,然后打开项目->管理NuGet程序包,搜索nupengl,安装nupengl.core程序包即可。

在这里插入图片描述

在我们画出出色的效果之前,首先要做的就是创建一个OpenGL上下文(Context)和一个用于显示的窗口。

GLFW是一个专门针对OpenGL的C语言库,它提供了一些渲染物体所需的最低限度的接口。中文学习网是用源码编译的,包括如何获取、编译、链接GLFW库,这里我用的二进制包,对于初学者来说可以更快的验证。

与之前配置nupengl程序包一样,先打开管理程序包,安装glfw:

在这里插入图片描述

GLAD是一个开源的库,它能解决一些繁琐的问题。GLAD的配置与大多数的开源库有些许的不同,是采用在线服务的。打开这个网站:https://glad.dav1d.de/

将语言(Language)设置为C/C++,在API选项中,选择3.3以上的OpenGL(gl)版本(我们的教程中将使用3.3版本,但更新的版本也能用)。之后将模式(Profile)设置为Core,并且保证选中了生成加载器(Generate a loader)选项。现在可以先(暂时)忽略扩展(Extensions)中的内容。都选择完之后,点击生成(Generate)按钮来生成库文件。

GLAD现在应该提供给你了一个zip压缩文件,包含两个头文件目录,和一个glad.c文件。将两个头文件目录(gladKHR)复制到你的Include文件夹中(并在工程中将include添加到包含目录),并添加glad.c文件到你的工程中。

windows+clion+cmake

下载好glfw的二进制包,并生成glad文件后,开始cmake配置。然后可以新建一个env的环境目录,将库相关的头文件和dll放在环境目录里,如:

在这里插入图片描述
代码语言:javascript
复制
# CMakeLists.txt示例
cmake_minimum_required(VERSION 3.19)
project(opengl_demo)

set(CMAKE_CXX_STANDARD 14)

include_directories("./env/include")
link_directories("./env/lib")

add_executable(opengl_demo main.cpp glad.c)
target_link_libraries(opengl_demo glfw3.dll)

然后就可以正常编译了。

ubuntu+cmake

待补充

😆3. 应用示例

下面就放一个学习网的创建窗口的简单示例,可以测试环境是否安装成功:

代码语言:javascript
复制
#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <iostream>

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);

// settings 全局变量
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

int main()
{
// glfw: initialize and configure
// ------------------------------
glfwInit(); //初始化GLFW
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef APPLE
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, &#34;LearnOpenGL&#34;, NULL, NULL);	//窗口对象
if (window == NULL)
{
    std::cout &lt;&lt; &#34;Failed to create GLFW window&#34; &lt;&lt; std::endl;
    glfwTerminate();
    return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

// glad: load all OpenGL function pointers 初始化glad
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
    std::cout &lt;&lt; &#34;Failed to initialize GLAD&#34; &lt;&lt; std::endl;
    return -1;
}

// render loop 渲染循环
// -----------
while (!glfwWindowShouldClose(window))
{
    // input
    // -----
    processInput(window); //调用输入控制

    // render
    // ------
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
    // -------------------------------------------------------------------------------
    glfwSwapBuffers(window);
    glfwPollEvents();
}

// glfw: terminate, clearing all previously allocated GLFW resources. 释放资源
// ------------------------------------------------------------------
glfwTerminate();
return 0;

}

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly 输入控制
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes 视窗大小
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
// make sure the viewport matches the new window dimensions; note that width and
// height will be significantly larger than specified on retina displays.
glViewport(0, 0, width, height);
}