本节书摘来异步社区《OpenGL ES 3.x游戏开发(下卷)》一书中的第2章,第2.4节,作者: 吴亚峰 责编: 张涛,更多章节内容可以访问云栖社区“异步社区”公众号查看。

2.4 展翅飞翔的雄鹰

前面3节分别介绍了3个不同软体的例子,虽然采用的数学模型各有不同,但都是通过编程直接实现特定的数学模型以实现软体动画的。这在一般情况下足够用了,但如果想呈现非常复杂的软体动画就很困难了。

有些复杂软体的动画虽然也可以采用数学模型编程实现,但对应的数学模型非常复杂,编程成本很高。本节将给出一种非常简便的实现软体动画的策略——关键帧动画,通过它可以方便地实现游戏中雄鹰飞过蓝天、英雄举刀杀敌的动画。

2.4.1 基本原理

关键帧动画的基本思想非常简单,就是给顶点着色器提供动画中每个关键帧对应的各个顶点的位置数据以及融合比例。顶点着色器根据两套位置数据及当前融合的比例融合出一套结果顶点位置数据。只要在绘制每一帧时提供不同的混合比例即可产生想要的动画。

如本节将要给出的展翅飞翔的雄鹰动画中就用到了3个关键帧,包含4个动画阶段。

  • 第一阶段是对1、2号关键帧中的顶点数据进行融合,即从1号关键帧到2号关键帧。
  • 第二阶段是对2、3号关键帧中的顶点数据进行融合,即从2号关键帧到3号关键帧。
  • 第三阶段是对3、2号关键帧中的顶点数据进行融合,即从3号关键帧到2号关键帧。
  • 第四阶段是对2、1号关键帧中的顶点数据进行融合,即从2号关键帧到1号关键帧。

上述4个阶段不断重复就可以呈现出雄鹰展翅飞翔的动画,每个关键帧的具体顶点位置情况如图2-12所示。

 说明

从图2-12中可以看出,最左侧是雄鹰翅膀上扬到最高位置的情况,中间是雄鹰翅膀放平的情况,右侧是雄鹰翅膀下垂到最低位置的情况。

到这里读者可能会产生疑问:为什么一定要3个关键帧呢?仅保留1、3号关键帧不也能融合出动画吗?确实如此,只保留1、3两个关键帧是可以的,但动画的真实感就会大打折扣。因为仅通过1、3关键帧融合出来的翅膀展平的情况翅膀就会缩短,如图2-13所示。

从图2-13中可以看出使用关键帧动画的一个要领,那就是不重要的中间帧可以通过按比例融合两个关键帧得到,真实感基本不受影响。但关键帧不应该省略而通过其他关键帧融合得到,否则动画的真实感就会变差。

 提示

使用基于顶点位置的融合的关键帧动画时有一点需要特别注意,那就是所有关键帧中顶点的数量需要一致,并能够形成一一对应的关系。

2.4.2 开发步骤

上一小节介绍了关键帧动画的基本原理以及注意事项,本小节将给出一个关键帧动画的案例Sample2_4,其运行效果如图2-14所示。

说明

图2-14给出了雄鹰展翅飞翔动画中的3帧画面,效果非常真实。由于本书插图是灰度印刷,效果可能不是很好,请读者自行用真机运行本案例。案例运行时可以用手指在屏幕上滑动以旋转雄鹰从不同的角度进行观察。

了解了案例的运行效果后,接下来简要介绍本案例的具体开发过程。由于本案例中的大部分类和前面章节很多案例中的类非常相似,因此这里只给出本案例中比较有代表性的部分,具体内容如下。

(1)本案例中用到的雄鹰的3个关键帧采用3ds Max设计并导出成obj文件,因此首先需要将3个关键帧的顶点数据加载进应用程序并存放到缓冲中,相关代码如下。

