前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >OpenGL ES for Android 世界

OpenGL ES for Android 世界

作者头像
100001509164
发布2022-01-20 21:50:24
1.2K0
发布2022-01-20 21:50:24
举报
文章被收录于专栏:DevTipsDevTips

01 前言

大家好,本文是 iOS/Android 音视频专题的第五篇,该专题中 AVPlayer 项目代码将在 Github 进行托管,你可在微信公众号(GeekDev)后台回复资料 获取项目地址。

上篇文章 《使用 MediaExtractor 及 MediaCodec 解码音视频》介绍过对音视频进行解码,但是我们并没有将解码后的数据在屏幕上展示,如果需要渲染到屏幕上我们就需要了解下 OpenGL 的相关知识。

目录:

  • OpenGL ES 基础概念
  • OpenGL ES GLSL 着色器
  • OpenGL ES Program
  • OpenGL ES 纹理
  • OpenGL ES 绘制纹理
  • 结束语

02 OpenGL ES 基础概念

OpenGL ES 是 OpenGL 三维图像 API 的子集,是为手机,PAD和游戏机等嵌入式设备而设计。OpenGL ES 目前支持 iOS、Android、BlackBerry、bada、Linux 和 Windows。

由于 OpenGL API 相当复杂,并且在嵌入式设备上很多功能并没有什么卵用,Khronos 组织牵头对 OpenGL API 进行了删减,最终诞生了 OpenGL ES。

OpenGL ES 在移动设备上做了很多优化,例如,降低电源消耗,提高着色器性能,在着色器语言中引入精度限定符(highp、mediump、lowp)。

Context 是 OpenGL 中的一个重要概念,理解 Context 我们首先需要知道状态机,OpenGL 本身是一个巨大且复杂的状态机,当调用一个 GL 函数时,其实,就是在改变 OpenGL 当前的状态信息,比如:颜色、纹理坐标、光照、混合、深度测试等。而这些状态信息都保存在 Context 上下中,因此渲染的时候,必须创建当前环境的 Context 。在 Android 中 Context 使用 EGLContext 对象表示。

03 OpenGL ES 着色器

OpenGL ES 中相当重要的一部分是 GL Shader Language(GLSL),GLSL 是 OpenGL ES 开放给我们的可编程部分,通常,我们编写的代码运行在 CPU 中,但 GLSL 在 GPU 中运行。 GLSL 由顶点(vertex)着色器和片段(fragment)着色器构成, 可以在着色器中自定义我们自己的渲染逻辑,比如,滤镜、素描、马赛克特效等。

GLSL 的语法与 C 语言比较类似,GLSL 包括:

  • 变量
  • 变量类型
  • main 函数
  • 结构体
  • 数组
  • 限定符

变量类型

void :用于函数无返回值或无参数列表声明

标量 float、int 、bool 浮点、整型、布尔型

浮点向量 float、vec2 、vec3、vec4 包含1、2、3、4个元素的浮点型向量

整数向量 int、ivec2 、ivec3、ivec4 包含1、2、3、4个元素的整型向量

布尔向量 bool、bvec2 、bvec3、bvec4 包含1、2、3、4个元素的布尔型向量

矩阵 mat2、mat3 、mat4 为 2x2、3x3、4x4 的浮点型矩阵

纹理句柄 sampler2D、samplerCube 表示 2D、3D纹理句柄

获取向量分量时即可以通过 "." 符号也可以通数组下标的方法,由于向量在 GLSL 中常常用来表示颜色、纹理坐标等, GLSL 提供了通过 {x, y, z, w}{r, g, b, a}{s, t, r, q} 操作来获取向量分量,这种方式在编写 GLSL 代码时很容易可以断定该向量的意义。

GLSL 限定符

限定符是对变量的解释说明,并限定变量在 GLSL 中的使用场景,在 GLSL 中支持如下限定符:

attribute : 只能用在顶点着色器中,一般用于表示顶点数据。由程序通过

glGetAttribLocation 获取 attribute 地址,并通过 glEnableVertexAttriArray / glVertexAttribPointer 为 attribute 属性赋值。

varying :可用于顶点和片段着色器,一般用于在着色器之间做数据传递。通常,

varying 在顶点着色器中进行计算,片段着色器使用 varying 计算后的值。

uniform :可用于顶点和片段着色器, 由程序通过 glGetUniformLocation 获取地址 ,并通过 glUniforml 系列函数复制。

顶点着色器

在一个 OpenGL ES 程序中,顶点着色器和片元着色器是标准配置,顶点着色器用于定义绘制的形状,片元着色器为这个形状上色。

例如,我们如果想要绘制一个三角形,我们首先确定三角形的三个顶点坐标,并将顶点信息告知顶点着色器,顶点着色器根据顶点坐标绘制三角形,然后交由片元着色器为三角形粉刷颜色。通常,顶点着色器为每个顶点调用一次顶点着色器。

下面是一个非常简单的顶点着色器:

代码语言:javascript
复制
"attribute vec3 aPosition;" +

片元着色器

"片元" 可以简单理解为像素,片元着色器也就意味着我们可以操作图像的像素,比如,颜色、坐标、深度等。所以,片元着色器就是我们实现各种特效的地方。

