Android OpenGL ES 完整教程
目录
-
第一部分:基础入门
(图片来源网络,侵删)- 什么是 OpenGL ES?
- 为什么在 Android 上使用 OpenGL ES?
- 开发环境准备
- 你的第一个 OpenGL ES 应用:一个黑色的屏幕
-
第二部分:绘制你的第一个图形
- 核心概念:渲染管线
- 顶点着色器和片段着色器
- 创建着色器程序
- 定义顶点数据
- 绘制一个三角形
-
第三部分:让图形动起来
- 透视投影和相机视图
- 模型变换
- 动画循环:
onDrawFrame
-
第四部分:进阶主题
- 纹理映射
- 3D 模型加载
- 光照与材质
- 使用 GLSurfaceView 的最佳实践
-
第五部分:推荐资源与学习路径
(图片来源网络,侵删)
第一部分:基础入门
什么是 OpenGL ES?
- OpenGL (Open Graphics Library): 一个跨语言、跨平台的图形 API,用于渲染 2D 和 3D 矢量图形。
- ES (Embedded Systems): 专为嵌入式系统(如手机、平板)设计的子集。
- 核心思想: OpenGL ES 是一个状态机,你设置一系列的状态(如当前颜色、使用的着色器、要绘制的顶点等),然后发出绘制命令,GPU 会根据当前状态进行渲染,它更像一个虚拟显卡的驱动,而不是一个高级的图形引擎。
为什么在 Android 上使用 OpenGL ES?
- 高性能: 对于游戏、数据可视化、视频播放等需要高性能图形的应用,OpenGL ES 是不二之选。
- 硬件加速: 它直接利用设备的 GPU 进行渲染,比在 CPU 上用软件绘制快得多。
- 跨平台: 基于 OpenGL ES 的代码可以在支持该标准的 Android 设备上运行。
开发环境准备
- 安装 Android Studio: 确保你安装了最新版的 Android Studio。
- 创建新项目: 选择 "Empty Views Activity"。
- 添加 OpenGL ES 依赖: 在
build.gradle(Module: app) 文件中,确保你有以下依赖:dependencies { // ... 其他依赖 implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.google.android.material:material:1.11.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' // OpenGL ES 相关库通常已包含在 SDK 中,无需额外添加 }
你的第一个 OpenGL ES 应用:一个黑色的屏幕
要使用 OpenGL ES,最简单的方式是使用 GLSurfaceView,它是一个专门为 OpenGL ES 渲染设计的 SurfaceView。
步骤 1: 创建 GLSurfaceView
在布局文件 activity_main.xml 中,添加一个 GLSurfaceView:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.opengl.GLSurfaceView
android:id="@+id/gl_surface_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
步骤 2: 初始化 GLSurfaceView

在 MainActivity.java (或 .kt) 中,进行基本配置:
public class MainActivity extends AppCompatActivity {
private GLSurfaceView glSurfaceView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
glSurfaceView = findViewById(R.id.gl_surface_view);
// 1. 设置 EGL 配置
glSurfaceView.setEGLContextClientVersion(3); // 使用 OpenGL ES 3.0,推荐使用 3.0+
// 可以在这里设置像素格式,glSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
// 2. 设置渲染器
// 我们将在下一步实现 MyGLRenderer
glSurfaceView.setRenderer(new MyGLRenderer());
// 3. 设置渲染模式:仅在数据变化时渲染(更省电)
glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
@Override
protected void onPause() {
super.onPause();
glSurfaceView.onPause(); // 必须调用,以避免内存泄漏
}
@Override
protected void onResume() {
super.onResume();
glSurfaceView.onResume(); // 必须调用
}
}
步骤 3: 创建渲染器 MyGLRenderer
创建一个新类 MyGLRenderer,实现 GLSurfaceView.Renderer 接口,我们只让它清空屏幕为黑色。
import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class MyGLRenderer implements GLSurfaceView.Renderer {
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// 当 Surface 被创建时调用,只调用一次
// 设置清空屏幕的颜色为黑色
gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// 当 Surface 的尺寸发生变化时调用(屏幕旋转)
gl.glViewport(0, 0, width, height);
}
@Override
public void onDrawFrame(GL10 gl) {
// 每一帧绘制时调用
// 清空颜色缓冲区,使用 onSurfaceCreated 中设置的颜色
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
}
}
现在运行你的应用,你应该能看到一个全屏的黑色窗口,恭喜,你已经成功启动了 OpenGL ES 环境!
第二部分:绘制你的第一个图形
核心概念:渲染管线
渲染管线是 GPU 处理图形数据的一系列固定步骤,对于初学者,可以简化为:
- 顶点数据: 你提供的一系列点(顶点)。
- 顶点着色器: 对每个顶点进行处理,如计算其最终在屏幕上的位置。
- 图元装配: 将顶点组装成指定的形状(如三角形、线段)。
- 光栅化: 确定哪些像素在图形内部。
- 片段着色器: 为每个像素计算最终的颜色。
- 测试与混合: 深度测试、模板测试、混合等,最终将颜色写入帧缓冲区。
顶点着色器和片段着色器
着色器是运行在 GPU 上的小程序,使用 GLSL (OpenGL Shading Language) 编写。
- 顶点着色器: 负责处理顶点,最基本的功能是接收顶点位置,并将其转换到屏幕坐标。
- 片段着色器: 负责处理像素,最基本的功能是输出一个颜色。
创建着色器程序
在 Java/Kotlin 代码中,我们需要加载、编译 GLSL 代码,然后将它们链接成一个可执行程序。
定义顶点数据
一个三角形由三个顶点定义,每个顶点包含其坐标,在 OpenGL ES 中,坐标通常在 -1.0 到 1.0 的范围内,这被称为标准化设备坐标。
绘制一个三角形
让我们修改 MyGLRenderer 来绘制一个红色三角形。
步骤 1: 编写着色器代码
在 MyGLRenderer 类中,定义 GLSL 字符串:
// 顶点着色器的代码
private final String vertexShaderCode =
"attribute vec4 vPosition;" // 顶点位置变量
"void main() {"
" gl_Position = vPosition;" // 直接将顶点位置赋值给 gl_Position
"}";
// 片段着色器的代码
private final String fragmentShaderCode =
"precision mediump float;" // 设置片段着色器的默认精度
"uniform vec4 vColor;" // 颜色变量
"void main() {"
" gl_FragColor = vColor;" // 将颜色赋值给 gl_FragColor
"}";
private int mProgram;
private float 