1    public void initVertexData(MySurfaceView mv) {    //初始化顶点数据的方法
2         //加载雄鹰模型
3         glede_one=LoadUtil.loadFromFileVertexOnly("laoying01.obj",mv);
4         glede_two=LoadUtil.loadFromFileVertexOnly("laoying02.obj",mv)[0];
5         glede_three=LoadUtil.loadFromFileVertexOnly("laoying03.obj",mv)[0];
6         //创建第一个顶点坐标数据缓冲
7         vCount=glede_one.length/3;
8         ByteBuffer vbb = ByteBuffer.allocateDirect(glede_one[0].length*4);
9         vbb.order(ByteOrder.nativeOrder());          //设置字节顺序
10        mVertexBuffer1 = vbb.asFloatBuffer();        //转换为Float型缓冲
11        mVertexBuffer1.put(glede_one[0]);            //向缓冲区中放入顶点坐标数据
12        mVertexBuffer1.position(0);                  //设置缓冲区起始位置
13        //创建第二个顶点坐标数据缓冲
14        vCount=glede_two.length/3;
15        vbb = ByteBuffer.allocateDirect(glede_two.length*4);
16        vbb.order(ByteOrder.nativeOrder());           //设置字节顺序
17        mVertexBuffer2 = vbb.asFloatBuffer();         //转换为Float型缓冲
18        mVertexBuffer2.put(glede_two);                //向缓冲区中放入顶点坐标数据
19        mVertexBuffer2.position(0);                   //设置缓冲区起始位置
20        //创建第三个顶点坐标数据缓冲
21        vCount=glede_three.length/3;
22        vbb = ByteBuffer.allocateDirect(glede_three.length*4);
23        vbb.order(ByteOrder.nativeOrder());            //设置字节顺序
24        mVertexBuffer3 = vbb.asFloatBuffer();          //转换为Float型缓冲
25        mVertexBuffer3.put(glede_three);               //向缓冲区中放入顶点坐标数据
26        mVertexBuffer3.position(0);                    //设置缓冲区起始位置
27        //创建纹理坐标数据缓冲
28        ByteBuffer tbb = ByteBuffer.allocateDirect(glede_one[1].length*4);
29        tbb.order(ByteOrder.nativeOrder());          //设置字节顺序
30        mTexCoorBuffer = tbb.asFloatBuffer();        //转换为Float型缓冲
31        mTexCoorBuffer.put(glede_one[1]);            //向缓冲区中放入顶点纹理坐标数据
32        mTexCoorBuffer.position(0);                  //设置缓冲区起始位置
33    }

说明

上述代码主要是加载obj文件中的顶点位置与纹理坐标数据。由于3个关键帧中各个对应顶点的纹理坐标是相同的,因此纹理坐标仅保留了一套。但各个关键帧中对应顶点的位置是不同的,因此顶点数据有3套。

(2)为了在顶点着色器中能够根据比例融合关键帧中的顶点数据,需要将融合的比例传入渲染管线。由于有3个关键帧,因此融合比例的取值在0~2连续变化。由于将融合比例送入渲染管线的代码非常简单,这里就不再赘述,需要的读者请自行参考中的源代码。

(3)接着需要介绍执行顶点融合以产生关键帧动画的顶点着色器,其代码如下。

1    #version 300 es
2    uniform mat4 uMVPMatrix;                        //总变换矩阵
3    in vec3 aPosition;                              //顶点位置(来自1号关键帧)
4    in vec3 bPosition;                              //顶点位置(来自2号关键帧)
5    in vec3 cPosition;                             //顶点位置(来自3号关键帧)
6    in vec2 aTexCoor;                              //顶点纹理坐标
7    uniform float uBfb;                            //融合比例
8    out vec2 vTextureCoord;                        //用于传递给片元着色器的纹理坐标
9    void main(){
10        vec3 tv;                                  //融合后的结果顶点
11        if(uBfb<=1.0){        //若融合比例小于等于1,则需要执行的是1、2号关键帧的融合
12              tv=mix(aPosition,bPosition,uBfb);
13        }else{                //若融合比例大于1,则需要执行的是2、3号关键帧的融合
14              tv=mix(bPosition,cPosition,uBfb-1.0);
15        }
16        gl_Position = uMVPMatrix * vec4(tv,1);;//根据总变换矩阵计算此次绘制此顶点的位置
17        vTextureCoord = aTexCoor;              //将接收的纹理坐标传递给片元着色器
18    }

说明

上述顶点着色器是实现关键帧动画的核心,其根据传入的融合比例选择对应的两个关键帧进行融合。需要注意的是,融合时是调用mix函数完成的,这是为了提高执行效率。实际开发中有些功能既可以采用函数完成也可以自己编程完成,笔者强烈建议直接调用函数完成。这是因为系统的函数在大部分情况下比自己开发的相同功能的代码片段性能优异。

