由于自己不是计算机图形学出身,所以采纳了一些教材、博客、GPT的综合答案,尽可能作为一篇认识图形学,学会OpenGL简单函数库的博客!多多包涵
我会从最基础的显示相关知识逐步导入OpenGL
一、屏幕是如何工作的,为什么电子屏幕能够显示出内容呢?
一般情况下,我们的手机屏幕和电脑屏幕都是有相同的工作原理的,只不过内部的一些硬件和参数有差别,所以可以归类总结,如下:
电子屏幕有几个组件构成:
液晶层:液晶层位于两个透明电极中间,是由两个平行的玻璃或其他材料组成,液晶的物理特性是,当通电的时候,排列变有序,不通电的时候变混乱,阻止光线通过
透明电极:位于液晶层的两侧,通过电流来创建电场,就是给液晶通电用的
色彩滤光器:一种光学器件,用于在光线通过的时候选择性的透过或者阻挡不同波长的光,从而实现对光的颜色的调控
光源:在电子显示屏的背部提供光源,一般是采用阴冷极荧光灯或者Led灯
所以从上面的信息可以知道,我们的电子显示屏工作原理就是在电场的作用下,利用液晶分子的排列方向发生变化,产生点、线、面,使外光源透光率改变(调制),完成电一光变换,再利用R、G、B三基色信号的不同激励,通过红、绿、蓝三基色滤光膜,完成时域和空间域的彩色重显,配合背部灯管构成画面。
二、如何在屏幕上显示我们输入的信息?
通过标题一我们知道,从硬件层面,我们的显示器就是通过电流刺激,使得液晶分子的排列方式产生变化,那我们是能够产生生动的各种画面呢?
1:我们平时在电脑上看电影实际上就是把接收到的各种视频信号(比如广播电视信号,卫星信号,或者光盘存储的视频文件,亦或是在线加载),这些视频信号可以通过显示器的输入接口(HDMI,USB,DP)连接到显示器上
2:视频解码,解码就是把压缩编码的视频数据还原成原始的图像或者声音信号,根据信号源的不同,显示器可能会使用不同的解码器进行解码(包含采样,量化,等步骤),因为我们在录制或者制作视频的时候,为了提高我们的传输效率,视频文件通常会使用压缩编码算法将连续的图像帧进行压缩,会使用汇编等底层硬件语言进行编码,不多冗余。
3:图像处理,解码后的视频图像被传送到图像处理单元,图像处理单元负责对图像进行处理 和优化,以提高图像质量和显示效果
4:帧缓冲,处理后的图像就被存储到了帧缓冲器,也成为帧缓存或者显存,这块区域是一块内存区域,用于存储图像的像素数据,图像帧缓冲器保存了当前要在屏幕上显示的图像帧,每个图像帧由一系列的像素组成,而每个像素则包含有关其颜色和亮度等信息。帧缓冲器通过存储这些像素数据,提供了对图像进行读取、写入和更新的能力。
5:显示控制,根据帧缓冲器中的图像数据,控制电视屏幕上每个像素点的亮度和颜色
6:屏幕刷新,电视屏幕上的每个像素点通过显示控制单元按照刷新率周期刷新屏幕上的像素,使得图像连续显示出来
三、键盘输入为什么能够显示在计算机屏幕上?
1:键盘输入,当在键盘输入文本的时候,触发键盘的相应电路,每个按键都有对应的字符
2:电信号传输,键盘内部的电路会把相应的按键信息转换成电信号,这些电信号通过连接电缆传输到计算机的输入接口
3:操作系统接收,接收到传入的电信号,操作系统符合管理和处理输入信号
4:字符编码,操作系统接收到电信号以后,把电信号转换成相应的字符,字符编码是一一种将字符映射为数字代码的方式,比如ASCII,Unicode等
5:应用程序接收处理,操作系统把字符或者按键发送给应用程序,如文本编辑器,应用程序将对字符进行相应的文本处理
6:图形处理,当应用程序更新文本后,它将通知操作系统和图形处理系统进行图形渲染,把文本转化成图形数据,以及计算和排列文字位置,字体,样式等
7:帧缓冲器更新,如上,我们介绍过帧缓冲器,把存储在帧缓冲器中的像素数据更新
8:显卡处理,显卡从帧缓冲器中读取图像数据,将其转化为电信号发送到显示器
9:显示器输出,显示器接收到电信号后,根据信号指令来更新屏幕上的像素点,最终在显示器上显示出相应的文字
四、为什么我们需要显卡?
从上面的内容我们知道,如果我们只是通过计算机来看视频,或者进行一些逻辑处理,或者显示一些简单的内容,完全可以通过帧缓冲器一样,你只需一个很大内存的内存条,那为什么我们需要引进显卡呢?
从最简单的需求开始,我们先引进一些图形学的知识来解释,假设我们需要在屏幕上绘制一个旋转的立方体
1:我们需要先定义立方体的模型数据,包括顶点坐标、法线向量,纹理坐标等
2:然后需要定义着色器程序,用于计算顶点和片元的属性,比如位置、颜色、光照,所以绘制一个立方体至少需要一个顶点着色器和片元着色器,顶点着色器负责计算顶点的位置和变换,片元着色器负责计算像素的颜色
3:创建帧缓冲器,用来存储图像帧数据的内存区域,如上
4:设置视口和投影矩阵,视口定义了绘制区域的大小和位置,投影矩阵定义了场景的透视或者正交投影,这些参数可以确定立方体在屏幕上的位置和大小
5:在每一帧中更新立方体的旋转变换矩阵,通过修改矩阵参数就能实现不同旋转速度和方向
我们现在来缕一缕,如果交给CPU来处理上述的步骤,也是可以的,但是当有成千上万个旋转的方块呢?就是需要成千上万个重复的顶点坐标变换,像素着色和纹理采样工作,由于CPU中的计算单元占比并不多,也就是ALU,这受限于计算机的设计初衷,CPU的指令比较复杂,可以实现很多核心功能,相当于一个爱因斯坦,而GPU就是相当于普通人,用来做大量简单重复的工作,它内部有大量的计算单元,所以矩阵变换这种小事儿,就交给GPU来做吧!
五、好了,我们有显卡了,可是渲染是什么?
当我们得到了第一块显卡,我们可以用显卡来做一个飞机大战的游戏,就是通过控制屏幕的像素点,那我们就需要了解一些渲染管线的知识了,什么是渲染???什么是渲染管线??
1:渲染,Rendering,又名呈现,绘制,我觉得我们一开始把Rendering这个单词翻译成呈现,可能会有更多人理解它,呈现就是展示,所以我们的渲染就是把我们的一些几何数据,比如顶点坐标计算,光照计算,纹理映射等,转化成为我们能够肉眼看到的图形,就是把一堆数据经过加工,变成一个活灵活现的样子
2:渲染管线,有了渲染以后,自然就需要渲染管线,渲染管线就是对渲染整个流程处理的统称,我们的常规渲染流程包括:
①顶点处理阶段(Vertex Processing Stage),也就是把输入的顶点坐标进行处理,进行几何变换和顶点属性的计算,包括顶点坐标变换,法线计算(法线计算就是通过对模型的顶点坐标进行处理,而获得每个顶点的法线向量,这些法线向量可以用来进行光照计算,阴影计算和视觉效果等计算),颜色和纹理坐标的插值等
②几何处理阶段(Geometry Process Stage),对经过定点处理的几何图元进行处理和组装,包括三角形的裁剪(用于剔除或裁剪掉位于视景体之外的三角形,以提高渲染效率和减少不必要的计算和绘制操作),投影变换(将三维场景投影到二维屏幕上,包括透视投影和正交投影,透视投影是模拟人眼视角的投影方式,呈现近大远小,而正交投影是无论距离相机远近,都不变大小的投影方式),光栅化(将连续的几何形状转化为离散的像素,通过逐像素的处理和插值计算,确定每个像素的最终颜色值。这样可以在屏幕上生成二维图像,并通过渲染管线的后续阶段进行显示)。
③片元处理阶段(Pixel Processing Stage)对光栅化后的像素进行处理,包括对每个像素进行片元着色,纹理采样等操作
④输出合成阶段(Output Assembly Stage),将处理后的像素最终合成为渲染结果,包括将像素写入帧缓冲器中,并应用混合,深度测试,模板测试等结果
六、引入OpenGL!我们来一起创造简单的图形吧!
针对上述的渲染管线过程,我们当然需要用工具来控制,常见的图形学接口包括
OpenGL(Open Graphic Library),DirectX,Vulkan(更加底层),Metal(针对IOS)
我最终选择了OpenGL进行学习,如果你是针对苹果电脑或者苹果手机进行的图形学渲染管线编辑,就需要学习Metal。
好了,既然我们选择了OpenGL,那就来开始学会使用OpenGL!
1:如何使用VS来创建OpenGL的应用程序呢?我们通常会选择GLUT或者GLFW,针对这两种工具库,我都会写好安装方法,步骤就很繁琐了,我会写的很详细,具体选择哪一种看你的需求,如果只是初学者,就安装GLUT就好了!
GLUT(OpenGL Utility Toolkit):
①:该工具简化了OpenGL应用程序的开发过程,能够做到窗口管理,事件处理,基本绘图功能,定时器,菜单等功能,适合初学者学习。但是目前GLUT已经停止维护了,功能有限,官网下载地址:http://www.opengl.org/resources/libraries/glut/glutdlls37beta.zip
②:解压后,在VS的安装目录下,找到VC\Tools\MSVC\版本号\include,创建一个文件夹,名字叫openglfiles,然后把刚解压的glut.h头文件放进去
③:打开VC\Tools\MSVC\版本号\lib的64位文件夹,把glut.lib和glut32.lib放进去
④:再打开C:Windows\SysWOW64(64位),把glut.dll和glut32.dll放进去即可
⑤:新建一个VS项目,打开顶部菜单中的项目,打开管理NuGet程序包,搜索nupengl,随便安装一个,避免繁琐添加头文件路径,只需要每次新建项目安装此程序包即可
GLFW(Graphics Library Framework):
①:一个更为强大和灵活的工具库,提供了更多的功能和控制权。它支持现代OpenGL功能、多窗口管理、输入处理、时间管理、窗口控件等。GLFW具有更好的跨平台支持,适用于开发较为复杂和高级的OpenGL应用程序。官方下载地址:Download | GLFW
②:根据自己的版本进行下载,打开VS安装路径,找到VC\Tools\MSVC\版本号\include,创建GLFW文件夹,然后打开GLFW文件夹glfw-3.3.6.bin.WIN64\include\GLFW,将glfw3.h和glwf3native.h文件夹放到,新建的GLFW文件夹中
③:打开下载的文件夹中的lib-vc2022文件夹glfw-3.3.6.bin.WIN64\lib-vc2019(根据自己版本),然后把glfw3.lib、glfw3_mt.lib、glfw3dll.lib放到VC\Tools\MSVC\版本号\lib的64位文件夹中
④:把glfw3.dll放到C:\Windows\SysWOW64,此时就安装完成了。
2:除此之外,还需要下载GLAD(OpenGL Loader Generator),它是独立的工具库,用于生成OpenGL加载器的工具,加载和管理OpenGL函数指针,自动生成加载OpenGL函数的代码。GLAD简化了加载和管理OpenGL函数指针的过程,使得在使用OpenGL时更加方便和统一
①:官方网站下载:http://glad.dav1d.de/
②:只选择API中的gl和Profile中的Core即可,然后GENERATE
③:下载最后的Zip文件,打开glad\include,将glad和KHR文件夹复制到VS的安装目录的VC\Tools\MSVC\14.31.31103\include中
④:glad.c则不需要换位置,如果该项目用到了GLAD,就把glad.c复制到源文件目录下即可!
3:新建cpp文件,输入如下的测试代码
#include <math.h>
#define GLUT_DISABLE_ATEXIT_HACK
#include <gl/glut.h>
void myDisplay()
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 0.0, 0.0);
//画分割线,分成四个视见区
glViewport(0, 0, 400, 400);
glBegin(GL_LINES);
glVertex2f(-1.0, 0);
glVertex2f(1.0, 0);
glVertex2f(0.0, -1.0);
glVertex2f(0.0, 1.0);
glEnd();
//定义在左下角的区域
glColor3f(0.0, 1.0, 0.0);
glViewport(0, 0, 200, 200);
glBegin(GL_POLYGON);
glVertex2f(-0.5, -0.5);
glVertex2f(-0.5, 0.5);
glVertex2f(0.5, 0.5);
glVertex2f(0.5, -0.5);
glEnd();
//定义在右上角的区域
glColor3f(0.0, 0.0, 1.0);
glViewport(200, 200, 400, 400);
glBegin(GL_POLYGON);
glVertex2f(-0.5, -0.5);
glVertex2f(-0.5, 0.5);
glVertex2f(0.5, 0.5);
glVertex2f(0.5, -0.5);
glEnd();
//定义在左上角的区域
glColor3f(1.0, 0.0, 0.0);
glViewport(0, 200, 200, 400);
glBegin(GL_POLYGON);
glVertex2f(-0.5, -0.5);
glVertex2f(-0.5, 0.5);
glVertex2f(0.5, 0.5);
glVertex2f(0.5, -0.5);
glEnd();
//定义在右下角
glColor3f(1.0, 1.0, 1.0);
glViewport(200, 0, 400, 200);
glBegin(GL_POLYGON);
glVertex2f(-0.5, -0.5);
glVertex2f(-0.5, 0.5);
glVertex2f(0.5, 0.5);
glVertex2f(0.5, -0.5);
glEnd();
glFlush();
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(400, 400);
glutCreateWindow("OpenGL程序");
glutDisplayFunc(&myDisplay);
glutMainLoop();
return 0;
}
运行,即可得到
至此,我们的OpenGL的PT1到此结束!
后续会更新PT2,更加详细的介绍函数库