《OpenGL ES 3.x游戏开发(下卷)》一2.6 吹气膨胀特效

  1. 云栖社区>
  2. 博客>
  3. 正文

《OpenGL ES 3.x游戏开发(下卷)》一2.6 吹气膨胀特效

异步社区 2017-05-02 10:53:00 浏览1666
展开阅读全文

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

2.6 吹气膨胀特效

通过前面几节的学习,读者应该对顶点着色器的使用有了一定的了解。本节将进一步给出通过使用顶点着色器实时改变3D模型中顶点的位置,以实现物体吹气膨胀效果的案例。

2.6.1 特效基本原理

介绍本节案例的具体开发之前,首先需要了解本节案例实现吹气膨胀特效的基本原理,如图2-20所示。


2_20

从图2-20中可以看出,实现吹气膨胀特效时,由顶点着色器根据收到的参数将当前处理的顶点位置沿当前顶点的法向量方向移动一定的距离。每次处理时移动距离的大小由传入的参数控制,这样就可以非常方便地实现吹气膨胀的效果了。

2.6.2 特效开发步骤

上一小节介绍了实现物体吹气膨胀特效的基本原理,本小节首先给出一个基于此原理开发的实现人物头部3D模型不断吹气膨胀的案例Sample2_6,其运行效果如图2-21所示。


2_21_1
2_21_2

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

(1)首先介绍用于在程序运行过程中不断修改吹气膨胀程度系数(fatFacror变量)的drawSelf方法,此方法来自于LoadedObjectVertexNormalTexture类。该类在上卷的第9章中介绍过,其对象表示从obj文件中加载的3D模型。本案例用于加载包含了人物头部的3D模型,其中drawSelf方法的具体代码如下。

1    public void drawSelf(int texId){
2         fatFacror+=fatFacrorStep;                        //计算新的膨胀系数
3         if(fatFacror>0.05f||fatFacror<0){                //若膨胀系数达到上限或下限
4                fatFacrorStep=-fatFacrorStep;             //将膨胀系数的符号置反
5         }
6         ……//此处省略了部分代码,与本书前面案例中的类似,有兴趣的读者可以自行查看随书
7         GLES30.glUniform1f(muFatFactor, fatFacror);     //将膨胀系数传入着色器
8         ……//此处省略了部分代码,与本书前面案例中的类似,有兴趣的读者可以自行查看随书
9    }

提示

上述代码的主要功能为不断地修改物体的吹气膨胀系数,并把膨胀系数传入着色器程序。

(2)接着介绍接收吹气膨胀系数,并根据系数将顶点位置沿法向量方向移动一定距离的顶点着色器,其具体代码如下。

