什么是OpenGL
OpenGL是一种API,是一种可以对图形设备访问的软件库。
OpenGL被设计为一个现代化的、与硬件无关的接口。(可以在不考虑操作系统或窗口系统/在不同图形硬件系统上/完全通过软件的方式 实现OpenGL接口)。
一个用于渲染的OpenGL程序主要操作:
从OpenGL的几何图元(包括点/线/三角形以及面片)中设置数据,用于构建形状
使用不同的着色器对输入图元数据执行计算操作,判断他们的位置,颜色,以及其他渲染属性
光栅化:将输入图元数据转换为与屏幕对应的片元(OpenGL中的片元若最终渲染为图像,那么片元等同于像素)
针对光栅化过程产生的每个片元,执行片元着色器,决定该片元的最终颜色和位置
对片元进行一些额外的操作,如判断片元对应的对象是否可见/将片元的颜色与当前屏幕位置的颜色进行融合
OpenGL是以客户端 — 服务端的形式实现的
编写的应用程序(客户端)— 计算机图形硬件厂商所提供的OpenGL实现(服务端)。
OpenGL的某些实现(如X窗口系统的实现)允许服务端和客户端在一个网络内的不同计算机上运行。这种情况下,客户端负责提交OpenGL命令,然后将其转换为窗口系统相关的协议,通过共享网络传输到服务端,服务端最终执行并产生图像内容。
在大部分现代实现方案中,硬件图形加速器被用来完成大部分OpenGL命令,它往往被构建在计算机的核心处理器中(依旧是独立部件)/作为一个独立的电路板插入到计算机主板上。无论是哪种形式,我们都应当将图形加速器作为服务端,把用户程序作为客户端对待。
初识OpenGL
OpenGL程序的基本结构
1、初始化物体渲染所对应的状态
2、设置需要渲染的物体
渲染:表示计算机从模型创建到生成最终图像的过程
OpenGL是一种基于光栅化的渲染系统。诸如光线追踪(ray tracing)、光子映射(photon mapping)、路径追踪(path tracing)、基于图像的渲染(image-based rendering)这样的技术都能简单地在可编程图形硬件端实现了。
像素是显示器上最小的可见单元。计算机将所有的像素保存到帧缓存(由图形硬件设备管理的一块独立内存区域,可以直接映射到最终的显示设备上)中
OpenGL语法
OpenGL库中所有函数都以“gl”作为前缀,所有常量以“GL_”作为前缀。
OpenGL是一个C语言形式的库。
后缀 | 数据类型 | C语言类型 | OpenGL类型 |
---|---|---|---|
b | 8位整型 | signed char | GLbyte |
s | 16位整型 | signed short | GLshort |
i | 32位整型 | int | GLint GLsizei |
f | 32位浮点数 | float | GLfloat GLclampf |
d | 64位浮点数 | double | GLdouble GLclampd |
ub | 8位无符号整型 | unsigned char | GLubyte |
us | 16位无符号整型 | unsigned short | GLushort |
ui | 32位无符号整型 | unsigned int | GLuint GLenum GLbitfield |
使用C的数据类型直接表示OpenGL数据类型时,因为OpenGL自身实现不同,可能会造成类型不匹配。
OpenGL渲染管线
渲染管线:一系列数据处理过程,将应用程序的数据转换成最终渲染出的图像
OpenGL首先接收用户提供的几何数据(顶点和几何图元),并且将其输入到一系列着色器阶段(顶点着色、细分着色、几何着色中进行处理,然后送入光栅化单元(负责对所有剪切区域内的图元生成片元数据),对生成的每个片元都执行一次片元着色器。
准备向OpenGL传输数据
OpenGL需要将所有的数据都保存到缓存对象(由OpenGL维护的一块内存区域)中,
将数据传输至OpenGL
当缓存初始化完毕之后,我们可以通过调用OpenGL的一个绘制命令来请求渲染几何图元。
OpenGL的绘制通常是将顶点数据传输到OpenGL服务端。我们可以将一个顶点视为一个需要统一处理的数据包(自己定义构成顶点的所有数据,一般会包括位置数据)。
顶点着色
对于绘制命令传输的每个顶点,OpenGL都会调用一个顶点着色器来处理顶点相关的数据。
一个复杂的应用程序可能包含许多顶点着色器,但同一时刻只能有一个顶点着色器起作用。
细分着色
分为细分控制着色器(管理面片数据)和细分计算着色器(生成最终形状)两个着色器阶段。
顶点着色器处理每个顶点的关联数据之后,如果同时激活了细分着色器后,细分着色器会使用面片(patch)来描述一个物体的形状,并且使用相对简单的面片几何体连接来完成细分的工作,会导致几何图元的数量增加,且模型的外观会更加平顺。
几何着色
几何着色允许在光栅化之前对每个几何图元做进一步处理,如创建新的图元。
图元装配
前面的着色阶段处理的都是顶点数据,这些顶点构成几何图元的所有信息也会被传递到OpenGL当中。
图元装配阶段会将这些顶点与相关的几何图元之间组织起来,准备下一步剪切和光栅化工作。
剪切
顶点可能会落在视口(可以进行绘制的区域),此时与顶点相关的图元会做出改动,以保证相关的像素不会在视口外绘制,是由OpenGL自动完成的。
光栅化
将更新后的图元传递到光栅化单元,生成对应的片元。
光栅化的工作是判断某一部分的几何体(点、线、三角形)所覆盖的屏幕空间。得到了屏幕空间信息以及输入的顶点数据之后,光栅化单元就可以直接对着片元着色器中的每个可变变量进行线性插值,然后将这些值传递给片元着色器。
我们可以将一个片元视为一个“候选的像素”(可以放置在帧缓存中的像素),也有可能最后被剔除,不再更新对应的像素。
OpenGL实现光栅化和数据插值的方法与具体平台相关,无法保证在不同平台上的插值结果总是相同的。
光栅化意味着一个片元的生命伊始,而片元着色器的计算过程本质上是计算这个片元的最终颜色,但绝不等价于OpenGL对这个片元的所有操作。
片元着色
最后一个可以通过编程控制屏幕上显示颜色得阶段。
我们使用着色器来计算最终颜色(逐片元操作可能还会改变一次颜色)和它得深度值。
会使用纹理映射的方式对顶点着色计算出的颜色值进行处理。
片元丢弃(discard):不应该继续绘制某个片元,可以在片元着色器中终止对这个片元的处理。
顶点着色(包括细分和几何着色)决定了一个图元位于屏幕的什么位置
片元着色决定某个片元的颜色应该是什么
逐片元操作
最后处理独立片元的过程。
会使用深度测试(depth test/z缓存)和模板测试(stencil test)来决定一个片元是否可见。
如果一个片元成功地通过了所有激活的测试,就可以直接绘制到帧缓存中了,它所对应像素的颜色值(可能包含深度值)也会更新。如果开启了融混(blending)模式,那么片元的颜色会和当前像素的颜色值叠加,形成一个新的值写入到帧缓存中。
一般来说,像素数据来自于图像文件(也可能是OpenGL直接渲染),保存在纹理贴图当中,通过纹理映射的方式调用。