Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Android SDK
Code

如何在Android APP中使用OpenGL ES

by
Difficulty:AdvancedLength:LongLanguages:

Chinese (Simplified) (中文(简体)) translation by Zhang Xiang Liang (you can also view the original English article)

现在市面上几乎每个Android手机都有一个图形处理单元,或简称为GPU。顾名思义,就是专门处理与3D图形计算相关的硬件单元。 作为一名APP开发人员,你可以利用GPU创建复杂的图形和动画,这些图形和动画可以以非常高的帧速率运行。

目前有两种不同的API可用于与Android设备的GPU交互:VulkanOpenGL ES。Vulkan仅适用于运行Android 7.0或更高版本的设备,而所有Android版本都支持OpenGL ES。

在本教程中,我将帮助你在Android APP中使用OpenGL ES 2.0。

准备工作

为了与本文一致,你需要:

  • 最新版本的Android Studio
  • 支持OpenGL ES 2.0或更高版本的Android设备
  • 最新版本的Blender,或任何其他3D建模软件

1.什么是OpenGL ES?

OpenGL是Open Graphics Library的缩写,它是一个独立于平台的API,可以创建硬件加速的3D图形。OpenGL ES是OpenGL for Embedded Systems的缩写,是OpenGL API的一个子集。

OpenGL ES是一个非常低级的API。换句话说,它不提供任何让你快速创建或操纵3D对象的方法。 相反,在使用它时,你需要手动管理任务,例如创建3D对象的各个顶点和面,计算各种3D变换,以及创建不同类型的着色器。

还值得一提的是,Android SDK和NDK可以让你在Java和C中编写与OpenGL ES相关的代码。

2.项目设置

因为OpenGL ES API是Android框架的一部分,所以不必为项目添加任何依赖关系就能够使用它们。 但是在本教程中,我们将使用Apache Commons IO库来读取几个文本文件的内容。因此,需要在build.gradle文件中的依赖项添加相关依赖:

此外,有些设备不支持OpenGL ES,无法安装你的APP,为了阻止此类Google Play用户,请将<uses-feature>添加到项目的清单文件中:

3.创建画布

Android框架为3D图形画布提供了两个控件:GLSurfaceView和TextureView。 大多数开发人员喜欢使用GLSurfaceView,只有当他们打算在其他View控件上叠加3D图形时才选择TextureView。这里,我们选择GLSurfaceView足够了。

GLSurfaceView控件添加到布局文件。

请注意,我们已经将窗口的宽度与高度设置为相等。这样做很重要,因为OpenGL ES坐标系是一个正方形。 如果你必须使用矩形画布,请记住在计算投影矩阵时使用宽高比。你将在后面了解投影矩阵。

Activity类中通过findViewById()方法初始化GLSurfaceView控件。

此外,我们必须调用setEGLContextClientVersion()方法来明确指定OpenGL ES版本。

4.创建3D对象

虽然可以通过手动编写所有顶点的X,Y和Z坐标来创建Java中的3D对象,但这样做非常麻烦。 相反,使用3D建模工具更容易。Blender就是这样的工具。它开源,功能强大,且非常容易学习。

启动Blender并按X删除默认立方体。接下来,按Shift-A并选择 Mesh > Torus。我们现在有一个相当复杂的3D对象,由576个顶点组成。

Torus in Blender

为了能够在Android APP中使用环面,我们必须将其导出为Wavefront OBJ文件。因此,进入File > Export > Wavefront (.obj)。 在下一个屏幕中,给OBJ文件一个名称,确保选择了Triangulate FacesKeep Vertex Order选项,然后点击Export OBJ按钮。

Exporting 3D object as Wavefront OBJ file

你现在可以关闭Blender并将OBJ文件移动到Android Studio项目的assets文件夹。

5.解析OBJ文件

如果你还没有注意到我们在上一步中创建的OBJ文件是一个文本文件,可以使用任何文本编辑器打开。

OBJ file opened with a text editor

在文件中,以“v”开头的每一行代表单个顶点。类似地,以“f”开始的每一行表示单个三角形面。 当每个顶点线包含顶点的X,Y和Z坐标时,每个面线包含三个顶点的索引,它们一起形成一个面。这就是解析OBJ文件需要知道的一切。

