OpenGL ES之一——概念扫盲
OpenGL ES之二——Android中的OpenGL ES概述
OpenGL ES之三——绘制纯色背景
OpenGL ES之四——绘制点,线,三角形
OpenGL ES之五——相机和投影,绘制等腰三角形
OpenGL ES之六——绘制矩形和圆形
OpenGL ES之七——着色器语言GLSL
OpenGL ES之八——GLES20类和Matrix类
OpenGL ES之九——相机和投影
OpenGL ES之十——纹理贴图(展示一张图片)
OpenGL ES之十一——绘制3D图形
OpenGL ES之十二——地球仪和VR图
介绍OpenGL和OpenGL ES。
OpenGL概念:Open Graphics Library(开放图形库)。
OpenGL是一个规范,解释如下
:OpenGL到底是什么?一般它被认为是一个API(Application Programming Interface, 应用程序编程接口),包含了一系列可以操作图形、图像的函数。然而,OpenGL本身并不是一个API,它仅仅是一个由Khronos组织制定并维护的规范(Specification)。
OpenGL规范严格规定了每个函数该如何执行,以及它们的输出值。解释如下
:OpenGL规范严格规定了每个函数该如何执行,以及它们的输出值。至于内部具体每个函数是如何实现(Implement)的,将由OpenGL库的开发者自行决定(译注:这里开发者是指编写OpenGL库的人)。因为OpenGL规范并没有规定实现的细节,具体的OpenGL库允许使用不同的实现,只要其功能和结果与规范相匹配(亦即,作为用户不会感受到功能上的差异)。
实际的OpenGL库的开发者通常是显卡的生产商,介绍如下
:实际的OpenGL库的开发者通常是显卡的生产商。你购买的显卡所支持的OpenGL版本都为这个系列的显卡专门开发的。当你使用Apple系统的时候,OpenGL库是由Apple自身维护的。在Linux下,有显卡生产商提供的OpenGL库,也有一些爱好者改编的版本。这也意味着任何时候OpenGL库表现的行为与规范规定的不一致时,基本都是库的开发者留下的bug。
由于OpenGL的大多数实现都是由显卡厂商编写的,当产生一个bug时通常可以通过升级显卡驱动来解决。这些驱动会包括你的显卡能支持的最新版本的OpenGL,这也是为什么总是建议你偶尔更新一下显卡驱动。
OpenGL库是用C语言写的,同时也支持多种语言的派生,但其内核仍是一个C库。无论是苹果还是安卓系统下,无论使用什么语言都是相通的
。OpenGL ES就是简化版本的OpenGL,更适合嵌入式设备
。OpenGL ES (OpenGL for Embedded Systems) 是 OpenGL 的子集,针对手机
、PDA和游戏主机等嵌入式设备
而设计。
OpenGL ES相对于OpenGL来说,减少了许多不是必须的方法和数据类型,去掉了不必须的功能,对代价大的功能做了限制,比OpenGL更为轻量。在OpenGL ES的世界里,没有四边形、多边形,无论多复杂的图形都是由点、线和三角形组成的(后面实际操作时候就知道了)。
OpenGL 1.x 系列采用的还是固定功能管线
。
从 OpenGL ES 2.0 开始采用了可编程图形管线
。
而 OpenGL ES 3.0 兼容了 2.0,并丰富了更多功能。
其中带阴影的方框表示 OpenGL ES 3.0 管线中可编程阶段
。顶点着色器
着色器(Shader)是在GPU上运行的小程序
。从名称可以看出,可通过处理它们来处理顶点。此程序使用OpenGL ES SL语言来编写。它是一个描述顶点或像素特性的简单程序。
顶点着色器可以操作的属性有:位置、颜色、纹理坐标,但是不能创建新的顶点
。最终产生纹理坐标、颜色、点位置等信息送往后续阶段
概念
:顶点组合成图元的过程叫做图元装配,这里的图元就是指点,线,三角。OpenGL ES中最基础且唯一的多边形就是三角形,所有更复杂的图形都是由三角形组成的。复杂的图形都可以拆分成多个三角形。
该过程还有两个重要操作:裁剪和淘汰。
裁剪是指对于不在视锥体(屏幕上可见的3D区域)内的图元进行裁剪;
淘汰是指根据图元面向前方或后方选择抛弃它们(如物体内部的点)。
概念
:将图片转化为片段(fragment)的过程叫做 光栅化。
实质:光栅化其实是一种将几何图元变为二维图像的过程;在这里,虚拟3D世界中的物体投影到平面上,并生成一系列的片段。
片段着色器
有时也翻译为片元着色器
,通常我们在这里对片段进行处理(纹理采样、颜色汇总等),将每个片段的颜色等属性计算出来并送给后续阶段。
判断当前像素是否归 OpenGL 所有,即 OpenGL ES 帧缓冲区窗口的部分被另一个窗口所遮蔽,则被遮挡像素不属于 OpenGL 上下文。
判断当前像素是否位于剪裁矩形范围内,如果位于裁剪区域外则被抛弃。
模板测试主要将绘制区域限定在一定范围内,一般用在湖面倒影、镜像等场合。
深度测试是将输入片元的深度与帧缓冲区中对应片元的深度进行比较,确定片段是否应该被拒绝。
将新生成的片段和保存在缓冲区的片段进行混合。
用于最小化因为使用有限精度在帧缓冲区中保存颜色值而产生的伪像,使用少量颜色模拟更宽的颜色范围。
帧缓冲区(FBO)
OpenGL 管线的最终渲染目的地被称作帧缓存(framebuffer也记做FBO)。(在后续的文中会着重介绍一下,这里记下)
GLSL(OpenGL Shading Language)是OpenGL着色语言
。
在图形卡的GPU (Graphic Processor Unit图形处理单元)上执行的,代替了固定的渲染管线的一部分,使渲染管线中不同层次具有可编程性。比如:视图转换、投影转换等。GLSL(GL Shading Language)的着色器代码分成2个部分:Vertex Shader(顶点着色器)和Fragment(片断着色器)
,有时还会有Geometry Shader(几何着色器)。负责运行顶点着色的是顶点着色器。它可以得到当前OpenGL 中的状态,GLSL内置变量进行传递。GLSL其使用C语言作为基础高阶着色语言,避免了使用汇编语言或硬件规格语言的复杂性。后面会有专门一篇来专门来介绍,这里只是简介。
OpenGL ES采用的虚拟坐标系,如下图。你可能会说你应该画一个正方形啊,其实不然,因为这是一个虚拟的坐标系。如下图中的空白区域我们可以想象成一个屏幕,那么是不是就很形象了。(后面文章会针对这里做详细描述)
解释一下:OpenGL ES采用的是右手坐标
,选取屏幕中心为原点,从原点到屏幕边缘默认长度为1,也就是说默认情况下,从原点到(1,0,0)的距离和到(0,1,0)的距离在屏幕上展示的并不相同。即向右为X正轴方向,向左为X负轴方向,向上为Y轴正轴方向,向下为Y轴负轴方向,屏幕面垂直向上为Z轴正轴方向,垂直向下为Z轴负轴方向。
由上面3.2 图元装配中知道所有复杂的图形都是由三角形组成
。那么按照什么顺序来绘制这个三角形呢?
在OpenGL中,形状的面是由三维空间中的三个或更多个点定义的表面。一组三个或更多个三维点(在OpenGL中称为顶点)具有正面和背面。你怎么知道哪个面朝前,哪个面朝后?好问题。答案与缠绕或者定义形状点的方向有关
。
在该示例中,三角形的点以按顺序定义,使得它们以逆时针方向绘制。绘制这些坐标的顺序定义了形状的缠绕方向。默认情况下,在OpenGL中,逆时针绘制的面是正面。定义图1中所示的三角形,以便您查看形状的正面(由OpenGL解释),另一面是背面。
为什么重要的是要知道形状的哪个面是正面?答案与OpenGL的常用功能有关,称为面部剔除。面部剔除是OpenGL环境的一个选项,它允许渲染管道忽略(不计算或绘制)形状的背面,从而节省时间,内存和处理周期:
// enable face culling feature
gl.glEnable(GL10.GL_CULL_FACE);
// specify which faces to not draw
gl.glCullFace(GL10.GL_BACK);
如果您尝试使用面部剔除功能而不知道形状的哪一侧是正面和背面,那么您的OpenGL图形看起来会有点薄,或者可能根本不显示。因此,始终以逆时针绘制顺序定义OpenGL形状的坐标。
注意:绘制三角形的时候按照逆时针方向绘制,这已经形成一种共识。
之于简单:OpenGL ES其实相对来说上手还是比较容易的,因为它的使用流程是很清晰的,我们大部分工作都是在处理”顶点着色器“和”片段着色器“。比如我们可以实现将解码好的视频图像渲染到屏幕上。
之于复杂:但是要想要深入的研究或者实现比较复杂的效果就没有那么的简单了,我们不光要熟练使用里面的api,更重要的是知道图像原理。例如添加美颜,滤镜等都是各种算法。
OpenGL官方网站:https://www.opengl.org/
OpenGL各版本的规范和扩展https://www.khronos.org/registry/OpenGL/index_gl.php
安卓OpenGL ES的官方指南https://developer.android.google.cn/guide/topics/graphics/opengl.html
苹果OpenGL ES的官方指南:https://developer.apple.com/documentation/opengles/
推荐一个学习OpenGL的专业的网站,中文版的:https://learnopengl-cn.github.io/;当然相应的也有英文版:https://learnopengl.com/
(小插曲:我发现谷歌的“翻译此页”功能太TM好用了,但是翻译成中文的时候它也可能会将代码块,以及变量翻译成中文,那样反而会给阅读带来困难。所以我的建议是尽量英文,实在看不懂再中文)