杰瑞科技汇

Android 3D教程怎么学?从哪开始入门?

我们将主要使用 Google 推荐的 OpenGL ES (Open Graphics Library for Embedded Systems) API,并重点介绍现代、高效的 Vulkan API,也会提及一些更高级的框架,如 UnityAndroid Studio 内置的 Sceneform

Android 3D教程怎么学?从哪开始入门?-图1
(图片来源网络,侵删)

目录

  1. 第一步:准备工作与环境搭建
  2. 核心概念:3D 图形流水线
  3. 技术路线选择:OpenGL ES vs. Vulkan
  4. 深入 OpenGL ES
    • 1. 创建一个 OpenGL ES 项目
    • 2. 绘制你的第一个三角形:渲染管线详解
    • 3. 添加颜色与纹理
    • 4. 实现 3D 效果:模型视图投影矩阵
    • 5. 添加光照
    • 6. 加载 3D 模型
  5. 探索 Vulkan
  6. 更高级的选择:Sceneform
  7. 专业级选择:Unity
  8. 推荐学习资源
  9. 总结与学习路径

第一步:准备工作与环境搭建

在开始之前,请确保你的开发环境已经就绪。

  • Android Studio: 最新版本的 Android Studio。
  • Android SDK: 安装最新的 SDK 和 SDK Build-Tools。
  • NDK (Native Development Kit): 如果你打算用 C/C++ 编写 OpenGL/Vulkan 代码,需要安装 NDK,这通常是最现代和推荐的方式。
  • 模拟器或真机: 强烈建议使用支持 OpenGL ES 3.0 或更高版本的真机进行开发,性能和兼容性更好。

项目创建: 在 Android Studio 中创建一个新项目时,选择 "Native C++" 模板,这会自动为你设置好 CMake 和 JNI 的环境,让你可以轻松地在 Java/Kotlin 和 C/C++ 代码之间进行交互。


核心概念:3D 图形流水线

在写代码之前,理解 3D 图形是如何在屏幕上显示出来的至关重要,这被称为 图形渲染管线,它是一个将 3D 模型数据最终转换为 2D 像素图像的固定流程。

主要阶段如下:

  1. 顶点数据: 你的 3D 模型由无数个顶点组成,每个顶点包含位置、颜色、纹理坐标等信息。
  2. 顶点着色器: 对每个顶点进行变换,将模型的局部坐标转换到世界坐标、视图坐标,最后是投影坐标(也就是我们常说的 MVP 矩阵变换)。
  3. 图元装配: 将顶点组装成指定的图元,如点、线或三角形。
  4. 光栅化: 确定哪些屏幕像素(片段)被图元覆盖,并计算出这些片段的坐标和插值后的属性(如颜色、纹理坐标)。
  5. 片段着色器: 对每个片段进行处理,决定其最终的颜色,这是实现光照、纹理贴图等效果的核心。
  6. 测试与混合: 进行深度测试(确保远处的物体不会挡住近处的)、模板测试,以及将片段颜色与屏幕上已有颜色进行混合(实现半透明效果等)。
  7. 帧缓冲: 最终处理好的像素被写入到帧缓冲中,然后显示在屏幕上。

关键工具:

  • GLSL (OpenGL Shading Language): 用于编写顶点着色器和片段着色器的语言,你将在 C++ 代码中以字符串的形式嵌入它们。

技术路线选择:OpenGL ES vs. Vulkan

对于初学者,选择合适的 API 非常重要。

特性 OpenGL ES (ES 3.0+) Vulkan
易用性 ,API 设计直观,封装了底层的复杂性,学习曲线较平缓。 ,API 非常底层和繁琐,需要开发者手动管理大量资源(如内存、命令缓冲区),学习曲线陡峭。
性能 良好,由驱动程序优化,但有一定开销。 极高,CPU 开销极低,并行度极高,能充分发挥硬件性能,适合移动高端设备和 PC 游戏。
控制力 较低,许多细节由驱动程序处理。 极高,开发者对渲染管线有完全的控制权。
适用场景 初学者、大多数 App、UI、中等复杂度的 3D 效果。 专业游戏、高性能计算、需要极致优化的 3D 应用。

