基础概念OpenGLES(一)
opengl es是什么?
-
opengl 是一个跨平台(不同的GPU类型,如电脑端NVIDIA,Intel,AMD的显卡,手机端PowerVR(苹果系列), Adreno(高通,安卓系列)等等)、跨编程语言(各个编程语言java,C,OC,js等等都有opengl的接口)的图形图像处理接口,opengl本身只是一组api接口规范,具体的实现一般是由具体的显卡(GPU)设备生产商编码实现的,比如苹果手机是由apple实现的,Linux系统则是由各个显卡生产商通过驱动程序导入的。
-
opengl es是opengl的一个子集,是专门用来针对手机,平板电脑等嵌入式设备的图形图像处理接口,接口设计中不涉及上下文环境和窗口的管理,这由各个平台自行实现。比如ios就是EAGL;安卓就是EGL;
tips:
libSDL是一个跨平台的图形处理库,其中有包含对opengl es的标准接口的封装及各个平台上下文,窗口管理的封装。
备注:opengl es 2.0 官方接口文档 https://www.khronos.org/registry/OpenGL-Refpages/es2.0/
基础概念
- 应用端:
即我们自己的程序端,相对于opengl es,我们属于应用端 - 图元:
要渲染的几何物体,或者形状。比如要渲染一个正方形的图片,就是一个图元,要渲染两条直线,这两条直线也是图元 - 纹理:
通俗点,可以理解为一张图片,在opengl es中纹理就是图片的另外一种叫法 - 纹素:
纹理的基础单元,也就是像素 - 顶点数组
顶点指的是组成图元的各个顶点的坐标数据(在3D笛卡尔坐标中即x,y,z坐标),这些坐标数据可以一起存到一个内存数组中,这个数组就叫做顶点数组 - 顶点缓冲区
在显存中专门分配一块显存来存储这个顶点数组,这个显存就称为顶点缓冲区 - 顶点着色器
顶点着⾊器(简称为VS)是opengl es中⽤于计算图形顶点属性(包括顶点坐标归一化、顶点光照运算等等)的程序。顶点着色器是逐顶点运算的程序,也就是说每个顶点数据都会执行⼀次顶点着⾊器,当然这是并行的 - 片元着色器
片元着色器(简称为PS)用来决定要渲染图形的每个像素的颜色的程序,它也是并行计算的
opengl es渲染管线
渲染管线也就是opengl es的工作流程,这里是opengl es2.0以后的版本的渲染管线
- 1、opengl es接收API的输入
通过 API 设定顶点的信息(一般包括顶点坐标,纹理坐标,颜色,变换矩阵等等),一般由顶点着色器接收这些信息作为输入 - 2、顶点着色器处理顶点坐标
前一阶段的输入将在 VS 中进行运算,以得到最终的顶点坐标。 - 3、图元装配
将顶点着色器计算出的最终顶点坐标进行图元装配,构建出最终想要渲染的图形。所有要渲染的图形都可以由三个基本的图元(点、线、三角形)组成,比如正方形或者长方形,就可以由两个三角形组成,圆形可以由无数个三角形组成,只是三角形的数量越多,圆形看上去越圆润 - 4、光栅化
通过计算,将要渲染的图形上所有的像素点找到,并根据插值或者其他方式计算出其颜色等信息的过程 - 5、片元着色器计算像素颜色
光栅化得到了要渲染图形的所有像素的信息,这些信息作为片元着色器的输入,那么在片元着色器中将计算像素颜色 - 6、像素处理
片元着色器计算的像素颜色还不是最终要渲染的颜色,这一步骤还包括Alpha Test、Depth/Stencil Test、Blend、Dither等几个步骤,经过这几个步骤后得到的就是最终的渲染颜色
备注:以上所有的操作都是并行处理
opengl es的相关坐标系
-
opengl es的坐标系原点位于要渲染区域的中心点,向右和向上分别为x轴和y轴的正向轴,取值范围为(-1,1)
如下为正确渲染一张图片为例传递顶点坐标
static float verData1[8] = {
-1.0f, -1.0f, // 左下角
1.0f,-1.0f, // 右下角
-1.0f,1.0f, // 左上角
1.0f,1.0f, // 右上角
};
一般按照如上的顺序传递坐标,取值也可以是其它值,比如
static float verData1[8] = {
-0.5f, -0.5f, // 左下角
0.5f,-0.5f, // 右下角
-0.5f,0.5f, // 左上角
0.5f,0.5f, // 右上角
};
表示取中间的1/2区域;实际上就是压缩效果
static float verData1[8] = {
-2.0f, -2.0f, // 左下角
2.0f,-2.0f, // 右下角
-2.0f,2.0f, // 左上角
2.0f,2.0f, // 右上角
};
表示将原来的坐标系放大了一倍后在基于原始的进行截取 -
纹理坐标系
在opengl 中,通常将纹理中的像素根据纹理坐标系来进行编址,纹理坐标系的横轴成为S轴,纵轴成为T轴,垂直于ST轴的成为R轴,在2D纹理中,没有
R轴,横轴和纵轴又称为UV轴,所以2D纹理坐标系又称为UV坐标系,UV轴的取值范围都是(0,1)。与OpenGL 坐标系不同的是,纹理坐标系的原点位于左
上角
如下为正确渲染一张图片为例传递纹理坐标:
static float uvData[8] = {
0.0f, 1.0f,// 左下角
1.0f, 1.0f, // 右下角
0.0f, 0.0f,// 左上角
1.0f, 0.0f, // 右上角
};
纹理坐标系一般不要去改变它的值,传这个就好,(当然也可该,前提是知道内部的转换原理),如果要实现压缩和裁剪效果,改变几何坐标系方便一些。 -
设定opengl es渲染区域
glViewPort(x,y,w,h) 函数窗口创建的渲染区域
它以左下角作为原点的坐标系
此函数的意思就是,基于当前视图左下角作为原点的坐标系,选取一个左下角坐标为x,y,长宽为w,h的区域作为渲染的区域
纹理映射
-
1、缩放
纹理的映射就是让物体的每个片元(每个颜色像素)都找到对应的纹理纹素点,在这个映射过程中,因为纹理和物体的不匹配,一般会出现两种情况
第一种是拥有大量纹素的纹理被映射到只还有少量片元的物体中
第二种是拥有少量纹素的纹理被映射到含有大量片元的物体中
如下:
glTexParameter(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR)
glTexParameter(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR)
GL_TEXTURE_MIN_FILTER:
是多个纹素对应一个片元的解决方案。可以设置两个值,GL_NEAREST 和 GL_LINEAR ,这两个值分别对应了两种不同的解决方案。GL_LINEAR 会混合匹配的颜色来得到片元的颜色,产生的颜色可能是纹理中不存在的颜色。例如一个纹理是由交替的黑色和白色的纹素组成,那个线性取样会使最终的片元颜色为灰色。GL_NEAREST 会拾取与片元 U,V 坐标最相近的颜色。
GL_TEXTURE_MAG_FILTER:
是没有足够的纹素来映射片元的解决方案。可以设置的值同 GL_TEXTURE_MIN_FILTER 一样。GL_LINEAR 会告诉OpenGL ES 混合附近纹素的颜色来计算片元的颜色。GL_LINEAR会有一个放大纹理的效果,当没有足够的纹素来映射片元时,会让纹理模糊的出现在要渲染的图形上。
GL_NEAREST 仅仅会拾取片元的 U ,V 位置接近的纹素的颜色,并放大纹理,会使其像素化的出现在要渲染的图形上。
GL_LINEAR:
线性插值,取最近的点的线性平均值 (性能消耗较大)
mipmaps
它也是一种纹理过滤算法,按我的理解它是以空间换时间的一种技巧,具体原理就是事先根据纹理生成长和宽逐渐除以2的小纹理,比如原始纹理大小128x128,
采用此方法后,会生成64x64 32x32 16x16 8x8 4x4 2x2 1x1的一系列纹理,如果需要20x18的纹理,则取最近的32x32 16x16进行平均
该方法很好的解决了如下问题:
1、当纹理很大,但是屏幕区域很小,渲染出现的闪烁问题,因为根据最邻近插值和线性插值都无法很快计算出合理的像素
它可以取的值如下:
GL_NEAREST_MIPMAP_NEAREST 选择最近的mipmap层,然后再用最邻近过滤插值
GL_LINEAR_MIPMAP_NEAREST 选择最近mipmap层,然后再用线性插值
GL_NEAREST_MIPMAP_LINEAR 选择最近的2层mipmap用最邻近过滤插值
GL_LINEAR_MIPMAP_LINEAR 选择最邻近的2层mipmap用线性插值
使用如下函数生成mipmaps
glGenerateMipmap(GLenum target); -
2、warp
另外一种在映射时可能出现的情况是纹理的四个顶点坐标不是1.0时,将采用如下方式:
glTexParameter(GL_TEXTURE_2D,GL_TEXTURE_WARP_S,GL_REPEAT) S轴方向
glTexParameter(GL_TEXTURE_2D,GL_TEXTURE_WARP_T,GL_REPEAT) T轴方向
GL_REPEAT:纹理没有覆盖的部分重复之前的纹理,当纹理大小大于物体大小时,纹理采样会出错。
GL_MIRRODED_REPEAT:将原来的纹理先颠倒再重复,当纹理大小大于物体大小时,纹理采样会出错。
GL_CLAMP_TO_EDGE:延续结束时的纹理
顶点着色器中顶点坐标的顺序规则
open gl 中顶点坐标
static float verData1[8] = {
-1.0f,-1.0f, // 左下角
1.0f,-1.0f, // 右下角
-1.0f,1.0f, // 左上角
1.0f,1.0f, // 右上角
};
对应的纹理坐标
static float uvData[8] = {
0.0f, 1.0f, // 左下角
1.0f, 1.0f, // 右下角
0.0f, 0.0f, // 左上角
1.0f, 0.0f, // 右上角
};
对应的为glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 可以渲染出一张图片
VBO
GPU中专门开辟的一块内存用于存储图元的顶点数据,这样每次调用glDrawArrays()函数时,GLSL就是直接从GPU中获取
顶点数据,而不是每次都有cpu拷贝到GPU(cpu拷贝到gpu肯定效率不如直接从GPU中取),这样对于顶点数据比较多时,
可以加快效率;
FBO
帧缓冲,也就是通过glGenFramebuffers()函数生成的对象,它主要用于存储opengl es最终的渲染结果。
离屏渲染
当需要对纹理进行多次渲染采样时(也就是对一个纹理进行多个着色器程序去处理),而这些渲染采样中间过程是不需要
展示给用户看的,所以就可以额外创建一个FBO对象来专门做这个事情,这个FPO对象就成为为离屏渲染帧缓冲,这个
过程叫做离屏渲染
渲染缓冲区
通过glGenRenderbuffers()生成的缓冲区,他用于将渲染的结果呈现到屏幕上,需要与FBO绑定
opengl es api和EGL api
opengl es api是用来进行图像渲染操作的,
egl api是由各个操作系统实现的系统api,它主要的作用就是创建窗口,创建opengl 上下文环境等。可以由各自厂商自行定义,使用通用的EGL接口。
如苹果就是EAGL,安卓就可以用EGL来自行管理,也可以用GLSurface系统创建opengl 环境