OpenGL学习脚印: 环境搭建

2023-11-01

写在前面
如果你正在阅读本文,那么你和我一样可能喜欢图形学或者游戏编程,想尝试编写自己的图形App :)。图形学的初学者往往非常着急,希望能立即编写出丰富生动、逼真又富有交互性的3D应用,至少我是这样。

笔者翻看了图形学教材,红宝书,3D数学基础等大部头书后,感觉到根据教材或者网络tutorial编写简单的应用程序并不困难,难点在于能够保持足够热情,坚持学习,使自己对图形学原理有更深更完整理解,培养自主学习更高级话题的能力和解决实际问题的能力。
贪多嚼不烂,如果过于急躁,学习教材或者网络tutorial就会感觉要么太简单,要么太难,难以持之以恒。因此我写下这一系列博客,目的在于保持自己的热情,保持自己的节凑,以OpenGL编程为切入点,扎实学习图形学原理,从而增强图形编程能力。

在学习OpenGL编程的过程中,当前感触比较深的是:宁愿暂时跳过一些复杂的数学背景知识,也不要因此损害到热情,兴趣非常重要。因此和我一样对图形学有热情的同路人,如果遇到难点一定不要轻易放弃,如果一次不能完全理解,可以标记下来,回过头来再做研究。
本系列将使用 windows & visual studio 2013 & C++ 作为开发平台,笔者建议初学者也是如此,尽量不要把精力耗费在linux下库文件编译和显卡程序安装,编程语言细节(例如C++11新特性,java语言绑定版本)等琐碎事情上,更多地关注图形原理和实现。

话不絮烦,下面开始本节内容。

通过本节,可以了解到:

  • OpenGL基本特点
  • Windows下OpenGL开发环境搭建
  • 第一个OpenGL程序-准备Context

OpenGL基本特点

OpenGL是一个跨语言和跨平台的图形编程接口,这个API用来与GPU交互,实现基于硬件加速的图形渲染。OpenGL实际上是一个抽象的图形绘制规范,尽管可以由软件来完全实现,但它被设计为大部分或者全部由硬件来实现。OpenGL是一个绘图规范,不同的显卡厂商负责具体实现,因此实现的具体程序也不尽相同。OpenGL是跨平台的,支持PC端,嵌入式设备,手机设备的渲染;同时也是跨语言的,它底层使用C语言实现,支持java语言、Python、javaScript绑定。
OpenGL包括旧的版本和现代版本 旧版本的绘图功能大多数在GPU内完成,某些功能无法实现,现代版本使用着色器程序提供了更大的灵活性。旧式版本是指1.x和2.x版本,现代版本是指3.x以上版本。两者函数的一个区别如下:

  • 旧版函数: glBegin, glVertex3f, glLightfv, glPushMatrix
  • 现代版函数:glDrawArrays, glVertexAttribPointer, glUniform, glCreateShader.

当3.2规范发布后,OpenGL支持两种profile:

  • Core Profile, 仅支持未被废弃特性。
  • The Compatibility Profile (兼容模式) 支持所有特性
    尽管在兼容模式下,仍然可以运行那些已经被废弃的API编写的程序,但为了程序以后的兼容性,我们将使用OpenGL3.3版本,开启core profile。
    想了解更多,关于为什么不要使用旧版OpenGL,可以参考Don’t use old OpenGL,以及Learning Modern OpenGL。关于如何从旧版转移到现代版本,可以参考Transition to Core Profile.

一个值得注意的问题是,为了保持跨平台,OpenGL规范中并未涉及到OpenGL context的创建和管理部分,这部分的实现依赖于不同系统的窗口管理系统。因此在编写OpenGL程序时需要借助第三方库完成与窗口系统的交互,创建绘制上下文对象(render context)。

开发环境搭建