建议:

  • 如果你是初学者从 OpenGL ES 3.0 开始,它能让你快速理解核心概念,并看到成果,建立信心。
  • 如果你是经验丰富的开发者,追求极致性能直接学习 Vulkan,虽然痛苦,但回报是巨大的性能提升。

深入 OpenGL ES

我们将通过一个 C++ 项目来演示。

1. 创建一个 OpenGL ES 项目

如前所述,使用 "Native C++" 模板创建项目,你会看到几个关键文件:

  • app/src/main/cpp/native-lib.cpp: C++ 代码的入口。
  • app/src/main/cpp/CMakeLists.txt: 用于编译 C++ 代码的脚本。
  • app/src/main/java/com/yourpackage/MainActivity.java: Java/Kotlin 主活动,用于加载 C++ 库和设置 GLSurfaceView

GLSurfaceView 是一个专门用于渲染 OpenGL 内容的 SurfaceView

2. 绘制你的第一个三角形

这是 "Hello, World!" 的 3D 版本。

步骤 1: 设置 GLSurfaceViewMainActivity 中,你需要设置一个 GLSurfaceView.RendererRenderer 是一个接口,它定义了 OpenGL 绘制的三个关键生命周期方法:

  • onSurfaceCreated(): 当 Surface 创建时调用,通常在这里初始化 OpenGL 环境和加载着色器。
  • onSurfaceChanged(int width, int height): 当 Surface 大小改变时调用,通常在这里设置视口和投影矩阵。
  • onDrawFrame(): 每一帧都会调用,这是执行渲染指令的地方。
// In MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    GLSurfaceView glView = findViewById(R.id.glview);
    glView.setEGLContextClientVersion(3); // 使用 OpenGL ES 3.0
    glView.setRenderer(new MyGLRenderer()); // 设置你的 Renderer
    glView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); // 当数据变化时才渲染
}

步骤 2: 编写着色器native-lib.cpp 中,我们将定义 GLSL 代码字符串。

顶点着色器 (simple_vertex_shader.glsl):

attribute vec4 vPosition; // 顶点位置属性
void main() {
    gl_Position = vPosition; // 直接将顶点位置设置为裁剪空间坐标
}

片段着色器 (simple_fragment_shader.glsl):

precision mediump float; // 设置浮点数精度
void main() {
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 所有片段都输出红色
}

步骤 3: 加载着色器并编译链接 你需要 C++ 函数来加载这些字符串,并编译、链接它们成一个可执行的着色器程序,这涉及到 glCreateShader, glShaderSource, glCompileShader, glCreateProgram, glAttachShader, glLinkProgram 等调用。

步骤 4: 定义顶点数据 定义一个三角形的顶点坐标数组,这些坐标是在一个标准的立方体空间(-1.0 到 1.0)中定义的,称为裁剪空间

// 定义一个三角形的顶点坐标
static const GLfloat triangleVertices[] = {
     0.0f,  0.5f, 0.0f, // 顶点1
    -0.5f, -0.5f, 0.0f, // 顶点2
     0.5f, -0.5f, 0.0f  // 顶点3
};

步骤 5: 在 onDrawFrame 中绘制

void MyGLRenderer::onDrawFrame(GL10 unused) {
    // 清除颜色缓冲区,为下一帧做准备
    glClear(GL_COLOR_BUFFER_BIT);
    // 使用着色器程序
    glUseProgram(mProgram);
    // 绑定顶点数据
    // 1. 启用顶点属性数组
    glEnableVertexAttribArray(mPositionHandle);
    // 2. 将顶点数据指针传递给着色器
    glVertexAttribPointer(mPositionHandle, 3, GL_FLOAT, false, 0, triangleVertices);
    // 绘制三角形
    glDrawArrays(GL_TRIANGLES, 0, 3); // 从第0个顶点开始,绘制3个顶点
    // 禁用顶点属性数组
    glDisableVertexAttribArray(mPositionHandle);
}

