ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Android 官方培训课程中文版:使用 OpenGL ES 2.0 为图形添加移动与旋转动画

Android 官方培训课程中文版:使用 OpenGL ES 2.0 为图形添加移动与旋转动画 文档教程移动开发【免费下载链接】android-training-course-in-chineseAndroid官方培训课程中文版项目地址https://gitcode.com/gh_mirrors/an/android-training-course-in-chinese点击查看免费下载本篇文章以 graphics/opengl/motion.md 为核心骨架编写是「使用 OpenGL ES 显示图像」系列课程的第五课。在 Android 开发中当应用需要通过 GPU 高效绘制 3D 图形、并让图形产生连续运动如旋转动画时OpenGL ES 是官方推荐的技术方案。读完本文你将掌握如何用旋转矩阵让绘制对象围绕指定轴旋转、如何将旋转矩阵与投影矩阵、相机视角矩阵正确相乘以及如何通过渲染模式配置让动画持续刷新。在 Android 的 OpenGL 应用开发中在屏幕上绘制图形只是最基本的能力——通过 Canvas 与Drawable对象同样可以完成二维绘图。OpenGL ES 的真正价值在于它能在三维空间中对绘制对象执行移动Translate、旋转Rotate、缩放Scale等矩阵变换从而创建出真正引人入胜的动态用户体验。这一系列能力都以矩阵运算为核心因此本课程motion.md在定义形状 → 绘制形状 → 运用投影与相机视角的基础上进一步讲解如何为图形添加旋转动画。本文承接该系列课程的前四课——定义形状shapes.md、绘制形状draw.md、运用投影与相机视角projection.md并参考环境配置environment.md与响应触摸事件touch.md中的相关代码完整还原旋转动画的实现过程并深入剖析底层矩阵运算与渲染模式机制。旋转动画的整体思路在 OpenGL ES 2.0 中物体的最终屏幕位置由顶点着色器Vertex Shader中的矩阵乘法决定。回顾运用投影与相机视角projection.md课程我们已经把两个变换叠加到了顶点上投影变换Projection基于 GLSurfaceView 的宽高比调整对象坐标避免图形发生形变通常在onSurfaceChanged()中用Matrix.frustumM()计算相机视角变换Camera View模拟虚拟相机位置用Matrix.setLookAtM()计算与投影矩阵相乘得到mMVPMatrixModel View Projection Matrix。当时的顶点着色器代码见 projection.md已经预留了矩阵接口public class Triangle { private final String vertexShaderCode // This matrix member variable provides a hook to manipulate // the coordinates of the objects that use this vertex shader uniform mat4 uMVPMatrix; attribute vec4 vPosition; void main() { // the matrix must be included as a modifier of gl_Position // Note that the uMVPMatrix factor *must be first* in order // for the matrix multiplication product to be correct. gl_Position uMVPMatrix * vPosition; }; // Use to access and set the view transformation private int mMVPMatrixHandle; ... }旋转动画的本质就是在uMVPMatrix之外再乘上一个旋转矩阵Rotation Matrix让每个顶点的坐标在每个渲染帧中围绕某个轴转动一定角度。由于每一帧的角度都在变化图形便呈现出连续的旋转效果。旋转一个形状三步矩阵运算在渲染器的onDrawFrame()方法中使用 OpenGL ES 2.0 旋转一个已绘制的图形非常简单核心代码如下摘自 motion.mdprivate float[] mRotationMatrix new float[16]; public void onDrawFrame(GL10 gl) { float[] scratch new float[16]; ... // Create a rotation transformation for the triangle long time SystemClock.uptimeMillis() % 4000L; float angle 0.090f * ((int) time); Matrix.setRotateM(mRotationMatrix, 0, angle, 0, 0, -1.0f); // Combine the rotation matrix with the projection and camera view // Note that the mMVPMatrix factor *must be first* in order // for the matrix multiplication product to be correct. Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0); // Draw triangle mTriangle.draw(scratch); }这段代码可以拆解为三个步骤准备旋转矩阵容器private float[] mRotationMatrix new float[16];。OpenGL ES 的变换矩阵是 4×4 的列主序矩阵因此用长度为 16 的float[]数组存储。计算随时间变化的角度通过SystemClock.uptimeMillis() % 4000L取得一个以 4000 毫秒4 秒为周期的系统运行时间再乘以0.090f换算成角度。4 秒内角度从 0° 匀速增长到约 360°即每 4 秒完成一整圈旋转。构造旋转矩阵并合成Matrix.setRotateM(mRotationMatrix, 0, angle, 0, 0, -1.0f)以角度angle、旋转轴向量(0, 0, -1.0f)即绕 Z 轴负方向填充旋转矩阵。setRotateM的参数依次为目标矩阵、偏移量offset、旋转角度度、旋转轴的 X/Y/Z 分量Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0)把之前算好的mMVPMatrix投影 × 相机视角与旋转矩阵相乘结果写入临时矩阵scratchmTriangle.draw(scratch)把合成后的变换矩阵传给draw()方法。矩阵乘法顺序为何不能颠倒代码注释反复强调the mMVPMatrix factor must be first——即mMVPMatrix必须作为乘法运算的左操作数。这是因为矩阵乘法不满足交换律变换的施加顺序决定了最终效果先对顶点施加旋转最靠近顶点再依次叠加相机视角变换和投影变换。若把顺序颠倒为multiplyMM(scratch, 0, mRotationMatrix, 0, mMVPMatrix, 0)旋转会被施加在已经完成投影的空间中导致图形绕屏幕坐标而非物体自身的局部坐标旋转产生错误的视觉效果。draw() 方法如何接收矩阵与 projection.md 中一致图形的draw()方法需要接收变换矩阵并通过 uniform 变量传入着色器public void draw(float[] mvpMatrix) { // pass in the calculated transformation matrix ... // get handle to shapes transformation matrix mMVPMatrixHandle GLES20.glGetUniformLocation(mProgram, uMVPMatrix); // Pass the projection and view transformation to the shader GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0); // Draw the triangle GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount); // Disable vertex array GLES20.glDisableVertexAttribArray(mPositionHandle); }其中glUniformMatrix4fv的参数含义为uniform 变量的 location、要传入的矩阵数量1 个、是否转置falseOpenGL ES 要求列主序矩阵不能转置、矩阵数据及偏移量。glDrawArrays则以GL_TRIANGLES模式从顶点 0 开始绘制vertexCount个顶点。在应用投影与相机变换后、加入旋转之前的图形效果可以参考系列课程中的示例图 ogl-triangle-projected.png应用了投影变换和相机视图变换的三角形——旋转动画就是在这样正确比例的图形上叠加的动态效果。启用连续渲染RENDERMODE_WHEN_DIRTY 的取舍完成了上述代码后如果你发现三角形依然纹丝不动最可能的原因就是渲染模式配置。这是本课程的一个关键易错点。默认模式与按需渲染模式GLSurfaceView 有两种渲染模式模式常量行为连续渲染GLSurfaceView.RENDERMODE_CONTINUOUSLY默认模式只要 Surface 有效就持续不断地重绘适合动画、游戏等场景按需渲染GLSurfaceView.RENDERMODE_WHEN_DIRTY仅在绘制数据发生变化、并显式调用requestRender()时才重绘省电省资源正如 motion.md 所指出的如果保留了RENDERMODE_WHEN_DIRTY配置OpenGL 只会对形状执行一次旋转然后就停留在那里等待 GLSurfaceView 容器调用 requestRender() 方法才继续渲染——因此自动动画永远不会推进。如何配置在 environment.md 中MyGLSurfaceView的构造函数展示了两种模式的设置方式。要让三角形自动旋转必须注释掉按需渲染的那行代码回归默认的连续渲染模式public MyGLSurfaceView(Context context) { ... // Render the view only when there is a change in the drawing data. // To allow the triangle to rotate automatically, this line is commented out: //setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); }这里的关键判断标准是除非某个对象的变化与用户交互无关、且内容持续运动否则才建议关闭按需渲染。换句话说面向用户交互、内容只在有变化时才需要绘制的场景例如触摸旋转应当保留RENDERMODE_WHEN_DIRTY并通过requestRender()主动请求绘制这样能显著降低功耗面向自动播放动画、游戏等需要每帧更新的场景则必须关闭该选项让渲染器以连续模式工作。requestRender() 与按需渲染的配合在系列课程的最后一课响应触摸事件touch.md中可以看到requestRender()与按需渲染的典型配合用法当用户在 onTouchEvent() 中检测到MotionEvent.ACTION_MOVE并计算出新的旋转角度后立即调用requestRender()触发一次重绘mRenderer.setAngle( mRenderer.getAngle() ((dx dy) * TOUCH_SCALE_FACTOR)); requestRender();同时保持构造函数中的按需渲染配置不被注释public MyGLSurfaceView(Context context) { ... // Render the view only when there is a change in the drawing data setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); }这种按需渲染 requestRender()的组合在触摸交互场景中是最有效率的图形仅在旋转角度发生变化时才重绘与本文自动旋转需关闭该模式的结论形成互补——同一个开关面向两种不同的运动驱动方式。从自动旋转到交互旋转的扩展理解setRotateMmultiplyMM的流程后把自动动画改造成用户交互只需做两处替换详见 touch.md用公开的角度变量替换时间计算在渲染器中添加public volatile float mAngle;由于渲染器运行在独立的渲染线程中、与 UI 线程通信必须声明为volatile保证可见性并提供getAngle()/setAngle()方法在onDrawFrame()中改用mAngle注释掉基于时间的角度计算直接用触控输入累积的角度public void onDrawFrame(GL10 gl) { ... float[] scratch new float[16]; // Create a rotation for the triangle // long time SystemClock.uptimeMillis() % 4000L; // float angle 0.090f * ((int) time); Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f); // Combine the rotation matrix with the projection and camera view // Note that the mMVPMatrix factor *must be first* in order // for the matrix multiplication product to be correct. Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0); // Draw triangle mTriangle.draw(scratch); }可以看到矩阵相乘的合成代码完全复用只有角度从何而来发生了变化——这正是本课程沉淀的核心能力一旦掌握了变换矩阵的组合方式就可以在同一个渲染管线中自由切换时间驱动、触摸驱动或其他来源的旋转角度。本课程在整个 OpenGL ES 系列中的位置本课程是使用 OpenGL ES 显示图像graphics/opengl/index.md系列课程的第五课完整的知识链路如下配置 OpenGL ES 环境environment.md声明glEsVersion、构建GLSurfaceView与Renderer定义形状shapes.md用顶点数组与ByteBuffer定义三角形、矩形理解逆时针卷绕Winding绘制形状draw.md编译着色器、链接 Program、执行glDrawArrays运用投影与相机视角projection.mdfrustumM()投影 setLookAtM()相机视角合成mMVPMatrix添加移动motion.md本课setRotateM()旋转矩阵与渲染模式配置让图形动起来响应触摸事件touch.mdonTouchEvent()requestRender()实现用户交互旋转。此外整个系列还强调一个重要前提不要把 OpenGL ES 1.x 的接口与 OpenGL ES 2.0 的接口混用见 index.md这两种接口不通用同时注意onDrawFrame()等回调签名中保留的GL10参数仅是 2.0 接口对旧签名Method Signature的复用并不意味着调用的是 1.x 的 API见 environment.md。小结本文以 motion.md 为骨架完整讲解了在 Android 的 OpenGL ES 2.0 应用中为图形添加旋转动画的完整方案矩阵合成用SystemClock.uptimeMillis()生成随时间变化的旋转角度通过Matrix.setRotateM()构造旋转矩阵再与投影、相机视角合成矩阵mMVPMatrix做Matrix.multiplyMM()乘法——务必保持mMVPMatrix为左操作数渲染模式自动旋转动画必须注释掉setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY)恢复默认的连续渲染而基于用户交互的旋转如 touch.md 中的触摸旋转则应保留按需渲染并用requestRender()驱动重绘通用性这套矩阵变换 渲染驱动的机制可以平滑地从时间驱动的自动动画切换到触摸驱动的交互动画也是后续更复杂 3D 变换平移、缩放、复合变换的基础。开发者可以沿 graphics/opengl/index.md 中的课程顺序继续阅读 touch.md 学习触摸交互或对照 projection.md 复习矩阵合成的前置知识从而构建完整的 OpenGL ES 图形应用能力。赞分享文档教程移动开发【免费下载链接】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官方培训课程中文版为应用添加过渡动画的完整实战指南Android官方培训课程中文版为应用添加过渡动画的完整实战指南 本文章节内容继承自本仓库 动画课程入口页 https://link.gitcode.com/文档教程移动开发Android 官方培训课程用 GLSurfaceView 与 Renderer 搭建 OpenGL ES 2.0 渲染环境android-training-course-in-chineseAndroid 官方培训课程用 GLSurfaceView 与 Renderer 搭建 OpenGL ES 2.0 渲染环境android training文档教程移动开发创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表