《Android 应用案例开发大全(第3版)》——第2章,第2.8节壁纸中的着色器开发

2.8 壁纸中的着色器开发
前面已经对3D动态壁纸——百纳水族馆的相关类进行了简要的介绍。本节将对本案例中用到的相关着色器进行介绍。本案例中用到的着色器共有四对,即气泡着色器、背景着色器、鱼类着色器及珍珠贝着色器。下面就对本壁纸中用到的着色器的开发进行一一介绍。

2.8.1 气泡的着色器
气泡着色器分为顶点着色器与片元着色器,下面便分别对气泡着色器的顶点着色器和片元着色器的开发进行详细介绍。

(1)首先介绍的是气泡着色器中的顶点着色器的开发,其详细代码如下。

代码位置:见随书光盘源代码/第2章/MyWallPaper/assets/shader/目录下的bubble_vertex.sh。

1 uniform mat4 uMVPMatrix;       //总变换矩阵
2 attribute vec3 aPosition;       //顶点位置
3 attribute vec2 aTexCoor;         //顶点纹理坐标
4 varying vec2 vTextureCoord;       //用于传递给片元着色器的变量
5 void main(){
6    gl_Position = uMVPMatrix * vec4(aPosition,1);//根据总变换矩阵计算此次绘制的顶点位置
7    vTextureCoord = aTexCoor;     //将接收的纹理坐标传递给片元着色器
8 }

第1~4行是着色器中接收数据传递数据的声明。接收Java代码部分的总变换矩阵、顶点位置及顶点纹理坐标。并将顶点纹理坐标从顶点着色器传递到片元着色器中。
第5~8行该顶点着色器的主要作用就是根据Java传递过来的模型本身的顶点位置aPosition与总变换矩阵计算出gl_Position,每顶点执行一次。
(2)完成顶点着色器的开发后,下面开发的是气泡的片元着色器,其详细代码如下。

代码位置:见随书光盘源代码/第2章/MyWallPaper/assets/shader/目录下的bubble_frag.sh。

1 precision mediump float;
2 uniform sampler2D sTexture;      //纹理内容数据
3 varying vec2 vTextureCoord;       //接收从顶点着色器过来的参数
4 void main(){
5  vec4 finalColor=texture2D(sTexture, vTextureCoord); //将计算出的颜色给此片元
6  gl_FragColor = finalColor;      //给此片元颜色值
7 }

第1~7行该片元着色器的作用主要为根据从顶点着色器传递过来的纹理坐标数据vTextureCoord和从Java代码部分传递过来的sTexture计算片元的最终颜色值,并将最终颜色值赋值给着色器内建输出变量gl_FragColor,每片元执行一次。
说明
因为背景的着色器代码与上述气泡着色器的代码基本一致,故在此不再详细介绍背景的着色器。读者可自行查看随书光盘中的源代码,其位置在项目目录assets/shader/目录下的back_vertex.sh与back_frag.sh。
2.8.2 珍珠贝的着色器
前面已经为读者介绍了珍珠贝模型的加载方法,但仅是一个带骨骼动画的珍珠贝,并不能使用户感觉真实,因为现实世界中是有阳光的,所以,我们用着色器给珍珠贝增加了灯光,这样就会出现水族馆中真实感超强的珍珠贝。下面对珍珠贝的着色器进行详细的介绍,其具体代码如下。

(1)首先介绍的是珍珠贝着色器中的顶点着色器的开发,具体代码如下。

代码位置:见随书光盘源代码/第2章/MyWallPaper/assets/shader/目录下的vertex.sh。

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