在开始之前,创建一个名为Torus的新Java类,并添加两个List对象作为成员变量,一个用于顶点,一个用于面。

读取OBJ文件的所有行的最简单方法是使用Scanner类及其nextLine()方法。当循环遍历行并填充两个列表时,你可以使用String类的startsWith()方法来检查当前行是否以“v”或“f”开头。

6.创建缓冲区对象

你不能将顶点和面的列表直接传递给OpenGL ES API中的方法。你必须先将其转换为缓冲对象。 要存储顶点坐标数据,我们需要一个FloatBuffer对象。对于仅由顶点索引组成的面部数据,ShortBuffer对象就足够了。

因此,将以下成员变量添加到Torus类中:

要初始化缓冲区,我们必须首先使用allocateDirect()方法创建ByteBuffer对象。对于顶点缓冲区,为每个坐标分配四个字节,坐标是浮点数。 一旦ByteBuffer对象被创建,你可以通过调用它的asFloatBuffer()方法将其转换成FloatBuffer

同样,为面缓冲区创建另一个ByteBuffer对象。这一次,为每个顶点索引分配两个字节,因为索引是unsigned short类型。 此外,请确保使用asShortBuffer()方法将ByteBuffer对象转换为 ShortBuffer

填充顶点缓冲区需要循环遍历verticesList内容,从每项中提取X,Y和Z坐标,并调用put()将数据放入缓冲区。因为verticesList只包含字符串,所以我们必须使用parseFloat()将坐标从字符串转换为float值。

请注意,在上述代码中,我们使用position()方法来重置缓冲区的位置。

填充面缓冲区略有不同。你必须使用parseShort()方法将每个顶点索引转换为一个short类型的值。 另外,由于索引从一开始而不是从零开始,因此你必须记住在将它们放入缓冲区之前从它们中减一。

7.创建着色器

为了能够渲染我们的3D对象,我们必须为它创建一个顶点着色器和片段着色器。现在,可以将着色器看成一个非常简单的程序,用类似C语言的语言编写,称为OpenGL着色语言,简称GLSL。

机智如你,顶点着色器负责处理3D对象的顶点。片段着色器(也称为像素着色器)负责着色3D对象的像素。

步骤1:创建顶点着色器

在项目的res/raw文件夹中创建一个名为vertex_shader.txt的新文件。

顶点着色器必须有一个attribute全局变量,以便从Java代码接收顶点位置数据。此外,添加一个uniform全局变量以便从Java代码接收视图投影矩阵。

在顶点着色器的main()方法内部,你必须设置gl_position值,它是GLSL内置变量,该变量决定顶点的最终位置。现在,你可以简单地将其值设置为的产品uniformattribute全局变量。

将以下代码添加到文件中:

步骤2:创建片段着色器

在项目的res/raw文件夹中创建一个名为fragment_shader.txt的新文件。

为了简化本教程,我们现在创建一个非常简约的片段着色器,只需将橙色分配给所有像素。要将一个颜色分配给一个像素,在片段着色器的main()方法内部,使用gl_FragColor内置的变量。

在上面的代码中,第一行指定浮点数精度很重要,因为片段着色器对没有任何默认的精度。

第3步:编译着色器

回到Torus类,你现在必须添加代码来编译你创建的两个着色器。但是,在此之前,必须先将它们从原始资源转换为字符串。 IOUtils类(是Apache Commons IO 库的一部分)有一个toString()可以做到。代码如下:

着色器的代码必须添加到OpenGL ES着色器对象中。要创建一个新的着色器对象,请使用GLES20类的glCreateShader()方法。 根据要创建的着色器对象的类型,你可以传递GL_VERTEX_SHADER或者GL_FRAGMENT_SHADER给它。该方法返回一个整数,该整数作为着色器对象的引用。 新创建的着色器对象不包含任何代码。要将着色器代码添加到着色器对象,必须使用glShaderSource()方法。

以下代码是顶点着色器和片段着色器创建着色器对象:

我们现在可以将着色器对象传递给glCompileShader()方法来编译它们包含的代码。

8.创建一个程序

不要直接使用着色器渲染3D对象。而是将它们附加到程序然后再使用。因此,向Torus类添加一个成员变量用来存储OpenGL ES程序的引用。

