《OpenGL ES 3.x游戏开发(下卷)》一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函数完成的,这是为了提高执行效率。实际开发中有些功能既可以采用函数完成也可以自己编程完成,笔者强烈建议直接调用函数完成。这是因为系统的函数在大部分情况下比自己开发的相同功能的代码片段性能优异。

时间: 2025-01-24 17:40:42

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

《OpenGL ES 3.x游戏开发(下卷)》一导读

前 言 OpenGL ES 3.x游戏开发(下卷) 为什么要写这样一套书 随着智能手机硬件性能的不断提升,如"水果忍者""极品飞车""狂野飙车8:极速凌云"等一批优秀的3D游戏娱乐应用在广大智能机用户间流行开来.与此同时,也带动了手机游戏产业逐渐从2D走向3D.但目前国内专门介绍3D游戏开发的书籍与资料都非常少,同时3D应用开发的门槛又比较高,使得很多初学者无从下手.根据这种情况,笔者结合多年从事3D游戏应用开发的经验编写了这样一套书. 了解一些

《OpenGL ES 2.0游戏开发(上卷):基础技术和典型案例》——6.4节点法向量和面法向量

6.4 点法向量和面法向量 OpenGL ES 2.0游戏开发(上卷):基础技术和典型案例 本章前面几节的案例都是基于球面开发的,球面属于连续.平滑的曲面,因此面上的每个顶点都有确定的法向量.但现实世界中的物体表面并不都是连续.平滑的,此时对于面上的某些点的法向量计算就不那么直观了,图6-18说明了这个问题. 从图6-18中可以看出,顶点A位于长方体左.上.前3个面的交界处,此处是不光滑的.这种情况下顶点A的法向量有两种处理策略,具体如下所列. 在顶点A的位置放置3个不同的顶点,每个顶点看作是仅

《OpenGL ES 3.x游戏开发(上卷)》一第1章 Android概述

第1章 Android概述 OpenGL ES 3.x游戏开发(上卷)随着移动互联网时代的到来,智能手机逐渐走进了人们的生活, Google公司于2007年11月5日发布了基于Linux平台的开源手机操作系统--Android.由于Android系统的开源性以及其他各个方面的因素,其受到了广大手机厂商的青睐,因此需要大量的Android开发人员来满足日益增长的海量软件开发需求. 提示 有些读者可能会有一点奇怪,不是介绍OpenGL ES 3.0的知识吗,怎么一开始就介绍Android呢?这是因为

《OpenGL ES 3.x游戏开发(上卷)》一第2章 游戏开发相关的 Android基础知识

第2章 游戏开发相关的 Android基础知识 OpenGL ES 3.x游戏开发(上卷)虽然本书主要是介绍OpenGL ES 3.0 3D应用及游戏开发的,但由于很多3D游戏应用中还需要用到目标平台的一些其他应用开发方面的知识,而本书3D基础知识部分主要是借助于Android平台来介绍OpenGL ES 3.0 3D应用及游戏开发的.故本章将向读者简要介绍一些在Android游戏开发中必备的一些基础知识,如音效.文件的读取.游戏信息的存储.Socket网络.蓝牙网络等. 提示 由于介绍Andr

OpenGL ES 2.0游戏开发(上卷):基础技术和典型案例》一6.3 定位光与定向光

6.3 定位光与定向光 OpenGL ES 2.0游戏开发(上卷):基础技术和典型案例上一节中介绍的光照效果都是基于定位光光源的,定位光光源类似于现实生活中的白炽灯灯泡,其在某个固定的位置,发出的光向四周发散.定位光照射的一个明显特点就是,在给定光源位置的情况下,对不同位置的物体产生的光照效果不同. 现实世界中并不都是定位光,例如照射到地面上的阳光,光线之间是平行的,这种光称为定向光.定向光照射的明显特点是,在给定光线方向的情况下,场景中不同位置的物体反映出的光照效果完全一致.图6-16中对定位

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

1.2 顶点数组对象 使用了顶点缓冲技术后,绘制效率有了较大的提升.但是还有一点不尽如人意,那就是顶点的位置坐标.法向量.纹理坐标等不同方面的数据每次使用时需要单独指定,重复了一些不必要的工作.OpenGL ES 3.0考虑到了这一点,提供了一种专门用于解决此问题的对象--顶点数组对象(VAO).本节将介绍顶点数组对象. 1.2.1 基本知识与案例效果 顶点数组对象的主要功能就是将绘制一个物体时所需的对应于不同方面(如顶点坐标.法向量.纹理坐标)的顶点缓冲及相关设置包装成一个整体,绘制时直接使用

《OpenGL ES 3.x游戏开发(下卷)》一2.7 固定渲染管线与可编程渲染管线实现方案的对比

2.7 固定渲染管线与可编程渲染管线实现方案的对比 本章最开始提到过,在固定渲染管线平台上想高效地实现本章案例中的特效是非常困难的.这是因为在固定渲染管线中,顶点数据一旦送入渲染管线后就不可能对其方便地自定义处理了.因此,在固定渲染管线上想实现本章案例中的特效只能采用以下两种策略之一. 提示 回顾一下,OpenGL ES 1.x(含1.0和1.1)采用的是固定渲染管线,从OpenGL ES 2.0开始采用可编程渲染管线. 1.初始化时预先计算数据此种策略的基本思想非常简单,就是在初始化时将动画中

《OpenGL ES 3.x游戏开发(下卷)》一1.1 顶点缓冲区

1.1 顶点缓冲区 将绘制物体的顶点数据保存在内存中,在调用glDrawArrays或者glDrawElements等绘制方法前需要调用相应的方法将数据送入显存,I/O开销大,性能不够好. 若采用顶点缓冲区对象存放顶点数据,则不需要在每次绘制前都将顶点数据复制进显存,而是在初始化顶点缓冲区对象时一次性将顶点数据送入显存,每次绘制时直接使用显存中的数据,可以大大提高渲染性能. 1.1.1 基本知识 OpenGL ES 3.0中支持两种类型的顶点缓冲区对象,分别为数组缓冲区对象(Array Buff

《OpenGL ES 3.x游戏开发(下卷)》一1.5 其他缓冲区对象操作

1.5 其他缓冲区对象操作 前面已经介绍了顶点缓冲区对象.顶点数组对象.一致缓冲区对象以及映射缓冲区对象,本节将介绍两个其他的缓冲区对象操作,包括复制缓冲区对象(Copying Buffer Objects)以及从颜色缓冲区复制纹理数据(Copying Texture Data from the Color Buffer). 1.5.1 复制缓冲区对象 到目前为止,已经介绍了如何使用glBufferData.glBufferSubData以及 glMapBufferRange方法将数据加载到缓冲