第1~11行是顶点着色器中全局变量的声明,相比于气泡的顶点着色器,它主要增加了变化矩阵、光源位置、摄像机位置以及顶点法向量的引用。此处还声明了传递给片元着色器3种通道光的变量,分别是环境光变量、散射光变量、镜面反射光变量。
第13~38行是计算光照的3种光的最终强度。首先直接计算出环境光最终强度,其中最重要的是在进行计算前要对顶点法向量进行变换,将法向量变换到当前的姿态下。然后计算各种所需数据,最后计算出镜面光和镜面反射光的最终强度。
第40~48行是顶点着色器的main方法,其中首先调用了pointLight方法将3种通道光的强度值传递给片元着色器,并将接收到的顶点纹理坐标传递给片元着色器,以供片元着色器计算每片片元的最后颜色值。
(2)之前介绍了珍珠贝顶点着色器的开发,下面为读者详细介绍珍珠贝着色器中片元着色器的开发。其详细代码如下。

代码位置:见随书光盘源代码/第2章/MyWallPaper/assets/shader/目录下的frag.sh。

1 precision mediump float;
2 varying vec2 vTextureCoord;      //接收从顶点着色器过来的参数
3 uniform sampler2D sTexture;      //纹理内容数据
4 varying vec4 ambient;       //环境光
5 varying vec4 diffuse;       //散射光
6 varying vec4 specular;       //镜面光
7 void main() {
8     vec4 finalColorDay;         //给此片元从纹理中采样出颜色值
9     finalColorDay= texture2D(sTexture, vTextureCoord);//给此片元从纹理中采样出颜色值
10  //综合3个通道光的最终强度及片元的颜色计算出最终片元的颜色并传递给管线
11     gl_FragColor=finalColorDay*ambient+finalColorDay*specular+finalColorDay*
     diffuse;
12 }

第1~12行是珍珠贝着色器中片元着色器的代码。它根据从顶点着色器传递过来的顶点纹理坐标,对Java代码部分传递过来的片元颜色值进行采样,并且接收顶点着色器传递过来的3个通道光值,计算出片元的最终颜色值,然后再将其传递给渲染管线。

2.8.3 鱼类的着色器
前面已为读者介绍了鱼类模型加载。单纯的一个鱼类的骨骼动画并不能使水族馆看起来真实,所以前面已为鱼类着色器传递了一张明暗纹理图为此做准备。在鱼类着色器中,我们为鱼类添加了灯光,并为鱼类本身采取了多重纹理采样绘制。

(1)首先介绍的是鱼类的顶点着色器,由于本着色器中对鱼类灯光的设置与上节中对珍珠贝的灯光设置一致,故不再赘述,请读者自行查看随书光盘中的源代码。本小节将着重介绍对多重纹理采样绘制的实现。其具体代码如下。

代码位置:见随书光盘源代码/第2章/MyWallPaper/assets/shader/目录下的fish_vertex.sh。

1 uniform mat4 uMVPMatrix;        //总变换矩阵
2 uniform mat4 uMMatrix;        //变换矩阵
3 uniform vec3 uLightLocation;      //光源位置
4 uniform vec3 uCamera;       //摄像机位置
5 attribute vec3 aPosition;        //顶点位置
6 attribute vec3 aNormal;          //顶点法向量
7 attribute vec2 aTexCoor;          //顶点纹理坐标
8 varying vec3 vNormal;       //将顶点法向量传给片元着色器
9 varying vec4 ambient;       //将环境光传给片元着色器
10 varying vec4 diffuse;        //将散射光传给片元着色器
11 varying vec4 specular;        //将镜面反射光传给片元着色器
12 varying vec2 vTextureCoord;        //用于传递给片元着色器的变量
13 varying vec3 vPosition;        //将顶点传给片元着色器
14 ......//该处省略了计算定向光照的方法pointLight,读者可自行查阅随书光盘中的源代码
15 void main(){
16  gl_Position = uMVPMatrix * vec4(aPosition,1);//根据总变换矩阵计算此次绘制的顶点位置
17  //该处省略了调用pointLight方法与传递3个光的通道变量代码,读者可自行查阅随书光盘中的源代码
18
19  vec3 normalTarget=aPosition+aNormal;    //计算变换后的法向量
20  vec3 newNormal=(uMMatrix*vec4(normalTarget,1)).xyz-(uMMatrix*vec4
  (aPosition,1)).xyz;
21  vNormal=normalize(newNormal);      //对法向量规格化
22  vTextureCoord = aTexCoor;//将接收的纹理坐标传递给片元着色器
23  vPosition=(uMMatrix*vec4(aPosition,1)).xyz;  //计算物理世界中顶点位置
24 }