《OpenGL ES 3.x游戏开发(下卷)》一2.4 展翅飞翔的雄鹰相关推荐

  1. 《OpenGL ES 3.x游戏开发(下卷)》一1.2 顶点数组对象

    本节书摘来异步社区<OpenGL ES 3.x游戏开发(下卷)>一书中的第1章,第1.2节,作者: 吴亚峰 责编: 张涛,更多章节内容可以访问云栖社区"异步社区"公众号 ...

  2. 《OpenGL ES 2.0游戏开发(上卷):基础技术和典型案例》一6.6 本章小结

    本节书摘来异步社区<OpenGL ES 2.0游戏开发(上卷):基础技术和典型案例>一书中的第6章,第6.6节,作者: 吴亚峰 责编: 张涛,更多章节内容可以访问云栖社区"异步社 ...

  3. 《OpenGL ES 3.x游戏开发(上卷)》一1.5 Android应用程序运行的机制

    本节书摘来异步社区<OpenGL ES 3.x游戏开发(上卷)>一书中的第1章,第1.5节,作者: 吴亚峰 责编: 张涛,更多章节内容可以访问云栖社区"异步社区"公众号 ...

  4. 《OpenGL ES 2.0游戏开发(上卷):基础技术和典型案例》——6.5节光照的每顶点计算与每片元计算...

    本节书摘来自异步社区<OpenGL ES 2.0游戏开发(上卷):基础技术和典型案例>一书中的第6章,第6.5节光照的每顶点计算与每片元计算,作者 吴亚峰,更多章节内容可以访问云栖社区&q ...

  5. 《OpenGL ES 2.0游戏开发(上卷):基础技术和典型案例》一第6章 让场景更逼真——光照效果...

    本节书摘来异步社区<OpenGL ES 2.0游戏开发(上卷):基础技术和典型案例>一书中的第6章,第6.1节,作者: 吴亚峰 责编: 张涛,更多章节内容可以访问云栖社区"异步社 ...

  6. 《OpenGL ES 3.x游戏开发(上卷)》一2.4 文件I/O

    本节书摘来异步社区<OpenGL ES 3.x游戏开发(上卷)>一书中的第2章,第2.4节,作者: 吴亚峰 责编: 张涛,更多章节内容可以访问云栖社区"异步社区"公众号 ...

  7. 使用opengl es编写2d游戏的一些说明和技巧

    即使是制作2d游戏,在移动设备上也往往使用opengl es绘图接口,来达到较高的绘制效率.这里记录一下我学习opengl es 2d绘图过程中逐渐明白的一些原理与技巧. opengl 3d贴图的基本 ...

  8. android opengl版本,Android OpenGL ES(一)开发入门

    早就听过大名鼎鼎的 OpenGL,却迟迟没有实践学习,有些惭愧.今天开始通过实践+博文方式学习掌握 OpenGL.此文对于 OpenGL 的学习分为以下部分: OpenGL 基础概念 OpenGL 坐 ...

  9. 一文了解 OpenGL ES

    OpenGL ES(OpenGL for Embedded Systems) 是一种免费的跨平台3D图形 API接口,其适用于低功耗设备,可用于嵌入式设备和移动设备(包括手机.车载大屏 和嵌入式电器终 ...

最新文章

  1. 核弹级漏洞log4shell席卷全球!危及苹果腾讯百度网易,修改iPhone名称就可触发...
  2. 简单实现支付密码输入框 By HL
  3. 大白话解析模拟退火算法、遗传算法入门
  4. 【C++深度剖析教程35】函数模板的概念和意义
  5. Elasticsearch--分词-自定义扩展词库---全文检索引擎ElasticSearch工作笔记022
  6. java死锁怎么用jvm调试_性能测试之JVM的故障排查-死锁
  7. 信号检测与估计理论 pdf_CVPR2020|行人检测与重识算法推荐论文源码大盘点
  8. sh-3.2非正常修正
  9. html+加粗+w3c,HTML5教程:html标签属性通过w3c验证
  10. 组合逻辑电路的分析与设计
  11. VS2012 产品密钥
  12. 【引用】教你会看电脑的配置
  13. OPENCV(七)对啤酒盖进行缺陷检测
  14. The Framework For Love
  15. 架构--网络关键指标公式
  16. 已经开源的阿里云播放器的播放内核
  17. win10如何打开摄像头_win10录屏软件哪个好?可录摄像头不限时长的视频录制方法...
  18. ACPC2015 K 树的直径
  19. 第14章 项目采购管理
  20. 学生托管班_小学生上托管班弊大于利

热门文章

  1. 处理告警“ warning #69-D integer conversion resulted in truncation”的方法
  2. 关闭微信朋友圈和公众号的广告
  3. 鲁棒局部均值分解 (RLMD)附Matlab代码
  4. 如何给Mac OS 更新到指定版本系统
  5. sbus storm32bgc_STorM32 BGC三轴增稳云台固件更新
  6. Par.ici法语听写练习A1 -L13
  7. python 根据word生成ppt_未明学院:利用Python将Wordamp;PPT批量转成PDF
  8. 从市场观察到案例解析,来看TopOn与JoyPac的2020年手游出海运营全攻略
  9. 香港十大黄金投资公司排名榜单(2022最新版)
  10. php-resque消息队列