上面已经提到OpenGL的具体实现由显卡驱动厂商负责,那么不同的厂商实现的库可能有所不同,有的函数可能没有实现,有的包含拓展的实现,而且厂商的实现一般通过动态链接库来提供。一般地使用一个函数前来查询函数是否实现了,而且要关心版本问题,这些细节比较繁琐,可以交给第三方库GLEW来实现。
同时与操作系统的窗口系统交互,创建绘制上下文对象,也是与具体操作系统相关的,可以交给第三方库GLFW来实现(当然还有其他选择,包括 freeglut,SDL,GLFW目前是比较推荐的)。
总结为我们需要下载和编译两个第三方库:

  • GLEW 负责绘图函数的获取
  • GLFW 负责创建绘图上下文和窗口系统管理

下载并编译后,获取到两个库的lib和dll文件,我们就可以开始编写第一个程序了。编译源文件的过程,可以选择对应的visual studio版本,然后编译为默认的调试版本即可。如果觉得下载和编译源码费劲,我也为你提供了Windows下编译好的库文件和必要的头文件,点击从github下载库文件

第一个OpenGL程序-准备Context

利用上面编译的两个第三方库,我们可以实现自己的第一个程序,创建用于渲染图形的上下文对象。
一般而言,OpenGL程序的流程大致都是这样(伪代码描述来自open.gl):

#include <libraryheaders>

int main()
{
    initLibraries();   // 初始化相关库
    createWindow(title, width, height); // 创建窗口
    createOpenGLContext(settings); // 创建绘制上下文

    while (windowOpen)  // 游戏主循环
    {
        while (event = newEvent())
            handleEvent(event); // 处理事件

        updateScene(); //更新场景中内容

        drawGraphics(); // 绘制场景
        presentGraphics(); // 交换缓冲区 呈现场景
    }
    freeResource();  // 资源清理
    return 0;
}

根据上述思路,我们开始创建本节的CreateContext程序。

Step1 : 新建项目CreateContext 在项目属性中链接头文件和库文件。在visual studio里面设置即可。
如下图 设置头文件目录和库文件目录:
这里写图片描述

如下图 链接库文件:
这里写图片描述

Step2 添加createContext.cpp文件。
首先我们要包含头文件如下:

// 引入GLEW库 定义静态链接
#define GLEW_STATIC
#include <GLEW/glew.h>
// 引入GLFW库
#include <GLFW/glfw3.h>

这里注意两点:

  • 首先包含glew,因为glew里面会包含gl.h等文件 这些文件是系统中OpenGL库文件
  • 定义GLEW_STATIC表示静态链接glew库,当然也可以使用GLEW的动态链接库dll

包含头文件后,注意我们需要首先创建绘制上下文,然后才能初始化glew库(想了解更多请参考Initializing GLEW)。另外注意GLFW和GLEW初始化的返回值,对于出错情况要做好处理。
在上文提到,我们应该使用OpenGL core profile, 通过glfw的函数这样实现:

glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

这里提示glfw开启OpenGL 3.3版本.
为了保持程序简单,不涉及放大缩小等问题,这里通过

     glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

使窗口固定大小。同时在绘制场景前,使用

     glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);

为窗口指定显示区域大小,关于这个参数,我们后面会继续深入了解。

最后是窗口事件处理,这里以键盘事件为例,当用户按下Esc键时,退出应用程序,键盘事件在主循环中由glfwPollEvents处理,这个函数处理时会调用键盘事件的回调函数,因此我们需要声明和编写键盘回调函数如下:

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
    {
        glfwSetWindowShouldClose(window, GL_TRUE); // 关闭窗口
    }
}

想了解更多的输入事件和参数详细解释,可以参考glfw Input guide.
一个好的习惯时,程序主循环结束时,释放程序占用的资源,这里我们通过:

glfwTerminate(); // 释放资源

释放GLFW分配的资源。当程序中分配有其他资源时,也需要做相应的回收处理, 这点要注意。
通过上述分析,完整的代码如下(点击从github下载代码):

// 引入GLEW库 定义静态链接
#define GLEW_STATIC
#include <GLEW/glew.h>
// 引入GLFW库
#include <GLFW/glfw3.h>
#include <iostream>


// 键盘回调函数原型声明
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);

// 定义程序常量
const int WINDOW_WIDTH = 800, WINDOW_HEIGHT = 600;