第1~13行是着色器中对全局变量的声明,主要包括总变换矩阵、变换矩阵、光源位置、摄像机位置、顶点位置以及顶点法向量的引用等,还有对传递给片元着色器的相关变量声明。
第19~21行是对Java代码部分传递过来的顶点法向量进行计算。对法向量进行变换,将法向量变换到当前的姿态下,并传递给片元着色器。
第22~23行将Java代码部分传递过来的顶点纹理坐标传递给片元着色器,然后根据鱼类本身的顶点计算出顶点在物理世界的坐标并传递到片元着色器。
(2)介绍完鱼类的顶点着色器后,下面将介绍鱼类的片元着色器,此片元着色器实现了鱼类身体上的明暗效果与灯光特效。下面着重介绍明暗效果的实现,其具体代码如下所示。

代码位置:见随书光盘源代码/第2章/MyWallPaper/assets/shader/目录下的fish_frag.sh。

1 precision mediump float;
2 varying vec2 vTextureCoord;       //接收从顶点着色器过来的参数
3 uniform sampler2D sTexture;      //本身纹理内容数据
4 uniform sampler2D sTextureHd;      //明暗纹理内容数据
5 varying vec3 vNormal;       //接收顶点着色器的法向量
6 varying vec3 vPosition;       //接收顶点着器的顶点
7 varying vec4 ambient;       //接收顶点着色器环境光
8 varying vec4 diffuse;       //接收顶点着色器散射光
9 varying vec4 specular;       //接收顶点着色镜面光
10 void main(){
11  float f;
12     vec4 finalColorDay;        //鱼类本身纹理颜色
13     vec4 finalColorNight;      //采样明暗纹理颜色
14     vec4 finalColorzj;        //混合后的纹理颜色
15     finalColorDay= texture2D(sTexture, vTextureCoord);//给此片元从纹理中采样出颜色值
16     vec2 tempTexCoor=vec2((vPosition.x+20.8)/5.2,(vPosition.z+18.0)/2.5);
     //8*8重复纹理
17     if(vNormal.y>0.2){        //鱼类动态相对上半身
18         finalColorNight = texture2D(sTextureHd, tempTexCoor);//采样出明暗纹理颜色值
        f=(finalColorNight.r+finalColorNight.g+finalColorNight.b)/3.0;
         //取3个颜色值平均值
20     }else if(vNormal.y<=0.2&&vNormal.y>=-0.2){   //过渡区域混合颜色值
21         if(vNormal.y>=0.0&&vNormal.y<=0.2){   //平滑过渡
22              finalColorNight = texture2D(sTextureHd,
23         tempTexCoor)*(1.0-2.5*(0.20-vNormal.y));//采样出过渡颜色
             f=(finalColorNight.r+finalColorNight.g
25         +finalColorNight.b)/3.0;    //取3个颜色值平均值
26         }else if(vNormal.y<0.0&&vNormal.y>=-0.2){   //平滑过渡
27              finalColorNight = texture2D(sTextureHd,  //采样出过渡颜色
28          tempTexCoor)*(0.5+2.5*vNormal.y);
             f=(finalColorNight.r+finalColorNight.g
30         +finalColorNight.b)/3.0;    //取3个颜色值平均值
31    }}else if(vNormal.y<-0.2){        //鱼类动态相对下半身
         f=0.0;
33     }
34     finalColorzj =finalColorDay*(1.0+f*1.5);      //算出混合后的片元颜色
35  //综合3个通道光的最终强度及片元的颜色计算出最终片元的颜色并传递给管线
36     gl_FragColor=finalColorzj*ambient+finalColorzj*specular+finalColorzj*
     diffuse;
37 }

