OpenGL ES学习阶段性总结
前言
最近观看下面这本书有感,结合之前的学习,对OpenGL的知识进行回顾。
概念
帧缓存:接收渲染结果的缓冲区,为GPU指定存储渲染结果的区域。
帧缓存可以同时存在多个,但是屏幕显示像素受到保存在前帧缓存(front frame buffer)的特定帧缓存中的像素颜色元素的控制。
程序的渲染结果通常保存在后帧缓存(back frame buffer)在内的其他帧缓存,当渲染后的后帧缓存完成后,前后帧缓存会互换。(这部分操作由操作系统来完成)
前帧缓存决定了屏幕上显示的像素颜色,会在适当的时候与后帧缓存切换。
- (BOOL)presentRenderbuffer:(NSUInteger)target;
Core Animation的合成器会联合OpenGL ES层和UIView层、StatusBar层等,在后帧缓存混合产生最终的颜色,并切换前后帧缓存;
OpenGL ES坐标是以浮点数来存储,即使是其他数据类型的顶点数据也会被转化成浮点型;
framebuffer object 通常也被称之为 FBO,它相当于 buffer(color, depth, stencil)的管理者,三大buffer 可以附加到一个 FBO 上。我们是用 FBO 来在 off-screen buffer上进行渲染。
GPU运算和CPU运算是分开的。(如果需要同步返回,可以使用glFinish
)
glReadPixels
从图形硬件中复制数据,通常通过总线传输到系统内存。此时,应用程序将被阻塞,直到内存传输完成。
如果指定的像素布局与图像硬件的本地排列不同,数据进行重定格式会产生额外的性能开销。
在使用完缓存后,可以调用glBindBuffer
把array绑定的对象重置为0,防止被其他地方误用;(注意,纹理对象需要在使用完后,再glBindTexture绑定为0)
CAEAGLLayer会与OpenGL ES的帧缓存共享它的像素颜色仓库。(这也是为什么我们想让绘制的内容显示到屏幕时,需要重载UIView的+layerClass方法,返回一个CAEAGLLayer实例。)
eaglLayer的属性kEAGLDrawablePropertyRetainedBacking为NO表示,不要试图保留任何以前绘制的图像留作以后重用。
在自定义UIView实现渲染时,需要在调整视图大小的回调中(layoutSubviews),调用-renderbufferStorage:fromDrawable: 方法来调整视图的尺寸,从而匹配层的新尺寸。
这个尺寸大小可以用glGetRenderbufferParameteriv()
方法来获取;
glGetError
返回错误,如果有多个错误,每次返回一个,需要多次调用。
CoreGraphics负责创建显示到屏幕上的数据模型,QuartzCore(CoreAnimation –> OpenGLES)负责把CoreGraphics创建的数据模型真正显示到屏幕上。
理想状态下,缓存生成后就不发生变化;
生成、初始化和删除缓存需要耗费时间来同步GPU和CPU,大多数情况下是CPU等待GPU,因为GPU在删除缓存之前必须等待该缓存相关的指令全部执行完毕;
故而一个程序在每帧都进行生成和删除缓存会有严重的性能消耗。
glDeleteFramebuffers
glDeleteRenderbuffers
glDeleteBuffers
坐标
齐次坐标表示法:用n+1维向量表示n维向量。
齐次坐标归一化,最后一个坐标为1。
If set to GL_TRUE, normalized indicates that values stored in an integer format are to be mapped to the range [-1,1]
整数才能归一化,浮点数无效。
万向节死锁:Wiki解释
如果是用高度角和偏航角来解释,就是当高度角等于90°的时候,偏航角的维度已经丢失,不管你怎么转都不会产生结果。
Gimbal_lock不是说空间存在某个点无法用极坐标的方式来表示,而是点的运动不能用连续的极坐标来表示。
纹理
纹理坐标系:S和T组成的2D轴。(0.0到1.0,还有1D和3D的纹理坐标系,R,S,T轴)
位图(bitmap):一系列表示开启和关闭像素值的0和1。
像素数据 != 位图。
像素图(pixmap):类似位图,每个像素需要一个以上的存储位来表示。
The more general term pixmap refers to a map of pixels, where each one may store more than two colors, thus using more than one bit per pixel. Often bitmap is used for this as well. In some contexts, the term bitmap implies one bit per pixel, while pixmap is used for images with multiple bits per pixel.
图像数据在内存中很少以紧密的形式存在,出于性能的考虑,每一行都该从特定的字节对齐地址开始。
OpenGL 采用4个字节的对齐方式。
存储大小 != 像素宽度 * 高度值。
应该是每行宽度 * 高度值,每行宽度可能会有填充的空字节。
1、纹理过滤
GL_TEXTURE_MIN_FILTER 表示多个纹素对应单个像素的时候
GL_TEXTURE_MAG_FILTER表示单个纹素对应多个像素的时候
GL_LINEAR 表示线性插值
GL_NEAREST 表示最近
2、纹理环绕
GL_TEXTURE_WRAP_S 表示S轴超过坐标系范围
GL_TEXTURE_WRAP_T 表示T轴超过坐标系范围
GL_CLAMP_TO_EDGE 表示取纹理边缘
GL_REPEAT 表示重复纹理
GL_MIRRORED_REPEAT 表示镜像重复纹理
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
OpenGL ES推荐使用尺寸为2的幂的纹理,其他纹理也支持,但是性能上会有额外的消耗。
3、各向异性过滤
非OpenGL标准的扩展支持,GL_EXT_texture_filter_anisotropic。
4、MIP纹理
glGenerateMipmap生成。
MIP纹理会比普通大33%,计算公式如下:
1 + 1/4 + 1/16 + 1/64 ... + 1/(4^n)
根据等比求和公式得到Sn = 1+1/3
glPixelStorei 方法可以改变或者恢复像素的存储方式
GL_PACK_ALIGNMENT
GL_UNPACK_ALIGNMENT
默认4字节对齐,即一行的图像数据字节数必须是4的整数倍,即读取数据时,读取4个字节用来渲染一行,之后读取4字节数据用来渲染第二行。对RGB 3字节像素而言,若一行10个像素,即30个字节,在4字节对齐模式下,OpenGL会读取32个字节的数据,若不加注意,会导致glTextImage中致函数的读取越界崩溃。
One value affects the packing of pixel data into memory: GL_PACK_ALIGNMENT
. The other affects the unpacking of pixel data from memory: GL_UNPACK_ALIGNMENT
https://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml
默认是4字节读取。
当着色器计算出来一个完全不透明的像素颜色时,可以简单的替换帧缓存中对应位置的颜色,也可以通过glEnable(GL_BLEND)来开启混合功能,并通过glBlendFunc设置混合函数。
也可以通过gl_LastFragData,自己计算混合后的颜色;
也可以通过多重纹理来实现。
多通道渲染:多次读写像素颜色缓存来创建一个最终的渲染结果的过程;
(举例:开启混合,只有纹理单元0,先绑定为纹理1,绘制;再绑定纹理2,绘制;再绑定纹理3,绘制;这样得到最后的结果,是3张图片混合后的结果)
glTexImage2D (1D和3D在ES2的头文件没找到,3D可以在ES3找到)加载纹理,纹理对象需要通过glGenTexture和glDelete 来创建和销毁。
glTexSubImage2D 是替换纹理,可以替换部分,也可以替换全部纹理,速度比重新加载更快。
glCopyTexImage2D 可以用颜色缓冲区加载数据。
glCopyTexSubImage2D 同上。
在销毁纹理的时候,如果不确定对象索引是否是纹理(比如作为参数传递),glIsTexture来判断。
纹理高级知识
1、矩形纹理
GL_TEXTURE_RECTANGLE
不能进行MIP贴图,只能加载glTexImage2D的第0层。 纹理坐标不是标准化的,纹理坐标实际上是对像素寻址,而不是从0到1的范围覆盖图像的。
纹理坐标(5,19)实际上是图像中从左起6个像素以及从上面起第20个像素。
2、立方体纹理
由6个正方形的2D图像组成的纹理。
3、多重纹理
同时使用两个或者更多纹理。
4、点块纹理
在一个顶点上应用纹理。
纹理数组、纹理代理略。
基本图形光栅化
1、直线
暴力法:微分方程,带入坐标,取整求解(x,y);
中点画线法:假设斜率在0~1之间,对于P(x, y),下一个点只能在P1或者P2,求P1P2中点M,直线与P1P2交点Q,判断M、Q的上下关系;
bresenham画线法:假设斜率在0~1之间,对于P(x, y),下一个点只能在P1或者P2,直线与P1P2交点为Q,判断P1Q和QP2的大小关系;
2、圆
圆具有八对称性,对于一个愿只需要绘制1/8的圆弧;
圆的bresenham,用D(P)来表示点P到原点的距离平方和圆的半径平方之差,
di = D(Si) + D(Ti)。
超级宝典遇到的问题
1、gltReadTGABits错误
因为没有引入头文件和对应的cpp文件。
2、Invalid storage qualifiers 'in' in global variable context
改成 attribute 和 varying
3、不支持的version
以下是对应的GLSL版本