【OpenGL学习笔记①】——openGL flew flgw flut 环境配置 + 绘制出Hello World
关键字:OpenGL安装、环境配置、glew、glfw、glut、Hello World、VS2010、VS2015、VS2017、VS2019。
该文章旨在帮助 计算机图形学 openGL 初学者快速安装和配置 openGL的环境(包括glew和glfw,还有glut【进阶】),并手把手指导如何绘制出第一句 “Hello World!”,不涉及过深的理论知识。
OpenGL总学习目录: https://blog.csdn.net/Wang_Dou_Dou_/article/details/121240714.
文章目录
一、最终成果预览图
二、安装并配置相关文件【openGL、glew、glfw、glut】
1、openGL、glew、glfw、glut之间的关系:
官方定义——OpenGL(Open Graphics Library,译名:开放式图形库)是用于渲染2D、3D矢量图形的跨语言、跨平台的应用程序编程接口(API)。这个接口由近350个不同的函数调用组成,用来绘制从简单的图形比特到复杂的三维景象。
相应的,glew、glfw、glut都用他们自己的全称,这里不在赘述。
这里简单浓缩一下它们三的作用:
glew:就是你要对老版的openGL进行扩展时,你要使用一些新的接口时,就要用这个!
glfw:类似于统一openGL的窗口,窗口管理与glut类似。
glut:openGL的工具包,隐藏平台之间的差别与细节,提供提供统一的方法来处理事件,消息等。
这三个库创建在OpenGL之上,提供了OpenGL本身没有的功能。
2、下载并安装和配置openGL、glew、glfw、glut:
<1> 安装 openGL
首先,openGL 是 Windows 系统里面自带的,我们可以不用再去下载最新版(如下图所示)。直接在此基础上去配置openGL的三个扩展库 glew、glfw、glut 就可以了。
<2> 安装 glew、glfw、glut
官方glew下载地址:https://sourceforge.net/projects/glew/
官方glfw下载地址:https://www.glfw.org/download.html
官方glut下载地址:https://www.opengl.org/resources/libraries/glut/glut_downloads.php#3.6
如果不是专业搞 openGL ,只是学习一下,我这里已经给大家打包好了。按照接下来的操作步骤走保准没问题。
链接:https://pan.baidu.com/s/19WNeJtpk5d2EYT4U66qLWA
提取码:open
<3> 新建C++项目(我用的VS2010)
Microsoft Visual Studio 2012、VS2013、VS2017、VS2015、VS2019都可以,它们的操作都大同小异。
第一步: 新建项目
第二步: 选 Win32 ,并取个项目名字,项目生成位置我选择的是桌面。 我取的名称为 “openGL项目(计算机图形学)”
第三步: 下一步 + 空项目 + 取消 “预编译的 √ √ √ ”
第四步: 新建C++文件,并取个名字,我取的 “test”。【该 .cpp 文件默认会创建在该项目的文件目录里面】
<4> 配置 glew、glfw、glut
第一步: 先不管VS2010(别关它)。这时桌面(项目生成位置我先前选择的是桌面)会产生一个 名为 “openGL项目(计算机图形学)” 的文件夹,旁边的 “openGL配置文件” 是那个百度网盘下载解压得到的。
这时将 “openGL配置文件” 移动到 “openGL项目(计算机图形学)” 的文件夹中,并 要求和 test.cpp 在同一个目录下!,如图所示:
第二步: 接着点开刚刚的VS2010,打开项目的属性
第三步: 点击左边的 “常规”,再点击 “附加包含目录” ,添加 glew、glfw、glut 的头文件进去。操作过程如图所示:
第四步: 点击左边的 “链接器”,再点击 “附加库目录” ,把 glew、glfw、glut 的库目录添加进去。操作过程如图所示:
【注意①:glew 的库目录要选到 Win32 才行】
【注意②:glfw 的库目录要选相应的版本才行,因为我用的VS2010,所以选的lib-vc2010。如果用VS2019,就要选liv-vc2019】
第五步: 点击左边的 “链接器”,再点击 “输入” ,把 glew、glfw、glut的 库目录链接 手敲进去。
【注意:一定要保证“glut32.lib” 在前,“glut.lib”在后。】
最后别忘了点 “应用”。
第六步: 打开项目文件后,点开 “openGL配置文件”,在里面找到 “glew32.dll、glew32.dll、glut.dll、glut32.dll”,并分别复制它们四个, 粘贴位置 要求和 test.cpp 在同一个目录下! 如图所示:
【注意:glfw 的 .dll文件 要选相应的版本才行,因为我用的VS2010,所以选的是lib-vc2010文件夹里面的 .dll文件】
补充:dll 是Dynamic Link Library的缩写,是一个包含可由多个程序,同时使用的代码和数据的动态链接库。
三、第一个简单程序 + 输出 Hello World!
1、第一个简单程序(glew + glfw)
简易版代码:
#include <iostream>
using namespace std;
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
const GLint WIDTH = 800, HEIGHT = 600; // 先设置窗口以及其大小
int main()
{
glfwInit(); //初始化,使用glfw来打开一个窗口
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);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); // 缩放窗口的关闭
GLFWwindow* window_1 = glfwCreateWindow(WIDTH, HEIGHT, "Hello, friend! I'm a openGL window!", nullptr, nullptr);
int screenWidth, screenHeight;
glfwGetFramebufferSize(window_1, &screenWidth, &screenHeight); // 获得实际占用的帧缓存大小
if (nullptr == window_1) //判断窗口输出是否成功
{
cout << "Failed to create GLFW window" << endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window_1); // 捕获当前窗口,准备对当前窗口进行画图
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) // 判断glew初始化是否成功
{
cout << "Failed to initialise GLEW" << endl;
glfwTerminate();
return -1;
}
glViewport(0, 0, screenWidth, screenHeight); // 设置视口的大小
while (!glfwWindowShouldClose(window_1))
{
glfwPollEvents();
glClearColor(0.1f, 0.8f, 0.7f, 1.0f); // 分别是红、绿、蓝、透明度的四个参数。RGB三原色+透明度
glClear(GL_COLOR_BUFFER_BIT);
glfwSwapBuffers(window_1); // 双缓存模式
}
glfwTerminate(); // 如果循环结束:glfw关闭
return 0;
}
运行结果: 一个窗口,涂满了墨绿色,窗口名为“Hello, friend! I’m a openGL window!”。
补充一个便于学习的 详细注释版代码:【功能和简易版相同】
#include <iostream> // C++的标准输入输出头文件
using namespace std;
#define GLEW_STATIC // 预定义:把 glew 这个库放到系统文件夹里面(STATIC:能找到静态库就优先使用静态库)
// 由静态库导出或从静态库导入的函数的签名用关键字 export .从动态库导入的功能必须用 extern __declspec(dllimport).GLEW_STATIC是激活第一种情况的预处理器定义.
#include <GL/glew.h> // 包含“GL”文件夹里面的“glew.h”的头文件
// 程序运行到这一段后,先找到“GL”文件,打开后找到“glew.h”头文件,然后
// 会在“编译”的时候把里面的整段代码复制到下面,只是没有显示出来
#include <GLFW/glfw3.h> // 我们需要和不同的操作系统进行交互,则需要“glfw.h”的头文件
// 先创建一个窗口,然后在窗口里面创建一个“视口”
const GLint WIDTH = 800, HEIGHT = 600; // 先设置窗口以及其大小
// 在openGL里面,数据类型名字前面都有个大写的“GL”,所以“GLint”其实内涵就是“整型int”的意思
// 而openGL里面的函数都以小写的“gl”开头
int main()
{
/*
说明:
glfw 提供的是环境(变量名或函数名以“glfw”或“GLFW”开头的都是)
glew 用来绘图(变量名或函数名以“glew”或“GLFW”开头的都是)
*/
glfwInit(); //初始化,使用glfw来打开一个窗口
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // 设置窗口版本,“MAJOR”代表主版本号
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // 设置窗口版本,“MAJOR”代表副版本号
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // GLFW_OPENGL_PROFILE用告诉窗口,这个版本是为了openGL 做准备的。
// openGL用的版本用“CORE_PROFILE”来表示,指的是3.1以后的版本 新版的
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 苹果系统需要加这条语句(Windows可加可不加)。函数作用:向前面的版本兼容
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); //缩放窗口的关闭
GLFWwindow* window_1 = glfwCreateWindow(WIDTH, HEIGHT, "Hello, friend! I'm a openGL window!", nullptr, nullptr);
// 新建一个窗口,"一支王同学再在此出没!"(第三个参数):设置窗口名字
// 开始为高清屏做设置
int screenWidth, screenHeight;
glfwGetFramebufferSize(window_1, &screenWidth, &screenHeight); // 获得实际占用的帧缓存大小。帧的宽传给screenWidth;帧的高传给screenHeight。
if (nullptr == window_1) //判断窗口输出是否成功,失败则给出一段提示
{
cout << "Failed to create GLFW window" << endl;
glfwTerminate(); // glfw关闭
return -1; // 进程结束
}
glfwMakeContextCurrent(window_1); // 捕获当前窗口,准备对当前窗口进行画图
glewExperimental = GL_TRUE; // 如果程序编译的时候说有问题的时候,再打开这条语句(以前解决问题的一条语句)
if (glewInit() != GLEW_OK) // 判断glew初始化是否成功,看返回值是否成功 失败则给出一段提示
{
cout << "Failed to initialise GLEW" << endl;
glfwTerminate(); // 关闭glfw
return -1;
}
glViewport(0, 0, screenWidth, screenHeight); // 设置视口的大小(帧缓存的大小传进去)
// 原函数声明: glViewport (GLint x, GLint y, GLsizei width, GLsizei height);
// (x ,y)代表视口(正方形)的左下角的坐标。width、height分别代表视口的宽和高
while (!glfwWindowShouldClose(window_1)) // 只要当前窗口不关闭,一直执行这个循环
{
glfwPollEvents(); // 事件相应的命令,作用:捕获所有的事件。
glClearColor(0.1f, 0.8f, 0.7f, 1.0f); // 分别是红、绿、蓝、透明度的四个参数。RGB三原色+透明度(1.0表示不透明,0.1表示完全透明)
// 一般电脑的RGB显示的都是8位,能表示256*256*256=16777216色
glClear(GL_COLOR_BUFFER_BIT); // glClear():使用 glClearColor 中指定的值设定颜色缓存区的值,即将窗口中的每一个像素设置为背景色GL_COLOR_BUFFER_BIT
glfwSwapBuffers(window_1); // 打开双缓存模式(进阶知识),相当于拿出两块“画板”(一块画好的展示在你面前,另一块接着画,反正画好才给你看)
}
glfwTerminate(); // 如果循环结束:glfw关闭
return 0;
}
2、在终端输出 Hello World! (附加内容——glut实现)
简易版代码:
#include<glut.h>
#include<Windows.h>
#define MAX_CHAR 128
void drawString(const char* str) // 屏幕绘制字体函数
{
static int isFirstCall = 1;
static GLuint lists;
glViewport(0,0,600,400); // 设置当前的视口
glColor3f(1.0f, 0.0f, 0.0f); // 设置字体颜色[红色]
glRasterPos2f(0, 0);
if (isFirstCall)
{
isFirstCall = 0;
lists = glGenLists(MAX_CHAR);
wglUseFontBitmaps(wglGetCurrentDC(), 0, MAX_CHAR, lists); // 把每个字符的绘制命令都装到对应的显示列表中
}
for ( ;*str != '\0';++str ) // 调用每个字符对应的显示列表,绘制每个字符
{
glCallList(lists + *str);
}
}
void RenderScene(void)
{
glClear(GL_COLOR_BUFFER_BIT); // 使用 glClearColor 中指定的值设定颜色缓存区的值,即将窗口中的每一个像素设置为背景色
drawString("Hello, World!"); // 画出“Hello, World!”
glFlush(); //清空OpenGL命令缓冲区,强制执行命令缓冲区中所有OpenGL函数
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv); //使用glut库需要进行初始化
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
glutCreateWindow("一支王同学在此出没!"); // 设置窗口名称
glutPositionWindow(100,100); // 设置窗口的位置。
glutReshapeWindow(600,400); // 设置窗口的长和宽
glClearColor(0.05f, 0.35f, 0.7f, 1.0f); // 画背景这句代码是用于清屏的[并画浅蓝色]
glutDisplayFunc(RenderScene); // 设定一个渲染时的(回调)函数RenderScene
glutMainLoop();
return 0;
}
再补充一个便于学习的 详细注释版代码:
#include<glut.h>
#include<Windows.h> // wglUseFontBitmaps是Windows系统特有的函数,所以在使用前需要加入头文件
#define MAX_CHAR 128 // ASCII字符总共只有0到127,一共128种字符
void drawString(const char* str) // 屏幕绘制字体函数
{
static int isFirstCall = 1;
static GLuint lists;
glViewport(0,0,600,400); // 设置当前的视口
glColor3f(1.0f, 0.0f, 0.0f); // 设置字体颜色[红色]
glRasterPos2f(0, 0); // glRasterPos2f(x,y)用于显示[字体]时设置字符的起始位置
if (isFirstCall) { // 如果是第一次调用,执行初始化
isFirstCall = 0; // 申请MAX_CHAR个连续的显示列表编号
lists = glGenLists(MAX_CHAR); // 列表初始化
wglUseFontBitmaps(wglGetCurrentDC(), 0, MAX_CHAR, lists); // 把每个字符的绘制命令都装到对应的显示列表中
/*
函数说明:
Windows系统中,可以使用wglUseFontBitmaps函数来批量的产生显示字符用的显示列表。函数有四个参数:
第一个参数表示是HDC,学过Windows GDI的朋友应该会熟悉这个。如果没有学过,那也没关系,只要知道调用wglGetCurrentDC函数,就可以得到一个HDC。
第二个参数表示第一个要产生的字符,因为我们要产生0到127的字符的显示列表,所以这里填0。
第三个参数表示要产生字符的总个数,因为我们要产生0到127的字符的显示列表,总共有128个字符,我们这里填的是 MAX_CHAR(宏定义)。
第四个参数表示第一个字符所对应显示列表的编号。假如这里填1000,则第一个字符的绘制命令将被装到第1000号显示列表,第二个字符的绘制命令将被装到第1001号显示列表,依次类推。
我们可以先用glGenLists申请128个连续的显示列表编号,然后把第一个显示列表编号填在这里。
*/
}
for ( ;*str != '\0';++str ) // 调用每个字符对应的显示列表,绘制每个字符
{
glCallList(lists + *str);
}
}
void RenderScene(void)
{
glClear(GL_COLOR_BUFFER_BIT); // 使用 glClearColor 中指定的值设定颜色缓存区的值,即将窗口中的每一个像素设置为背景色
drawString("Hello, World!"); // 画出“Hello, World!”
glFlush(); // 清空OpenGL命令缓冲区,强制执行命令缓冲区中所有OpenGL函数
/*
补充说明:
OpenGL内部使用渲染管线来处理命令队列,OpenGL驱动执行这些命令之前,这些命令和申明处在
挂起状态,也就是在排队。因为渲染需要和硬件打交道,而和硬件进行交互的效率非常低,所以一
次性处理一个大的数据块远比多次处理一个个非常小的数据块的效率高的多的多。
屏幕上的各个像素的计算是并行处理的。
*/
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv); // 使用glut库需要进行初始化
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA); // GLUT_SINGLE 的意思是使用单个缓冲窗口,单个缓冲窗口的意思是所有的绘制命令都在窗口绘制的时候一次
// 性执行.可供选择的还有GLUT_DOUBLE,这个方式实际是有一个后台缓冲缓存了所有渲染命令的结果,在全部
// 完成时快速的和屏幕缓冲进行交换,常用于制作动画效果。
glutCreateWindow("一支王同学在此出没!"); // 设置窗口名称
glutPositionWindow(100,100); // 设置窗口的位置。(x,y)表示 该窗口左上角 相对于 电脑屏幕左上角 的相对位置。
glutReshapeWindow(600,400); // 设置窗口的长和宽
glClearColor(0.05f, 0.35f, 0.7f, 1.0f); // 画背景这句代码是用于清屏的[并画浅蓝色]
// 原型是 void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
// GLclampf 是OpenGL定义的一种float数据类型 代表(0,1)之间的浮点数,使用四个GLclampf的
// 浮点数来表示不同的颜色,和windows上的RGBA表示类似,不同的是windows使用(0,255)的整数
// 来表示,不同的 windows能表示 255*255*255 大约 16千万 多种颜色。
glutDisplayFunc(RenderScene); // 设定一个渲染时的(回调)函数RenderScene,GLUT会在窗口需要作出任何绘制的时候调用这个(回调)函数
// 在窗口的尺寸变化或者再次聚焦该窗口的时候也会调用这个回调函数
glutMainLoop(); // 这个函数启动了GLUT框架。在这一步之前做的操作相当于设置属性,并未执行。glutMainLoop只需要被唤醒
// 一次,然后在窗口关闭之前是会一直循环执行。它会处理所有操作系统提供的信息,键盘输入等等,直到你主动
// 关闭程序。
return 0;
}
运行结果:
OK,终于完工了!⭐️ ⭐️ ⭐️
本文查了很多资料,程序也出了很多不可预知的 bug ,有些小 bug 有时要解决一个多小时。昨晚3个小时+今天上下午8个多小时,才写出这篇比较夯实的博文。⏰
如果觉得这篇教程还不错的话,点赞支持一下吧~ 🙈 🙈
下一篇文章链接: 【OpenGL学习笔记②】——OpenGL基础【渲染管线 顶点着色器 片元着色器 VAO VBO 万字总结】.
OpenGL总学习目录: https://blog.csdn.net/Wang_Dou_Dou_/article/details/121240714.
四、参考附录
[1] 《OpenGL笔记16 文字绘制》📓
里面有文本绘制的详细讲解,写得挺不错的一篇博客
链接: https://www.cnblogs.com/mattins/p/4126298.html.
[2] 《OpenGL: 文本显示》📖
链接: https://blog.csdn.net/Augusdi/article/details/7600590.
[3] 《OpenGL入门(一) glfw ,glew,glut,freeglut区别》📚
里面有对四者详细英文定义
链接: https://blog.csdn.net/what951006/article/details/79268897.
[4] 《一张图弄懂opengl的诸多库gl glu glut freeglut glew glfw之间关系》📘
本文章中的绘图灵感来源于这里
链接: https://www.cnblogs.com/chencarl/p/10722839.html.
[5] 《OpenGL HelloWorld》📙
该文中有 glut 入门知识的详细剖析
链接: https://blog.csdn.net/Struugle_Guy/article/details/98957847.
二十多张截图绘制不易,多多支持 🍬 🍬
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)