第1~9行接收Java代码部分传过来的鱼类本身纹理内容数据和鱼类明暗采样纹理内容数据,接收顶点着色器传递过来的法向量、顶点数据与环境光、散射光、镜面反射光及顶点纹理坐标数据,用于片元着色器对每一片元颜色的计算。
第11~16行首先声明一个浮点数变量f,再声明3个用于采样颜色存储的浮点数向量。然后根据鱼类模型的顶点在物理世界中的坐标,计算出在88明暗采样纹理图中对应的顶点纹理坐标S、T,为后面的采样颜色值做准备。
第17~33行根据顶点着色器传递过来的顶点法向量计算出明暗纹理的采样颜色值。当然鱼类不能全身都有明暗,因此根据其法向量规定大于0.2的为全部纹理明暗采样颜色值,在0.2~0.2之间将明暗采样颜色值从1逐渐降为0,使之平滑过渡,小于0.2取采样值为0。然后计算出混合后的颜色值,再综合3个通道光计算出片元的最终颜色值,送入渲染管线。

时间: 2024-10-27 13:53:29

《Android 应用案例开发大全(第3版)》——第2章,第2.8节壁纸中的着色器开发的相关文章

《Android 应用案例开发大全(第3版)》——第2.8节壁纸中的着色器开发

2.8 壁纸中的着色器开发前面已经对3D动态壁纸--百纳水族馆的相关类进行了简要的介绍.本节将对本案例中用到的相关着色器进行介绍.本案例中用到的着色器共有四对,即气泡着色器.背景着色器.鱼类着色器及珍珠贝着色器.下面就对本壁纸中用到的着色器的开发进行一一介绍. 2.8.1 气泡的着色器气泡着色器分为顶点着色器与片元着色器,下面便分别对气泡着色器的顶点着色器和片元着色器的开发进行详细介绍. (1)首先介绍的是气泡着色器中的顶点着色器的开发,其详细代码如下. 1 uniform mat4 uMVPM

《Android 应用案例开发大全(第二版)》——2.9节壁纸的优化与改进

2.9 壁纸的优化与改进 Android 应用案例开发大全(第二版) 本章对3D水族馆动态壁纸的开发进行了详细介绍,在学习过程中,重点掌握鱼游动过程中鱼旋转角度的算法,并掌握鱼和鱼之间作用力变化规律,鱼受到的墙壁作用力变化规律等. 动态壁纸界面的优化 没有哪一个案例的运行界面是不可以更加完美和绚丽的,所以,对本案例的界面.风格,读者可以自行根据自己的想法进行改进,使其更加完美,如水族馆地面.背景壁纸.水草类等的纹理图都可以进一步完善,从而达到一个更加理想的效果. 动态壁纸物理碰撞的优化 本案例物

《Android 应用案例开发大全(第二版)》——1.8节本章小结

1.8 本章小结Android 应用案例开发大全(第二版)本章首先介绍了Android的诞生及其特点,相信读者对Android开发平台已有所了解.本章中还介绍了Android开发环境的搭建以及用Android创建的第一个应用程序--Hello Android,通过该程序,读者应该对Android应用程序的开发步骤有所了解. 本章重点为读者介绍的是Android应用程序的详细调试方法.项目结构和系统架构,这些能够帮助读者进一步更深层次地了解Android.对于理论性的知识,读者只要先暂时有些概念,

《Android 游戏开发大全(第二版)》——6.11节小结

