本文共 4600 字,大约阅读时间需要 15 分钟。
本节书摘来异步社区《OpenGL ES 3.x游戏开发(下卷)》一书中的第2章,第2.6节,作者: 吴亚峰 责编: 张涛,更多章节内容可以访问云栖社区“异步社区”公众号查看。
通过前面几节的学习,读者应该对顶点着色器的使用有了一定的了解。本节将进一步给出通过使用顶点着色器实时改变3D模型中顶点的位置,以实现物体吹气膨胀效果的案例。
介绍本节案例的具体开发之前,首先需要了解本节案例实现吹气膨胀特效的基本原理,如图2-20所示。
从图2-20中可以看出,实现吹气膨胀特效时,由顶点着色器根据收到的参数将当前处理的顶点位置沿当前顶点的法向量方向移动一定的距离。每次处理时移动距离的大小由传入的参数控制,这样就可以非常方便地实现吹气膨胀的效果了。
上一小节介绍了实现物体吹气膨胀特效的基本原理,本小节首先给出一个基于此原理开发的实现人物头部3D模型不断吹气膨胀的案例Sample2_6,其运行效果如图2-21所示。
了解了本案例的运行效果后,接下来简单介绍本案例的具体开发过程。由于本案例中的大部分代码与本书前面的很多案例非常类似,因此这里仅给出本案例中有代表性的部分,具体内容如下。
(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 es2 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 //计算从表面点到光源位置的向量vp31 vec3 vp= normalize(lightLocation-(uMMatrix*vec4(aPosition,1)).xyz);32 vp=normalize(vp); //格式化vp33 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所示。
提示
案例Sample2_7与案例Sample2_6的代码完全一致,只是将案例Sample2_6中采用点平均法向量的头部模型head.obj替换为使用面法向量的地雷模型zd.obj。至于3D模型是采用点平均法向量还是面法向量,则是在3ds Max中完成设置的,读者可自行设置,在此不再赘述。不熟悉的读者可以参考其他介绍3ds Max的书籍或资料,非常方便。
转载地址:http://bxjta.baihongyu.com/