Android 官方培训课程:用 GLSurfaceView 与 Renderer 搭建 OpenGL ES 2.0 渲染环境(android-training-course-in-chinese) 文档教程移动开发【免费下载链接】android-training-course-in-chineseAndroid官方培训课程中文版项目地址https://gitcode.com/gh_mirrors/an/android-training-course-in-chinese点击查看免费下载本文基于《Android 官方培训课程中文版》android-training-course-in-chinese中使用 OpenGL ES 显示图像系列课程的 environment.md 展开讲解如何从零为应用搭建一个可绘制 OpenGL ES 2.0 图形的完整环境。导读本文以 graphics/opengl/environment.md 为骨架系统讲解在 Android 应用中启用 OpenGL ES 的完整四步流程——在 Manifest 中声明 GL 能力、创建承载渲染的 Activity、构建GLSurfaceView容器、实现GLSurfaceView.Renderer渲染器。读完本文你将掌握glEsVersion声明、setEGLContextClientVersion()、setRenderer()、RENDERMODE_WHEN_DIRTY与requestRender()的搭配用法以及onSurfaceCreated()/onDrawFrame()/onSurfaceChanged()三个渲染回调的生命周期职责并理解如何沿着本仓库graphics/opengl系列课程把黑色屏幕推进到可交互的 3D 图形。环境搭建的三种路线为什么首选 GLSurfaceView要在应用中使用 OpenGL ES 绘制图像第一步是创建一个承载图形输出的 View 容器。Android 官方与本仓库教程推荐的最直接方案是组合使用两个类GLSurfaceView一个 View 容器专门用来存放使用 OpenGL 绘制的图形GLSurfaceView.Renderer一个渲染器接口用来控制在该 View 中实际绘制的内容。除了这条官方推荐路线environment.md 还给出了另外两种适用场景不同的备选方案可归纳为下表方案适用场景代价GLSurfaceViewRenderer全屏或接近全屏的图形 View集成成本最低需要继承 View 以捕获触摸事件见系列最后一课TextureView只把 OpenGL ES 图形嵌入整个布局的一小部分需要额外管理表面Surface生命周期SurfaceView手工搭建喜欢自己动手实现渲染管线需要编写大量额外代码从源码结构看本仓库的graphics/opengl课程目录index.md、environment.md、shapes.md、draw.md、projection.md、motion.md、touch.md采用的正是第一类方案且全部样例基于OpenGL ES 2.0 接口当前 Android 设备推荐版本。教程在 index.md 中特别强调不要把 OpenGL ES 1.x 接口与 2.0 接口混合调用两者不通用。第一步在 Manifest 中声明 OpenGL ES 能力为了让应用能够使用 OpenGL ES 2.0 接口必须把下列声明添加到 Manifest 配置文件中uses-feature android:glEsVersion0x00020000 android:requiredtrue /android:glEsVersion是一个十六进制编码高 16 位表示主版本号低 16 位表示次版本号。0x00020000即主版本 2、次版本 0代表 OpenGL ES 2.0android:requiredtrue表示该能力是应用运行所必需的系统会据此过滤掉不支持的设备确保应用只被安装到兼容设备上。如果应用还使用了纹理压缩Texture Compression那么还必须对支持的压缩格式逐一声明让应用仅安装在可以兼容的设备上supports-gl-texture android:nameGL_OES_compressed_ETC1_RGB8_texture / supports-gl-texture android:nameGL_OES_compressed_paletted_texture /supports-gl-texture元素用于声明应用所依赖的 GL 纹理压缩格式上例覆盖了 ETC1 RGB8 与调色板纹理两种常见格式。第二步创建承载渲染的 ActivityAndroid 应用在呈现 OpenGL ES 图形时同样以 Activity 作为用户界面入口与其他应用并无本质区别差异只体现在布局内容上——普通应用往布局里放TextView、Button、ListView而 OpenGL 应用则放入一个GLSurfaceView。下面的代码展示了以GLSurfaceView作为主 View 的 Activitypublic class OpenGLES20Activity extends Activity { private GLSurfaceView mGLView; Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Create a GLSurfaceView instance and set it // as the ContentView for this Activity. mGLView new MyGLSurfaceView(this); setContentView(mGLView); } }NoteOpenGL ES 2.0 需要 Android 2.2API Level 8或更高版本的系统请确保项目minSdkVersion满足该要求。第三步构建 GLSurfaceView 容器GLSurfaceView是一种特殊的 View它负责承载 OpenGL ES 图形但自身并不承担多少绘制任务——真正控制绘制内容的是在其中配置的GLSurfaceView.Renderer。它的核心代码非常简短为快速实现效果通常直接在 Activity 中创建一个内部类并使用class MyGLSurfaceView extends GLSurfaceView { private final MyGLRenderer mRenderer; public MyGLSurfaceView(Context context){ super(context); // Create an OpenGL ES 2.0 context setEGLContextClientVersion(2); mRenderer new MyGLRenderer(); // Set the Renderer for drawing on the GLSurfaceView setRenderer(mRenderer); } }这里的两个关键调用setEGLContextClientVersion(2)请求创建一个 OpenGL ES 2.0 的 EGL 上下文Context这是后续所有 GL 调用生效的前提setRenderer(mRenderer)把渲染器挂载到 View 上View 会将 GL 上下文与绘制时机交给该渲染器驱动。为什么不直接使用未修改的GLSurfaceView实例而要继承它environment.md 明确指出因为我们需要重写onTouchEvent()来捕捉触控事件。这一动机在系列课程的最后一课 touch.md 中得到了完整印证——通过重写onTouchEvent(MotionEvent e)监听MotionEvent.ACTION_MOVE把滑动距离换算成旋转角度并驱动渲染。可选优化RENDERMODE_WHEN_DIRTY 渲染模式GLSurfaceView默认采用持续渲染模式每帧都重绘。另一个可选配置是GLSurfaceView.RENDERMODE_WHEN_DIRTY其含义是仅当绘制数据发生变化时才重绘视图// Render the view only when there is a change in the drawing data setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);选用该模式后除非调用requestRender()否则GLSurfaceView不会被重新绘制。这能让示例应用更省电、更高效尤其适合图形只在交互时才变化的场景。这一机制在 touch.md 中被再次强调触摸回调在计算完旋转角度后调用requestRender()通知渲染器现在可以渲染了并配套setRenderMode(RENDERMODE_WHEN_DIRTY)实现按需绘制而在需要形状自动连续旋转的 motion.md 中则反过来要求注释掉这行配置恢复持续渲染。因此渲染模式的选择应当与图形变化是否由用户交互驱动这一条件绑定。第四步实现 Renderer 渲染器渲染器GLSurfaceView.Renderer的实现类是 OpenGL ES 应用中真正有趣的部分它控制着相关联的GLSurfaceView上绘制什么、如何绘制。Android 系统会调用渲染器中的三个方法其职责可归纳如下回调方法调用时机典型职责onSurfaceCreated()仅调用一次建立 View 的 OpenGL ES 环境如设置背景色、初始化形状对象onDrawFrame()每次重新绘制 View 时执行一帧的绘制指令如清屏、绘制形状onSurfaceChanged()View 几何形态变化时如屏幕方向改变更新视口Viewport如glViewport、重算投影矩阵下面是一个非常基本的渲染器实现它只在GLSurfaceView中绘制一个黑色背景public class MyGLRenderer implements GLSurfaceView.Renderer { public void onSurfaceCreated(GL10 unused, EGLConfig config) { // Set the background frame color GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); } public void onDrawFrame(GL10 unused) { // Redraw background color GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); } public void onSurfaceChanged(GL10 unused, int width, int height) { GLES20.glViewport(0, 0, width, height); } }要点拆解GLES20.glClearColor(r, g, b, a)设置清屏颜色四个分量取值范围都是0.0f ~ 1.0f1.0f为不透明GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)用清屏颜色填充颜色缓冲实现每帧重绘背景GLES20.glViewport(0, 0, width, height)把 GL 视口映射到整个GLSurfaceView区域屏幕旋转后系统会携带新的宽高再次回调。Note你可能会疑惑明明使用的是 OpenGL ES 2.0 接口为什么这些方法会有一个GL10类型的参数这是因为这些方法签名在 2.0 接口中被简单地复用了以此保持 Android 框架代码尽量简单。实际调用中该参数可忽略通常命名为unused。从黑色屏幕到完整渲染管线系列课程的衔接完成上述四步后应用已经具备可用的 OpenGL ES 环境——运行起来是一块黑色屏幕。environment.md结尾指出如果熟悉 OpenGL ES 接口现在就可以开始绘制图形了否则应继续学习系列后续课程。这些课程共同构成了从环境到可交互 3D 图形的完整链路定义形状shapes.md使用三维坐标系定义浮点顶点数组并通过ByteBuffer按nativeOrder()字节序传入 GL 管线顶点按逆时针顺序排列以定义正面矩形由两个三角形拼接而成并借助drawOrder索引数组避免重复顶点绘制形状draw.md编写顶点着色器与片段着色器GLSL 代码通过loadShader()→glCreateProgram()→glAttachShader()→glLinkProgram()构建 Program再在draw()中用glVertexAttribPointer()、glDrawArrays()真正绘制同时建议在onSurfaceCreated()中初始化形状对象以兼顾内存与效率运用投影与相机视角projection.md在onSurfaceChanged()中用Matrix.frustumM()基于屏幕宽高比计算投影矩阵在onDrawFrame()中用Matrix.setLookAtM()设定相机位置再以Matrix.multiplyMM()合成 MVP 矩阵修正形变添加移动motion.md用Matrix.setRotateM()创建旋转矩阵并乘入 MVP 矩阵让形状自动旋转此时需注释掉RENDERMODE_WHEN_DIRTY以启用连续渲染响应触摸事件touch.md回到本文第三步为什么要继承GLSurfaceView的答案——重写onTouchEvent()把ACTION_MOVE位移换算成角度通过setAngle()写入渲染器注意渲染器运行在独立 GL 线程跨线程共享的mAngle需声明为volatile再调用requestRender()触发重绘mRenderer.setAngle( mRenderer.getAngle() ((dx dy) * TOUCH_SCALE_FACTOR)); requestRender();可以看到触摸旋转三角形这一最终效果正是建立在本文所搭建的GLSurfaceViewRenderer环境之上的环境容器负责承载图形与接收触摸渲染器负责在每帧中应用旋转矩阵。常见问题与注意事项版本兼容性。OpenGL ES 2.0 要求 Android 2.2API Level 8Manifest 中glEsVersion声明不完整时应用可能被安装到不支持 GL 2.0 的设备上导致运行时异常。同时严禁混用 1.x 与 2.0 接口调用。渲染线程与 UI 线程。渲染器代码运行在GLSurfaceView内部创建的独立 GL 线程非主 UI 线程上。如 touch.md 所示凡是 UI 线程与渲染线程共享的数据如旋转角度应声明为volatile保证可见性。渲染模式与性能。只有图形随用户交互变化时才启用RENDERMODE_WHEN_DIRTYrequestRender()的组合若图形需要自动连续动画如 motion.md 的旋转示例则必须使用默认的连续渲染模式。着色器编译成本。编译着色器与链接 Program 对 CPU 周期与处理时间的消耗巨大见 draw.md应确保它们只被创建一次并缓存复用例如在形状类的构造函数中完成编译链接。继续深入学习本系列课程在仓库 SUMMARY.md 中登记为使用 OpenGL ES 显示图像章节包含六个小节建立环境、定义形状、绘制形状、投影与相机视角、添加移动、响应触摸事件。若想查看完整示例工程教程 index.md 提到官方配套的 OpenGLES 示例压缩包仓库其余课程目录如 graphics/displaying-bitmaps、graphics/opengl还可帮助你继续深入图形渲染与位图处理方向。赞分享文档教程移动开发【免费下载链接】android-training-course-in-chineseAndroid官方培训课程中文版项目地址https://gitcode.com/gh_mirrors/an/android-training-course-in-chinese点击查看免费下载相关推荐Android 官方培训课程中文版使用 OpenGL ES 2.0 绘制形状Drawing Shapes实战指南Android 官方培训课程中文版使用 OpenGL ES 2.0 绘制形状Drawing Shapes实战指南 本文基于开源仓库 android tra文档教程移动开发Android 网络操作实战指南连接网络、管理流量使用与解析 XML——基于 android-training-course-in-chinese 的官方培训课程精讲Android 网络操作实战指南连接网络、管理流量使用与解析 XML——基于 android training course in chinese 的官方培训文档教程移动开发Locust 安装完整指南pip 与 uvx 两种安装路径、预发布构建与常见安装问题排查Locust 安装完整指南pip 与 uvx 两种安装路径、预发布构建与常见安装问题排查 本文基于 Locust 官方安装文档 installation.rs文档教程移动开发上一篇浙江大学学位论文封面类型一键配置终极指南下一篇如何3分钟快速上手SPT-AKI Profile Editor逃离塔科夫离线存档编辑终极指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考