6.11 小结Android 游戏开发大全(第二版)本章对目前流行的手机游戏类型逐一进行了介绍.希望读者对这些游戏的主要玩法和不同于其他游戏类型的独到之处有所了解,以便自己在开发相应类型的手机游戏时能够做到心中有数. 虽然本章将各种游戏类型分开介绍,其实随着手机产业的不断发展,一些手机游戏类型之间的分类也越来越模糊,很多手机游戏中融合了不同游戏类型的风格.还有一些新的游戏类型在悄然出现,读者在实际开发中也需要注意这些问题.

《Android 应用案例开发大全(第二版)》——1.6节本书案例项目的导入

1.6 本书案例项目的导入 Android 应用案例开发大全(第二版) 1.6.1 导入并运行Android程序 前面已经对手机如何与Eclipse连接进行了详细讲解,本小节将介绍如何在Eclipse中导入项目,并在手机上运行已经写好的Android程序.本节将以导入本书第9章百纳理财专家为例进行详细讲解,具体步骤如下. 1.在Eclipse中导入项目 (1)启动Eclipse,依次选择"File/Import"将弹出导入项目的窗口Import,选择Existing Projects

《Android 应用案例开发大全(第二版)》——2.6节绘制相关类

2.6 绘制相关类 Android 应用案例开发大全(第二版) 上一节完成了水族馆辅助绘制类开发过程的介绍,这一节将对本案例中的绘制相关类进行详细介绍.主要包括气泡绘制相关类.群鱼绘制相关类.鱼群绘制相关类和鱼食绘制相关类,从而使读者对本案例的开发有一个更加深刻的理解.下面就对这些绘制相关类进行详细介绍. 2.6.1 绘制气泡相关类 真实的水族馆中时常会冒出一些气泡,所以,在该壁纸中加入了透明气泡元素,从而达到仿真.酷炫的效果.最后本案例的运行效果是鱼在水族馆里面游,透明的气泡从屏幕下方不断冒出

《Android 应用案例开发大全(第二版)》——2.7节线程相关类

2.7 线程相关类 Android 应用案例开发大全(第二版) 上一节详细介绍了绘制相关类,使读者对本案例的开发有了进一步的理解,在这一节将对线程相关类的开发进行详细介绍.前面已经完成了对水族馆背景及水族馆中鱼.鱼群和气泡绘制的开发,但是只是将鱼.鱼群.气泡绘制出来是远远不够的,还要让它们动起来,从而产生更加真实的效果. 本案例中启动了多个线程来定时刷新它们的位置.线程相关类主要包括气泡移动线程类.群鱼游动线程类.鱼群游动线程类.鱼食游动线程类和吸引力线程类,下面就对线程相关类的开发进行详细介绍

《Android 应用案例开发大全(第二版)》——1.3节Android开发环境的搭建

1.3 Android开发环境的搭建 Android 应用案例开发大全(第二版) 本节主要讲解基于Eclipse的Android开发环境的搭建,模拟器的创建和运行,以及如何应用DDMS进行程序的调试. 1.3.1 Android SDK的安装及环境变量的配置 Android使用的编程语言是时下最流行的Java高级语言,Java的跨平台性和开源性,极大地促进了Android平台的发展. 首先要安装Java的JDK,并且正确地配置系统的环境变量(基于Windows操作系统).具体步骤如下. (1)

《Android 应用案例开发大全(第二版)》——2.1节案例背景及功能概述

2.1 案例背景及功能概述 Android 应用案例开发大全(第二版) 这一节将会对本案例背景及其基本功能进行简单介绍,通过本节学习,读者将会对本案例的具体功能及相应开发过程有一个整体了解,为读者进行以后的学习打下基础. 2.1.1 水族馆动态壁纸背景概述 壁纸是用户在手机屏幕上用来替代原先单一颜色背景的一张图片,有了这样一张图片可以使手机屏幕变得好看.漂亮.而随着移动手持设备功能的不断强大,静态壁纸已经不能再满足用户对酷炫壁纸的需求了,所以产生了动态壁纸. 动态壁纸是将手机屏幕所使用的壁纸以动