现在运行你的 App,你应该能在屏幕中央看到一个红色的三角形!

3. 添加颜色与纹理

  • 颜色: 修改顶点数据,为每个顶点添加一个颜色属性,然后在顶点着色器中将颜色传递给片段着色器,片段着色器接收插值后的颜色并输出。
  • 纹理:
    1. 加载图片: 在 Java/Kotlin 中使用 BitmapFactory 加载一张图片。
    2. 生成纹理: 在 C++ 中调用 glGenTextures 创建一个纹理对象。
    3. 绑定并上传数据: 调用 glBindTextureglTexImage2D 将图片数据上传到 GPU。
    4. 修改着色器: 顶点着色器需要传递纹理坐标,片段着色器使用 sampler2Dtexture2D() 函数来采样纹理颜色。

4. 实现 3D 效果:模型视图投影矩阵

目前我们画的是 2D 三角形,要让它看起来是 3D 的,需要使用矩阵变换

  • 模型矩阵: 将模型从自己的局部空间移动到世界空间(如旋转、平移、缩放)。
  • 视图矩阵: 将世界中的所有物体移动到相机(观察者)的视角下,通常将相机放在原点,并看向 Z 轴负方向。
  • 投影矩阵: 将 3D 坐影转换到 2D 屏幕坐标,有透视投影(近大远小)和正交投影。

这三个矩阵相乘 (Projection * View * Model) 得到最终的 MVP 矩阵

在 C++ 中,你可以使用 GLM (OpenGL Mathematics) 库来方便地进行矩阵运算。

流程:

  1. 在 C++ 中创建一个 glm::mat4 类型的 MVP 矩阵。
  2. 使用 GLM 函数填充这个矩阵。
  3. 在顶点着色器中,定义一个 uniform mat4 uMVPMatrix; 变量。
  4. 在 C++ 中获取 uMVPMatrix 的位置 (glGetUniformLocation)。
  5. onDrawFrame 中,使用 glUniformMatrix4fv 将计算好的 MVP 矩阵传递给着色器。
  6. 在顶点着色器中,将 gl_Position 修改为 gl_Position = uMVPMatrix * vPosition;

通过修改模型矩阵,你就可以让三角形旋转起来!

5. 添加光照

要实现逼真的 3D 效果,光照是必不可少的,最简单的是冯氏光照,它包含三个分量:

  • 环境光: 环境的基础亮度。
  • 漫反射光: 来自特定方向的漫反射,取决于表面法线和光照方向的夹角。
  • 镜面高光: 来自特定方向的镜面反射,取决于视线方向和反射方向的夹角。

这需要在片段着色器中进行计算,你需要为着色器提供:

  • 光源位置
  • 物体表面颜色(或纹理)
  • 物体表面法线(每个顶点一个)
  • 观察者(相机)位置

计算会变得复杂一些,但效果非常棒。

6. 加载 3D 模型

手动定义顶点数据只适合简单的几何体,加载复杂的 3D 模型(如 .obj 文件)是常见需求。

  1. 解析模型文件: 你需要一个解析器(如 tinyobjloader)来读取 .obj 文件,提取顶点位置、法线、纹理坐标和面信息(索引)。
  2. 使用索引缓冲区: 为了避免重复的顶点数据,模型文件通常使用索引,你需要创建一个顶点缓冲对象 和一个索引缓冲对象
  3. 渲染: 使用 glDrawElements 代替 glDrawArrays,并传递索引缓冲区。

探索 Vulkan

如果你已经掌握了 OpenGL ES 并且对底层性能有追求,那么可以开始学习 Vulkan。