int main(int argc, char** argv)
{

    if (!glfwInit())    // 初始化glfw库
    {
        std::cout << "Error::GLFW could not initialize GLFW!" << std::endl;
        return -1;
    }

    // 开启OpenGL 3.3 core profile
    std::cout << "Start OpenGL core profile version 3.3" << std::endl;
    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(WINDOW_WIDTH, WINDOW_HEIGHT,
        "Demo of createContext", NULL, NULL);
    if (!window)
    {
        std::cout << "Error::GLFW could not create winddow!" << std::endl;
        glfwTerminate();
        return -1;
    }
    // 创建的窗口的context指定为当前context
    glfwMakeContextCurrent(window);

    // 注册窗口键盘事件回调函数
    glfwSetKeyCallback(window, key_callback);

    // 初始化GLEW 获取OpenGL函数
    glewExperimental = GL_TRUE; // 让glew获取所有拓展函数
    GLenum status = glewInit();
    if (status != GLEW_OK)
    {
        std::cout << "Error::GLEW glew version:" << glewGetString(GLEW_VERSION) 
            << " error string:" << glewGetErrorString(status) << std::endl;
        glfwTerminate();
        return -1;
    }

    // 设置视口参数
    glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);

    // 开始游戏主循环
    while (!glfwWindowShouldClose(window))
    {
        glfwPollEvents(); // 处理例如鼠标 键盘等事件

        // 清除颜色缓冲区 重置为指定颜色
        glClearColor(0.18f, 0.04f, 0.14f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // 这里填写场景绘制代码 本节不绘制物体

        glfwSwapBuffers(window); // 交换缓存
    }
    glfwTerminate(); // 释放资源
    return 0;
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
    {
        glfwSetWindowShouldClose(window, GL_TRUE); // 关闭窗口
    }
}

运行程序,效果如下:
这里写图片描述

这里我们没有绘制任何内容,在下一节将会绘制我们的第一个图形。

使用模板快速获取本节工程

有网友留言索要整个工程,因为github上面上传二进制的VS工程不太合适,这里制作了一个方便的模板供使用,可以从我的github下载。模板使用方法:

Step1: 将模板getting-started.zip拷贝值VS的项目模板目录,如下图所示:
安装模板

Step2: 使用模板新建工程,如下图:

新建工程

Step3: 将libraries拷贝至新建项目的同级目录下。
拷贝libraries

Step4: 编译运行新建工程即可。

推荐阅读:

[1]: OpenGL wikipedia
[2]: Don’t use old OpenGL
[3]: Learning Modern OpenGL
[4]: NVIDIA opengl

参考资料

[1] Creating a window in http://www.learnopengl.com/
[2] Window and OpenGL context in open.gl

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OpenGL学习脚印: 环境搭建 的相关文章

