Skip to content

Commit 761174c

Browse files
committed
add opengl notes
1 parent afba6f2 commit 761174c

17 files changed

+5717
-56
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@
8484

8585
#### 自定义TextureView
8686

87+
#### OpenGL实现
88+
8789

8890

8991

File renamed without changes.
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
## 1.OpenGL简介
2+
3+
[OpenGL官网]https://www.opengl.org/)
4+
5+
OpenGL(Open Graphics Library开发图形库)是用于渲染2D、3D矢量图形的跨语言、跨平台的应用程序编程接口。
6+
7+
OpenGL的前身是硅谷图形功能(SGI)公司为其图形工作站开发的IRIS GL,后来因为IRIS GL的移植性不好,所以在其基础上,开发出了OpenGl。OpenGl一般用于在图形工作站,PC端使用,由于性能各方面原因,在移动端使用OpenGl基本带不动。为此,Khronos公司就为OpenGl提供了一个子集,OpenGl ES(OpenGl for Embedded System)。
8+
9+
10+
11+
### OpenGL ES
12+
13+
[OpenGL ES 官网]https://www.khronos.org/opengles/)
14+
15+
OpenGL ES是免费的跨平台的功能完善的2D/3D图形库接口的API,是OpenGL的一个子集,主要针对手机、Pad和游戏主机等嵌入式设备而设计。
16+
17+
移动端使用到的基本上都是OpenGl ES,当然Android开发下还专门为OpenGl提供了android.opengl包,并且提供了GlSurfaceView,GLU,GlUtils等工具类。
18+
19+
20+
21+
### OpenGL的作用
22+
23+
24+
25+
手机上做图像处理用很多方法,但是目前为止最高效的方法就是有效的使用图形处理单元(GPU),图像的处理和渲染就是在将要渲染到窗口上的像素上做很多的浮点匀速,而GPU可以并行的做浮点运算,所以用GPU来分担CPU的部分,可以提高效率。
26+
27+
- 图片处理:比如图片色调转换、美颜等。
28+
- 摄像头预览效果处理。比如美颜相机、恶搞相机等。
29+
- 视频处理。视频播放的时候增加一些滤镜效果。
30+
- 3D游戏。比如神庙逃亡、都市赛车等。
31+
32+
33+
34+
### Android中的OpenGL ES
35+
36+
Android中OpenGL ES的版本支持如下:
37+
38+
- OpenGL ES 1.0 和 1.1 - 此 API 规范受 Android 1.0 及更高版本的支持。
39+
- OpenGL ES 2.0 - 此 API 规范受 Android 2.2(API 级别 8)及更高版本的支持。
40+
- OpenGL ES 3.0 - 此 API 规范受 Android 4.3(API 级别 18)及更高版本的支持。
41+
- OpenGL ES 3.1 - 此 API 规范受 Android 5.0(API 级别 21)及更高版本的支持。
42+
43+
44+
45+
### 渲染管线
46+
47+
OpenGL 1.x系列采用的是固定功能管线。
48+
49+
OpenGL ES 2.0开始采用了可编程图形管线。
50+
51+
OpenGL ES 3.0兼容了2.0,并丰富了更多功能。
52+
53+
OpenGL渲染管线的流程为: 顶点数据 -> 顶点着色器 -> 图元装配 -> 几何着色器 -> 光栅化 -> 片段着色器 -> 逐片段处理 -> 帧缓冲
54+
55+
如下图,阴影的表示OpenGL ES 3.0管线中可编程阶段。
56+
57+
![](https://raw.githubusercontent.com/CharonChui/Pictures/master/oepngl_es_pip.jpg)
58+
59+
![](https://raw.githubusercontent.com/CharonChui/Pictures/master/opengl_es_progress.jpg)
60+
61+
62+
63+
- 顶点着色器(Vertex Shader): 用来渲染图形顶点的OpenGL ES代码。生成每个顶点的最终位置
64+
65+
着色器(shader)是在GPU上运行的小程序。从名称可以看出,可通过处理它们来处理顶点。此程序使用OpenGL ES SL语言来编写。它是一个描述顶点或像素特性的简单程序。OpenGL最本质的概念之一就是着色器,它是图形硬件设备所执行的一类特殊函数。理解着色器最好的办法就是把它看做是专为图形处理单元(即GPU)编译的一种小型程序。任何一种OpenGL程序本质上都可以被分为两部分:CPU端运行的部分,采用C++、Java之类的语言编写;以及GPU端运行的部分,使用GLSL语言编写。
66+
67+
顶点着色器可以操作的属性有: 位置、颜色、纹理坐标,但是不能创建新的顶点。最终产生纹理坐标、颜色、点位置等信息送往后续阶段。
68+
69+
- 图元装配(Primitive Assembly)
70+
71+
顶点组合成图元的过程叫做图元装配,这里的图元就是指点、线、三角。OpenGL ES中最基础且唯一的多边形就是三角形,所有更复杂的图形都是由三角形组成的。复杂的图形都可以拆分成多个三角形。比如OpenGL提供给开发者的绘制方法glDrawArrays,这个方法的第一个参数就是指定绘制方式,可选值有:
72+
73+
- GL_POINTS:以点的形式绘制
74+
- GL_LINES:以线的形式绘制
75+
- GL_TRIANGLE_STRIP:以三角形的形式绘制,所有二维图像的渲染都会使用这种方式。
76+
77+
该过程还有两个重要操作:裁剪和淘汰。
78+
79+
- 裁剪是指对于不在视椎体(屏幕上可见的3D区域)内的图元进行裁剪。
80+
81+
- 淘汰是指根据图元面向前方或后方选择排期它们(如事物内部的点)。
82+
83+
- 光栅化阶段(Rasterization Stage)
84+
85+
这里会把图元映射为最终屏幕上相应的像素,生成供片段着色器(fragment shader)使用的片段。在图元装配后传递过来的图元数据中,这些图元信息只是顶点而已。顶点处都还没有像素点,直线段端点之间是空的、多边形的边和内部也是空的,光栅化的任务就是构造这些。将图片转化为片段(fragment)的过程叫做光栅化。 这个阶段会将图元数据分解成更小的单元并对应于帧缓冲区的各个像素,这些单元成为片元,一个片元可能包含窗口颜色、纹理坐标等属性。
86+
87+
光栅化其实是一种将几何图元变成二维图像的过程。在这里,虚拟3D世界中的物体投影到平面上,并生成一系列的片段。
88+
89+
- 片段着色器或片元着色器(Fragment Shader):使用颜色或纹理(texture)渲染图形表面的OpenGLES代码。
90+
91+
主要目的是计算一个像素的最终颜色。光栅化操作构造了像素点,这个阶段就是处理这些像素点,根据自己的业务,例如高亮、饱和度调节、高斯模糊等来变化这个片元的颜色。为组成点、直线和三角形的每个片元生成最终颜色/纹理,针对每个片元都会执行一次,一个片元是一个小的、单一颜色的长方形区域,类似于计算机屏幕上的一个像素。一旦最终颜色生成,OpenGL就会把它们写到一块成为帧缓冲区的内存块中,然后Android就会把这个帧缓冲区显示到屏幕上。
92+
93+
通常在这里对片段进行处理(纹理采样、颜色汇总等),将每个片段的颜色等属性计算出来并送给后续阶段。
94+
95+
- 逐片段操作
96+
97+
具体的细分步骤又分为:
98+
99+
![](https://raw.githubusercontent.com/CharonChui/Pictures/master/opengl_es_fragment_opera.jpg)
100+
101+
- 像素归属测试
102+
103+
判断当前像素是否归OpenGL所有,即OpenGLES帧缓冲区窗口的部分是否被另一个窗口所遮蔽,被遮挡像素不属于OpenGL上下文。
104+
105+
- 裁剪测试
106+
107+
判断当前像素是否位于裁剪矩形范围内,如果位于裁剪区域外则被抛弃。
108+
109+
- 模板测试
110+
111+
模板测试主要将绘制区域限定在一定范围内,一般用在湖面倒影、镜像等场合。
112+
113+
- 深度测试
114+
115+
深度测试是将输入片元的深度与帧缓冲区中对应片元的深度进行比较,确定片段是否应该被拒绝。
116+
117+
- 混合
118+
119+
将新生成的片段和保存在缓冲区的片段进行混合。
120+
121+
- 抖动
122+
123+
用于最小化,因为使用有限精度在帧缓冲区中保存颜色值而产生的伪像,使用少量颜色模拟更宽的颜色范围。
124+
125+
- 帧缓冲区
126+
127+
OpenGL管线的最终渲染目的地被称为帧缓存(framebuffer也被记做FBO)
128+
129+
- 着色器语言
130+
131+
GLSL(OpenGL Shading Language)是OpenGL着色语言。
132+
133+
在图形卡的GPU上执行,代替了固定的渲染管线的一部分,是渲染管线中不同层次具有可编程性,例如:视图转换、投影转换等。GLSL的着色器代码分为两个部分:
134+
135+
- 顶点着色器(Vertex Shader)
136+
- 片段着色器(Fragment Shader)
137+
138+
- 坐标系
139+
140+
OpenGL ES采用虚拟坐标系
141+
142+
![](https://raw.githubusercontent.com/CharonChui/Pictures/master/opengl_es_xy.png)
143+
144+
OpenGL ES采用的是右手坐标,选取屏幕中心为原点,从原点到屏幕边缘默认长度为1,也就是默认情况下,从原点到x左边的1和到y左边的1的位置在屏幕上显示的并不相同。
145+
146+
- 形状面和缠绕
147+
148+
在OpenGL的世界里,我们只能画点、线、三角形,图元装配中说到所有复杂的图形都是由三角形组成。
149+
150+
![](https://raw.githubusercontent.com/CharonChui/Pictures/master/opengl_ex_xy_1.jpg)
151+
152+
三角形的点按顺序定义,使得它们以逆时针方向绘制。绘制这些坐标的顺序定义了形状的缠绕方向。默认情况下,在OpenGL中,逆时针绘制的面是正面。
153+
154+
- 程式(Program)
155+
156+
一个OpenGL ES对象,包含了你所希望用来绘制图形所要用到的着色器,最后顶点着色器和片元着色器都要放入到程式中,然后才可以使用,简单的说就是将两个着色器变为一个对象。
157+
158+
159+
160+
如果想要绘制图像,需要至少一个顶点着色器来定义一个图形顶点,以及一个片元着色器为该图形上色。这些着色器必须被编译然后再添加到一个OpenGL ES Program中,并利用这个Program来绘制形状。
161+
162+
163+
164+
### OpenGL Context
165+
166+
OpenGL是一个仅仅关注图像渲染的图像接口库,在渲染过程中它需要将顶点信息、纹理信息、编译好的着色器等渲染状态信息存储起来,而存储这些信调用任何OpenGL函数前,必须先创建OpenGL Context,它存储了OpenGL的状态变量以及其他渲染有关的信息。OpenGL是个状态机,有很多状态变量,试个标准的过程式操作过程,改变状态会影响后续所有的操作,这和面向对象的解耦原则不符,毕竟渲染本身就是个复杂的过程。OpenGL采用Client-Server模型来解释OpenGL程序,即Server存储OpenGL Context,Client提出渲染请求,Server给予响应。之后的渲染工作就要依赖这些渲染状态信息来完成,当一个上下文被销毁时,它所对应的OpenGL渲染工作也将结束。
167+
168+
169+
170+
### EGL
171+
172+
在OpenGL的设计中,OpenGL是不负责管理窗口的,窗口的管理交由各个设备自己完成。具体的来说就是iOS平台上使用EAGL提供本地平台对OpenGL的实现,在Android平台上使用EGL提供本地平台对OpenGL的实现。OpenGL其实是通过GPU进行渲染。但是我们的程序是运行在CPU上,要与GPU关联,这就需要通过EGL,它相当于Android上层应用于GPU通讯的中间层。
173+
174+
EGL为双缓冲工作模式,既有一个Back Frame Buffer和一个Front Frame Buffer,正常绘制的目标都是Back Frame Buffer,绘制完成后再调用eglSwapBuffer API,将绘制完毕的FrameBuffer交换到Front Frame Buffer并显示出来。
175+
176+
要在Android平台实现OpenGL渲染,需要完成一系列的EGL操作,主要为下面几步:
177+
178+
1. 获取显示设备(EGL Display)
179+
180+
获取将要用于显示的设备,有些系统具有多个显示器,会存在多个display,在Android上通过调用EGL10的eglGetDisplay(Object native_display)方法获得EGLDisplay对象,通常传入的参数为EGL10.EGL_DEFAULT_DISPLAY。
181+
182+
2. 初始化EGL
183+
184+
调用EGL10的egInitialize(EGLDisplay display, int[] major_minor)方法完成初始化操作。display参数即为上一步获取的对象,major_minor传入的是一个int数据,通常传入的是一个大小为2的数据。
185+
186+
3. 选择Config配置
187+
188+
调用EGL10的eglChooseConfig(EGLDisplay display, int[] attire_list, EGLConfig[] configs, int config_size, int[] num_config)方法,参数3用于存放输出的configs,参数4指定最多输出多少个config,参数5由EGL系统写入,表明满足attributes的config一共有多少个。
189+
190+
4. 创建EGL Context
191+
192+
eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext share_context, int[] attrib_list);参数1即为上面获取的Display,参数2为上一步chooseConfig传入的configs,share_context,是否有context共享,共享的contxt之间亦共享所有数据,通常设置为EGL_NO_CONTEXT代表不共享。attrib_list为int数组 {EGL_CONTEXT_CLIENT_VERSION, 2,EGL10.EGL_NONE };中间的2代表的是OpenGL ES的版本。
193+
194+
5. 创建EGLSurface
195+
196+
eglCreateWindowSurface(EGLDisplay display, EGLConfig config, Object native_window, int[] attrib_list);参数1、2均为上述步骤得到的结果,参数3为上层创建的用于绘制内容的surface对象,参数4常设置为null。
197+
198+
6. 设置OpenGL的渲染环境
199+
200+
eglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context);该方法的参数意义很明确,该方法在异步线程中被调用,该线程也会被成为GL线程,一旦设定后,所有OpenGL渲染相关的操作都必须放在该线程中执行。
201+
202+
通过上述操作,就完成了EGL的初始化设置,便可以进行OpenGL的渲染操作。
203+
204+
205+
206+
#### OpenGL纹理
207+
208+
纹理(Texture)是一个2D图片(也有1D和3D纹理),他可以用来添加物体的细节,你可以想象纹理是一张绘有砖块的纸,无缝折叠贴合到你的3D房子上,这样你的房子看起来就有砖墙的外表了。
209+
210+
211+
212+
Android 通过其框架 API 和原生开发套件 (NDK) 来支持 OpenGL。
213+
214+
Android 框架中有如下两个基本类,用于通过 OpenGL ES API 来创建和操控图形:`GLSurfaceView``GLSurfaceView.Renderer`
215+
216+
217+
218+
219+
220+
参考
221+
222+
---
223+
224+
- [https://blog.csdn.net/gongxiaoou/article/details/89199632](https://blog.csdn.net/gongxiaoou/article/details/89199632)
225+
- [https://blog.csdn.net/junzia/category_6462864.html](https://blog.csdn.net/junzia/category_6462864.html)
226+
227+
228+
229+
230+
231+
232+
233+
234+
235+
236+
237+
238+
239+
240+
241+
242+
243+
244+
245+
246+
247+
248+
249+
250+
251+
252+
253+
254+
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
## 10.OpenGL ES滤镜
2+
3+
滤镜其实就是利用纹理。
4+
5+
滤镜的编写主要是靠基类。
6+
7+
- 先抽取BaseFilter类,封装好每个滤镜的着色器及onDraw等方法
8+
- 不同的滤镜集成该BaseFilter类
9+
- 在Renderder接口的实现类中去创建不同的Filter类,然后在onDrawFrame方法中去调用该filter.onDraw方法
10+
11+
滤镜的不同大部分只需要修改片元着色器就可以了。
12+
13+
14+
15+
### 反色滤镜
16+
17+
RGB三个通道的颜色取反,而alpha通道不变。
18+
19+
### 灰色滤镜
20+
21+
让RGB三个通道的颜色取均值
22+
23+
### 位移滤镜
24+
25+
纹理默认传入的读取范围是(0,0)到(1,1)内的颜色值。如果对读取的位置进行调整修改,那么就可以做出各种各样的效果,例如缩放动画就是让读取的范围改成(-1,-1)到(2,2)。
26+
27+
28+
29+
30+
31+
32+
33+
34+
35+
36+
37+
38+
39+
40+
41+
42+
43+
44+
45+
46+
47+
48+
49+
50+
51+
52+
53+
54+
55+
56+
57+
58+
59+
60+

0 commit comments

Comments
 (0)