片元着色器总是在顶点着色器之后执行,片元着色器会为每个 "片元" 执行一次片元着色器,这意味着顶点着色器和片元着色器的执行次数并不是相同的。你可能会产生疑问?? 如果不相同顶点着色器的顶点坐标如何传入片元着色器呢???

如果要搞清楚这个问题,我们就需要知道 OpenGL 的渲染管线,如下图:

渲染管线是指图形数据经过一系列处理过程,最终输出到屏幕上,这个过程就像一个输送管道,或者一个处理流水线,它有着固定的处理顺序。

从上图管线,我们可以看到在顶点着色器和片元着色器之间有图元装配、几何着色器、光栅化阶段。

图元装配 (Primitive Assembly):将顶点着色器输出的所有顶点作为输入,根据指定类型(GL_POINTS、GL_LINES、GL_TRIANGLES)装配图元形状。

光栅化 (Resterization Stage): 光栅化阶段会将图元形状映射为最终屏幕上显示的像素,然后生成供片元着色器使用的 "片元",然后将每个片元输入片元着色器。

下面是一个简单的片元着色器代码:

代码语言:javascript
复制
"precision mediump float;" +

下图是通过顶点着色器和片元着色器绘制的三角形,具体代码可以参考 AVPlayer 项目。

详见 DemoGLTriangleActivity

04 OpenGL ES Program

Program 是 OpenGL 另外一个重要的概念,一个完整的 GL 程序顶点着色器、片元着色器、Program 对象是必不可少的部分,缺一不可。

Program 通过链接顶点着色器和片元着色器,并将 Program 激活后,后续我们执行的绘制命令,会在 Program 链接的顶点着色器和片元着色器中执行。

创建一个 完整的 GL 程序的过程大致如下:

代码语言:javascript
复制
// step1:创建一个 Program 程序

详见 AVPlayer 工程

05 OpenGL ES 纹理

纹理、贴图、材质的概念都比较相似,大致关系是:材质(Material)> 贴图(Map)> 纹理(Texture)( > 表示为包含关系), 纹理是最小输入单位,贴图更多是用来做纹理映射,贴图包含纹理及纹理的 UV 坐标,材质不仅包含纹理和贴图,更主要的功能是提供了光照、透明度、折射、质感等属性信息。

你可以把纹理想象成墙面上的壁纸,它可以为物体添加细节,有更强的视觉感受。如下图所示:

一张纹理图片

在 GLSL 中纹理类型使用 sampler2D (2D世界)表示,在片元着色器中我们已经看到纹理变量的声明方式为:

代码语言:javascript
复制
uniform sampler2D sTexture;

我们知道 uniform 属性值由应用程序赋值,

代码语言:javascript
复制
/** 生成一个纹理id,texutes 用以接收纹理句柄id */
代码语言:javascript
复制

如果要把改纹理绘制到屏幕上,还需指定纹理的映射关系,通常我们需要指定顶点坐标,每个顶点坐标对应一个纹理坐标(Texture Coordiate),用来标明纹理图像的哪部分被采集片段颜色(采样)。

2D 纹理坐标(x,y)范围在 0 - 1 之间,它是一个归一化坐标,不依赖实际分辨率。 纹理坐标起始点为(0,0),(0,0) 在纹理图片的左下角,与 Android 屏幕坐标系 y 轴相反,终始于(1,1),即纹理图片的右上角。使用纹理坐标获取纹理颜色的过程叫做纹理采样(Sampling)。

将上述纹理映射到三角形上

06 OpenGL 绘制纹理

现在我们已经有一个纹理图片了,现在我们就把这张图片绘制到屏幕上,对以上内容做个整合,首先,准备顶点和片元着色器代码:

顶点着色器:

代码语言:javascript
复制
private static final String VERTEX_SHADER =

在顶点着色其中我们声明了一个 aPosition 属性,aPosition 用以确定在窗口中的绘制位置。另外,我们也声明了一个 aTextureCoord 属性,该属性用来确定纹理坐标。 vTextureCoord 会传递给片元着色器,片元着色器通该属性的插值结果对纹理进行采样。

片元着色器:

代码语言:javascript
复制
private static final String FRAGMENT_SHADER_2D =

在片元着色器中,我们通过 vTextureCoord 获取从顶点着色器传入的纹理坐标,通过定义 sampler2D 属性用来接收程序传入需要绘制的纹理,然后通过 texture2D 方法对纹理进行采样渲染。

紧接着,我们需要创建一个 Program ,并生产一个纹理 id,

代码语言:javascript
复制
// GPU2DTextureProgram 为 AVPlayer 封装的 2D 纹理绘制程序

然后,我们在 GLSurafaceView 的 Render 方法中进行绘制,GLSurafaceView 我们会在下篇文章进行讲解。

代码语言:javascript
复制
@Override

详见 DemoGLTextureActivity

该部分代码已经在 AVPlayer 项目中有详细说明,这里就不在做介绍。

最终效果如下:

DemoGLTextureActivity

07 结束语

现在, 你已经对 OpenGLES 有所了解,对接下来 GLSurafeView 的使用打下了基础,这部分内容我们将在下篇文章中进行讲解。

往期内容:

iOS/Android 音视频开发专题介绍

iOS/Android 音视频概念介绍

MediaCodec/OpenMAX/StageFright 介绍

使用 MediaExtractor 及 MediaCodec 解码音视频

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-05-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DevTips 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档