随机推荐

  • Varnish的简单配置及使用

    一 Varnish的简单介绍 1 varnish是什么 Varnish是一个web加速器 被安装在web应用程序前面 缓存web应用程序 并响应用户请求 是一款具有高性能的开源HTTP加速器 具有方向代理及缓存的功能 功能与squid服务器
  • 基于STM32emWin5.32的移植

    基于STM32emWin532版本的移植 移植前提 1 下载所需源码 2 直接拷贝Cubemx的STemwin 文件说明 2 需已完成底层绘制LCD的驱动调试 移植 在项目工程目录增加以下文件夹及文件 配置单次图像刷写最大允许字节 配置驱动
  • 【ARIMA-WOA-LSTM】差分自回归移动平均方法-鲸鱼优化算法-LSTM预测研究(python代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Python代码实现 1 概述 差分自回归移动平均模型 ARIMA 是一种
  • 加速读取——三式(getchar,std::ios::sync_with_stdio(false)、cin.tie(0))

    在一些题目中会出现数据输入很大的情况 如果我们运用不好读取数据工具的话 那么很可能会在读取数据上花费较多的时间 甚至还没读完就超时了 读取速度 cin lt cin 关闭流同步 lt scanf lt getchar cin 关闭流同步 为
  • mysql让自增的id重新从0开始的命令

    1 清空表 2 TRUNCATE TABLE 表名
  • Linux下的压缩zip,解压缩unzip命令详解及实例

    http www cnblogs com zdz8207 p 3765604 html
  • Python爬虫分布式架构 - Redis/RabbitMQ工作流程介绍

    在大规模数据采集和处理任务中 使用分布式架构可以提高效率和可扩展性 本文将介绍Python爬虫分布式架构中常用的消息队列工具Redis和RabbitMQ的工作流程 帮助你理解分布式爬虫的原理和应用 为什么需要分布式架构 在数据采集任务中 单
  • 列举工作中常用的几个git命令?

    列举工作中常用的几个git命令 新增文件的命令 git add file或者git add 提交文件的命令 git commit m或者git commit a 查看工作区状况 git status s 拉取合并远程分支的操作 git fe
  • React16入门

    环境搭建 安装node 安装官方脚手架create react app npm install g create react app 创建项目 进入文件夹 进入cmd运行命令 mkdir ReactDemo 创建ReactDemo文件夹 c
  • 固态硬盘和机械硬盘的区别

    固态硬盘的应用范围要比机械硬盘的使用范围更广泛 固态硬盘在电子世界中起着非常重要的部件 而机械硬盘使用范围就不像固态硬盘那样灵活了 目前固态硬盘中最大的容量体积为1 6TB 传言IBM公司开始测试4TB的高速固态硬盘组了 和机械硬盘相比按T
  • jmeter配置java版本,Jmeter 在windows下的安装配置方法

    Jmeter通常用于并发测试 本文介绍Jmeter工具的安装步骤 2 安装jdk 下载完成后 双击安装 2 步骤二 配置jdk环境变量 右键计算机属性 gt 高级系统设置 gt 系统属性 gt 高级 gt 环境变量 gt 添加如下的系统变量
  • 2021-12-5 《聪明的投资者》学习笔记

    摘自 聪明的投资者 The Intelligent Investor 第4版 本杰明 格雷厄姆 第一章 投资与投机 聪明投资者的预期收益 第二章 投资者与通货膨胀 第三章 一个世纪的股市历史 1972年年初的股价水平 股市周期性 股价 利润
  • 【千律】C++基础:获取数组中的最大值和最小值

    include
  • springboot中的json、gson、fastjson如何使用与日期格式转换

    关于如何引用json gson fastjson srpngboot中默认用的是json格式 如果需要使用gson和fastjson其中一种格式的话 首先需要在pom文件中排除对json格式的依赖 再去引入你想要gson或者fastjson
  • CSS样式——div居中方法

    1 绝对居中 给div设置样式 div默认显示位置为body的左上方 width 400px height 300px background color orange 如下图所示 首先给div添加绝对定位 并设置上下左右边距为0 然后使用m
  • 【git国内镜像地址】【速度快】

    重申一下 此处说的是git客户端下载 而不是github网站 github网站请绕路 git官网地址 官网地址 大家可以打开试试 每秒几十或者只有几kb 下完要一个月 大家可以试试国内地址 5秒之内下载完成 git国内镜像地址 git国内镜
  • C++泛型基础

    1 泛型的基本思想 泛型编程 Generic Programming 是一种语言机制 通过它可以实现一个标准的容器库 像类一样 泛型也是一种抽象数据类型 但是泛型不属于面向对象 它是面向对象的补充和发展 在面向对象编程中 当算法与数据类型有
  • c语言fwrite函数的总结

    头文件 include
  • 华东地区响应最快的DNS服务排名

    223 5 5 5 阿里DNS 约 29 ms 114 114 115 115 114DNS 约 30 ms 114 114 115 119 114DNS 约 30 ms 114 114 114 114 114DNS 约 31 ms 114
  • OpenGL学习脚印: 环境搭建

    写在前面 如果你正在阅读本文 那么你和我一样可能喜欢图形学或者游戏编程 想尝试编写自己的图形App 图形学的初学者往往非常着急 希望能立即编写出丰富生动 逼真又富有交互性的3D应用 至少我是这样 笔者翻看了图形学教材 红宝书 3D数学基础等