要创建一个新程序,请使用glCreateProgram()方法。要将顶点和片段着色器对象附加到它,请使用glAttachShader()方法。

此时,你可以链接程序并开始使用它。使用glLinkProgram()和glUseProgram()方法来做。

9.绘制3D对象

随着着色器和缓冲区的准备,我们有了绘制环面所需要的一切。在Torus类中添加一个新方法draw

在前面的步骤中,我们在顶点着色器内部定义了一个position变量来接收来自Java代码的顶点位置数据。现在是将顶点位置数据发送给它的时候了。 为此,我们必须首先使用glGetAttribLocation()方法获取Java代码中的position变量的句柄。此外,必须使用glEnableVertexAttribArray()方法启用句柄。

draw()方法中添加以下代码:

我们必须使用glVertexAttribPointer()方法来将position句柄指向我们的顶点缓冲区。除了顶点缓冲区本身之外,该方法还要求每个顶点的坐标数,坐标的类型以及每个顶点的字节偏移量。 因为我们每个顶点有三个坐标,每个坐标是一个float类型,所以字节偏移必须是3 * 4

我们的顶点着色器还需要一个视图投影矩阵。虽然这样的矩阵并不总是必需的,但使用它可以更好地控制3D对象的渲染方式。

视图投影矩阵只是视图和投影矩阵的乘积。视图矩阵允许你指定相机的位置和它正在查看的点。 另一方面,投影矩阵不仅能让你将OpenGL ES的平方坐标系映射到Android设备的矩形屏幕,还可以指定观察 viewing frustum的近平面和远平面。

要创建矩阵,可以简单地创建三个float类型的数组,每个数组大小为16

要初始化投影矩阵,可以使用Matrix类的frustumM()方法。它需要左,右,底,顶,近和远剪辑平面的位置。 因为我们的画布已经是一个正方形,所以左和右的值为-11,顶和底的值也为-1和1。对于近和远的剪辑平面,请随时尝试不同的值。

要初始化视图矩阵,请使用setLookAtM()方法。它需要相机的位置和它正在看的点。你再次自由尝试不同值。

最后,使用multiplyMM()方法计算产品矩阵。

要将产品矩阵传递给顶点着色器,你必须使用glGetUniformLocation()方法获取matrix变量的句柄。一旦有了句柄,就可以使用glUniformMatrix()方法将其指向产品矩阵。

你要知道我们还没有使用faces缓冲区。这意味着我们还没有告诉OpenGL ES如何连接顶点来形成三角形,这将作为我们3D对象的面。

glDrawElements()方法让你使用faces缓冲区来创建三角形。需要的参数有,顶点索引的总数,每个索引的类型和面缓冲区。

最后,记得禁用attribute handler,之前将顶点数据传递给顶点着色器的时候被你打开了的。

10.创建渲染器

我们的GLSurfaceView控件需要一个GLSurfaceView.Renderer对象来渲染3D图形。可以使用setRenderer()将渲染器与其关联。

在渲染器的onSurfaceCreated()方法内,你必须指定3D图形呈现的频率。现在,只有当3D图形发生变化时,我们才能渲染。 为此,将RENDERMODE_WHEN_DIRTY常数传递给setRenderMode()方法。此外,初始化Torus对象的新实例。

在渲染器的onSurfaceChanged()方法内,可以使用glViewport()方法定义 viewport的宽度和高度。

在渲染器的onDrawFrame()方法内,调用Torus类的draw()方法来实际绘制圆环。

现在,运行应用程序来查看橙色圆环。

App displaying torus

结语

你现在知道如何在Android APP中使用OpenGL ES了。在本教程中,你还学到了如何解析Wavefront OBJ文件并从中提取顶点和面数据。 我建议你使用Blender生成更多的3D对象,并尝试在APP中渲染。

虽然我们只关注OpenGL ES 2.0,但是也要知道OpenGL ES 3.x向后兼容了OpenGL ES 2.0。这意味着,如果你喜欢在你的APP中使用OpenGL ES 3.x中,你可以用类GLES30GLES31类简单地替换GLES20类。

要了解有关OpenGL ES的更多信息,可以参考其它页面。要了解有关Android APP开发的更多信息,请在Envato Tuts +上查看我们的其他教程!


关注我们的公众号
Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.