版本记录
版本号 | 时间 |
---|---|
V1.0 | 2018.01.11 |
前言
OpenGL ES这种图形库的使用,由于底层C函数比较多,理论也更偏向于底层和硬件GPU,所以使用起来代码可能你都见过,顺序也知道改怎么做,但是还是多少会有点感觉不知道为什么这么做?具体有什么含义?接下来这几篇我们就一起来看一下OpenGL ES基础理论,一起学习一下相关的基础理论。感兴趣的可以看上面几篇文章。
1. OpenGL ES基础理论 (一) —— 缓存、帧缓存、上下文与坐标系等
OpenGL ES 在Cocoa Touch中的位置
下面我们就看一下OpenGL ES 在Cocoa Touch中的位置,如下图所示。
这里面简单的说下GLKView
,它是 Cocoa Touch UIView
类的内建子类。GLKView 简化了通过用 Core Animation 层来自动创建并管理帧缓存和渲染缓存共享内存所需要做的工作。GLKView 相关的 GLKViewController
实例是视图的委托并接收当视图需要重绘时的消息。
几个重要函数详细说明
1. GL_API void GL_APIENTRY glGenBuffers (GLsizei n, GLuint* buffers);
glGenBuffers()
函数的第一个参数用于指定要生成的缓存标识符的数量,第二个参数是一个指针,指向生成的标识符的内存保存位置。
2. GL_API void GL_APIENTRY glBindBuffer (GLenum target, GLuint buffer);
glBindBuffer()
的第一个参数是一个常量,用于指定要绑定哪一种类型的缓存。 OpenGL ES 对于glBindBuffer()
的实现只支持两种类型的缓存,GL_ARRAY_ BUFFER
和 GL_ELEMENT_ARRAY_BUFFER
。GL_ARRAY_BUFFER 类型用于指定一个顶点属性数组,glBindBuffer() 的第二个参数是要绑定的缓存的标识符。
3. GL_API void GL_APIENTRY glBufferData (GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage);
glBufferData()
的第一个参数用于指定要更新当前上下文中所绑定的是哪一个缓 存。第二个参数指定要复制进这个缓存的字节的数量。第三个参数是要复制的字节的地 址。最后,第 4 个参数提示了缓存在未来的运算中可能将会被怎样使用。GL_STATIC_ DRAW
提示会告诉上下文,缓存中的内容适合复制到 GPU 控制的内存,因为很少对其 进行修改。这个信息可以帮助 OpenGL ES 优化内存使用。使用 GL_DYNAMIC_DRAW
作为提示会告诉上下文,缓存内的数据会频繁改变,同时提示 OpenGL ES 以不同的方 式来处理缓存的存储。
4. GL_API void GL_APIENTRY glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr) __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);
glVertextAttribPointer()
函数会告诉 OpenGL ES 顶点数据在哪里,以 及怎么解释为每个顶点保存的数据。在这个例子中,glVertexAttribPointer() 的第一个参数指示当前绑定的缓存包含每个顶点的位置信息。第二个参数指示每个位置有几个部 分。第三个参数告诉 OpenGL ES 每个部分都保存为一个浮点类型的值。第四个参数告诉 OpenGL ES 小数点固定数据是否可以被改变。第五个参数叫做“步幅”,它指定了每个顶点的保存需要多少个字节。换句话说, 步幅指定了 GPU 从一个顶点的内存开始位置转到下一个顶点的内存开始位置需要跳过 多少字节。sizeof(GLKVector3)
指示在缓存中没有额外的字节,即顶点位置数据是密封的。在一个顶点缓存中保存除了每个顶点位置的 X、Y、Z 坐标之外的其他数据也是可能的。最后一个参数是 NULL,这告诉 OpenGL ES 可以从当前 绑定的顶点缓存的开始位置访问顶点数据。
5. GL_API void GL_APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count);
通过调用 glDrawArrays()
来执行绘图。glDrawArrays() 的第一个参数 会告诉 GPU 怎么处理在绑定的顶点缓存内的顶点数据。glDrawArrays() 的第二个参数和第三个参数分别指定缓存内的需要渲染的第一个顶点的位置和需要渲染的顶点的数量。
6. GL_API void GL_APIENTRY glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint* params) __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);
获取和返回当前上下 文的帧缓存的像素颜色渲染缓存的尺寸。
纹理
纹理是一个用来保存图像的颜色元素值的 OpenGL ES 缓存。
在纹理的缓存中保存的颜色值可能要耗费很多的内 存。所有的嵌入式系统都为纹理设定了内存的最大尺寸 限制。所有的 iPhone 和 iPad Touch 版本都可以支持由 1024×1024
像素的图像生成的纹理。iPad 和最新一代 iPhone、iPod Touch 所支持的尺寸甚至更大。
当用一个图像初始化一个纹理缓存之后,在这个图像中的每个像素变成了纹理中 的一个纹素(texel)
。与像素类似,纹素保存颜色数据。像素和纹素之间的差别很微妙 : 像素通常表示计算机屏幕上的一个实际的颜色点,因此,像素通常被用来作为一个测量单位。与此相反,纹素存在于一个虚拟的没有尺寸的数学坐标系中。如下所示, 显示了 OpenGL ES 纹理坐标系中的一个纹理。
纹理坐标系有一个命名为 S 和 T 的 2D 轴。在一个纹理中无论有多少个纹素,纹理的尺寸永 远 是 在 S 轴 上 从 0.0 到 1.0, 在 T 轴 上 从 0.0 到 1.0。从一个 1 像素高 64 像素宽的图像初始化来的纹 理会沿着整个 T 轴有 1 纹素,沿着 S 轴有 64 纹素。
在每个顶点的 X、Y、Z 坐标被转换成视口坐标后,GPU 会设置转换生成的三角形 内的每个像素的颜色。转换几何形状数据为帧缓存中的颜色像素的渲染步骤叫做点阵化(rasterizing)
,每个颜色像素叫做片元(fragment)
。当 OpenGL ES 没有使用纹理时, GPU 会根据包含该片元的对象的顶点的颜色来计算每个片元的颜色。当设置了使用纹理后,GPU 会根据在当前绑定的纹理缓存中的纹素来计算每个片元的颜色。
1. 纹理的取样模式
每个顶点的 U 和 V 坐标会附加到每个顶点在视口坐标中的最终位置。然后 GPU 会 根据计算出来的每个片元的 U、V 位置从绑定的纹理中选择纹素。这个选择过程叫做取样(Sampling)
。取样会把纹理的 S 和 T 坐标系与每个渲染的三角形的顶点的 U、V 坐标匹配起来。具体如上图所示。在 S 和 T 坐标系中与 {0,0} 位置最近的纹素会被映射到 拥有 {0,0} 的 U、V 坐标的顶点所对应的片元上。每个随后的片元位置对应于一个沿着 S 和 T 轴的与该片元在 U、V 坐标中的位置等比例的位置。例如,一个 U、V 坐标为{0.5,0,5} 的片元会被当前绑定的纹理中最接近中间位置的纹素所着色。
渲染过程中的取样可能会导致纹理被拉伸、压缩,甚至翻转。
2. MIP贴图
MIP 贴图是与取样密切相关的。根据维基百科,MIP 代表拉丁短语 Multum In Parvo
,意思是“放置很多东西的小空间”。回忆一下,内存存取是现代图形处理的薄弱 环节。当有多个纹素对应一个片元时,线性取样会导致 GPU 仅仅为了计算一个片元的 最终颜色而读取多个纹素的颜色值。MIP 贴图是一个为纹理存储多个细节级别的技术。 高细节的纹理会沿着 S 轴和 T 轴存储很多的纹素。低细节的纹理沿着每个轴存储很少的 纹素。最低细节的纹理只保存一个纹素。多个细节级别增加了在 S、T 轴上的纹素和每 个片元的 U、V 坐标之间有紧密的对应关系的可能性。当存在一个紧密的对应关系时, GPU 会减少取样纹素的数量,进而会减少内存访问的次数。
后记
未完,待续~~~
网友评论