学习 Vulkan 的挑战在于它需要你做 OpenGL 驱动程序为你做的大部分事情:

  • 实例和设备: 显式创建 Vulkan 实例和逻辑设备。
  • 交换链: 管理前后缓冲区,防止画面撕裂。
  • 命令缓冲区: 你需要自己录制一系列绘制命令,然后提交给 GPU 执行。
  • 内存管理: 你需要显式地为每个资源(缓冲区、图像)分配内存,并指定其类型(如设备本地、主机可见等)。

好处是,一旦你掌握了这些繁琐的设置,你就能获得极高的性能和更可预测的帧率,Vulkan 是 3D 图形的未来,尤其是在移动平台上。


更高级的选择:Sceneform

如果你不想陷入底层的 OpenGL/Vulkan 细节,只想快速地在 AR 或普通 App 中添加 3D 内容,Sceneform 是一个绝佳的选择。

  • 是什么: Sceneform 是一个由 Google 提供的高层 3D 框架,专门为 Android 设计,它使用 Vulkan 作为后端,但为你提供了简洁的 Java/Kotlin API。
  • 优点:
    • 纯 Java/Kotlin: 无需编写 C++。
    • 基于物理的渲染: 轻松实现逼真的光照和材质。
    • 易于使用: API 设计直观,几行代码就能加载和显示一个 3D 模型。
    • 内置 AR 支持: 与 ARCore 无缝集成。
  • 缺点:
    • 灵活性较低: 不像 OpenGL/Vulkan 那样可以完全控制渲染管线。
    • 社区较小: 相比 OpenGL,学习资源和社区支持较少。

适合人群: Android 应用开发者,希望快速集成 3D 内容,而不是成为图形专家。


专业级选择:Unity

如果你的目标是开发一个完整的 3D 游戏或复杂的 3D 应用,而不是在 App 中嵌入一些 3D 效果,Unity 是行业标准。

  • 是什么: 一个完整的游戏引擎,它内置了强大的渲染器、物理引擎、动画系统、音频系统等。
  • 工作流程:
    1. 在 Unity 编辑器中进行可视化场景搭建、材质编辑、动画制作。
    2. 使用 C# 编写游戏逻辑。
    3. Unity 负责将你的所有内容打包成一个 Android App。
  • 优点:
    • 极高效率: 无需处理底层图形 API,专注于创意和玩法。
    • 庞大的生态系统: 资源商店、教程社区、工具链极其成熟。
    • 跨平台: 一套代码可以发布到 Android, iOS, Windows, macOS, Console 等。
  • 缺点:
    • 引擎体积大: 最终的 App 包体积会比较大。
    • 非原生: 对 Android 平台特性的直接控制不如原生开发灵活。

推荐学习资源

  • OpenGL ES:

    • Learn OpenGL (中文版): https://learnopengl-cn.github.io/ - 必看! 从最基础的光照到高级的阴影、PBR,讲解非常清晰,有 C++ 代码示例。
    • OpenGL ES 3.0 Programming Guide: 官方书籍,非常权威。
    • GitHub: 搜索 opengl-es-tutorial-android 可以找到很多开源项目。
  • Vulkan:

  • Sceneform:

  • Unity:

    • Unity Learn: https://learn.unity.com/ - Unity 官方学习平台,有大量免费课程。
    • B站/YouTube: 无数优秀的 Unity 教程。

总结与学习路径

  1. 初学者/应用开发者:

    • 目标: 在 App 中添加漂亮的 3D 效果或 AR 功能。
    • 路径: Sceneform -> 如果需要更多控制,再学习 OpenGL ES 3.0,直接跳过 Vulkan。
  2. 游戏开发者/图形工程师:

    • 目标: 开发高性能的 3D 游戏或应用。
    • 路径: Unity (快速上手和开发) -> OpenGL ES 3.0 (理解底层原理) -> Vulkan (追求极致性能和平台控制)。

学习 3D 图形是一个循序渐进的过程,从简单的三角形开始,逐步添加颜色、纹理、光照和变换,不要害怕遇到问题,查阅官方文档和优秀的开源项目是解决问题的关键,祝你学习愉快!

分享:
扫描分享到社交APP
上一篇
下一篇