1    #version 300 es
2    uniform float uFatFactor;           //接收的吹气膨胀系数
3    uniform mat4 uMVPMatrix;            //总变换矩阵
4    uniform mat4 uMMatrix;              //变换矩阵
5    uniform vec3 uLightLocation;        //光源位置
6    uniform vec3 uCamera;               //摄像机位置
7    in vec3 aPosition;                  //顶点位置
8    in vec3 aNormal;                    //顶点法向量
9    in vec2 aTexCoor;                   //顶点纹理坐标
10    out vec4 ambient;                  //用于传递给片元着色器的环境光最终强度
11    out vec4 diffuse;                  //用于传递给片元着色器的散射光最终强度
12    out vec4 specular;                 //用于传递给片元着色器的镜面光最终强度
13    out vec2 vTextureCoord;            //用于传递给片元着色器的纹理坐标
14    void pointLight(                   //定位光光照计算的方法
15         in vec3 normal,               //法向量
16         inout vec4 ambient,           //环境光最终强度
17         inout vec4 diffuse,           //散射光最终强度
18         inout vec4 specular,          //镜面光最终强度
19         in vec3 lightLocation,        //光源位置
20         in vec4 lightAmbient,         //光源环境光强度
21         in vec4 lightDiffuse,         //光源散射光强度
22         in vec4 lightSpecular         //光源镜面光强度
23    ){
24         ambient=lightAmbient;                        //直接得出环境光的最终强度
25         vec3 normalTarget=aPosition+normal;          //计算变换后的法向量
26         vec3 newNormal=(uMMatrix*vec4(normalTarget,1)).xyz-(uMMatrix*vec4(aPosition,1)).xyz;
27         newNormal=normalize(newNormal);         //对法向量规格化
28         //计算从表面点到摄像机的向量
29         vec3 eye= normalize(uCamera-(uMMatrix*vec4(aPosition,1)).xyz);
30         //计算从表面点到光源位置的向量vp
31         vec3 vp= normalize(lightLocation-(uMMatrix*vec4(aPosition,1)).xyz);
32         vp=normalize(vp);                              //格式化vp
33         vec3 halfVector=normalize(vp+eye);             //求视线与光线的半向量
34         float shininess=50.0;                          //粗糙度,越小越光滑
35         float nDotViewPosition=max(0.0,dot(newNormal,vp)); //求法向量与vp的点积与0的最大值
36         diffuse=lightDiffuse*nDotViewPosition;                //计算散射光的最终强度
37         float nDotViewHalfVector=dot(newNormal,halfVector);   //法线与半向量的点积
38         float powerFactor=max(0.0,pow(nDotViewHalfVector,shininess));//镜面反射光强度因子
39         specular=lightSpecular*powerFactor;               //计算镜面光的最终强度
40    }
41    void main(){
42    //根据总变换矩阵计算此次绘制此顶点的位置,在计算时将顶点位置沿着法向量方向移动一定的距离
43         gl_Position = uMVPMatrix * vec4(aPosition+aNormal*uFatFactor,1);
44         vec4 ambientTemp, diffuseTemp, specularTemp;//环境光、散射光、镜面反射光的临时变量
45         pointLight(normalize(aNormal),ambientTemp,diffuseTemp,specularTemp,uLightLocation,
46         vec4(0.15,0.15,0.15,1.0),vec4(0.9,0.9,0.9,1.0),vec4(0.4,0.4,0.4,1.0));
47         ambient=ambientTemp;                      //将环境光最终强度传给片元着色器
48         diffuse=diffuseTemp;                      //将散射光最终强度传给片元着色器
49         specular=specularTemp;                    //将镜面光最终强度传给片元着色器
50         vTextureCoord = aTexCoor;                 //将接收的纹理坐标传递给片元着色器
51    }

提示

从上述顶点着色器的代码中可以看出,大部分都与上卷案例相同,如计算定位光光照等。最能体现本节案例特点的就是第43行的代码,其在计算顶点经过变换后的最终位置时不是直接针对顶点的坐标计算的。而是首先将顶点坐标沿着顶点的法向量方向移动一定的距离(移动距离的大小由接收的吹气膨胀系数uFatFactor来确定),然后再与变换矩阵相乘。

另外,本案例所采用的思路不但可以用来实现吹气膨胀特效,如果将案例中的obj模型替换为使用面法向量的模型,还可以实现简单的爆炸效果。例如将上述吹气膨胀特效案例中的头部模型替换为一个使用面法向量的地雷模型(替换后的案例为Sample2_7),再运行案例就会出现爆炸的效果,如图2-22所示。


2_22_1
2_22

提示

案例Sample2_7与案例Sample2_6的代码完全一致,只是将案例Sample2_6中采用点平均法向量的头部模型head.obj替换为使用面法向量的地雷模型zd.obj。至于3D模型是采用点平均法向量还是面法向量,则是在3ds Max中完成设置的,读者可自行设置,在此不再赘述。不熟悉的读者可以参考其他介绍3ds Max的书籍或资料,非常方便。

网友评论

登录后评论
0/500
评论
异步社区
+ 关注