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

6.3 定位光与定向光

OpenGL ES 2.0游戏开发(上卷):基础技术和典型案例
上一节中介绍的光照效果都是基于定位光光源的,定位光光源类似于现实生活中的白炽灯灯泡,其在某个固定的位置,发出的光向四周发散。定位光照射的一个明显特点就是,在给定光源位置的情况下,对不同位置的物体产生的光照效果不同。

现实世界中并不都是定位光,例如照射到地面上的阳光,光线之间是平行的,这种光称为定向光。定向光照射的明显特点是,在给定光线方向的情况下,场景中不同位置的物体反映出的光照效果完全一致。图6-16中对定位光与定向光的照射特点进行了比较。

了解了定位光与定向光的区别后,接下来将通过一个案例Sample6_6介绍定向光效果的开发,其运行效果如图6-17所示。

说明

图6-17中左侧的图表示定向光方向从左向右照射的情况,右侧的图表示定向光方向从右向左照射的情况。从左右两幅效果图的对比中可以看出,在定向光方向确定的情况下,其对场景中任何位置的物体都产生相同的光照效果。

了解了定向光的基本原理及案例的运行效果后,就可以进行案例的开发了。由于实际上本案例仅仅是将案例Sample6_5复制了一份并进行了修改,因此这里仅给出修改的主要步骤,具体如下所列。

(1)由于使用定向光需要将光线的方向由Java程序传入渲染管线,为了方便首先需要对MatrixState类进一步升级,在其中增加设置定向光方向并存入缓冲的相关代码,具体情况如下。

1  public static float[] lightDirection = new float[] { 0, 0, 1 };//定向光方向向量数组
2  public static FloatBuffer lightDirectionFB;   //定向光方向向量数据缓冲
3  public static void setLightDirection(float x,float y,float z){//设置定向光方向的方法
4    llbbL.clear();                    //清空缓冲
5    //将定向光方向向量的X、Y、Z分量存入数组
6    lightDirection[0] = x;lightDirection[1] = y;lightDirection[2] = z;
7    llbbL.order(ByteOrder.nativeOrder());      //设置字节顺序
8    lightDirectionFB = llbbL.asFloatBuffer();    //转换为float型缓冲
9    lightDirectionFB.put(lightDirection);      //将光源方向向量放入数据缓冲
10    lightDirectionFB.position(0);          //设置缓冲区起始位置
11  }
  • 第1-2行为该类中新增加的成员变量,包括定向光方向向量数组lightDirection、定向光方向向量数据缓冲lightDirectionFB。
  • 第3-11行为设置定向光方向的方法,该方法首先将接收的定向光方向向量x、y、z轴的分量依次存入数组,然后再将数组中的值存入缓冲,供绘制时传入渲染管线。

(2)接着需要修改的是Ball类,在该类中增加了将定向光方向传入渲染管线的相关代码,具体情况如下。

1  public class Ball {              
2   ……//此处省略了部分代码,请读者自行查看随书光盘中的源代码
3   int muLightDirectionHandle;          //定向光方向一致变量的引用
4   public Ball(MySurfaceView mv) {/*代码省略*/}
5   public void initVertexData() {/*代码省略*/}
6   public void initShader(MySurfaceView mv) {  //初始化着色器
7    ……//此处省略了部分代码,请读者自行查看随书光盘中的源代码
8     muLightDirectionHandle=GLES20.glGetUniformLocation(
9       mProgram, "uLightDirection");       //获取着色器中定向光方向一致变量的引用
10   }
11    public void drawSelf() {    
12      ……//此处省略了部分代码,请读者自行查看随书光盘中的源代码
13        GLES20.glUniform3fv(        //将定向光方向传入渲染管线
14          muLightDirectionHandle, 1, MatrixState.lightDirectionFB);
15      ……//此处省略了部分代码,请读者自行查看随书光盘中的源代码
16      GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount); //绘制球体
17  }}
  • 第3行增加了声明定向光方向一致变量引用muLightDirectionHandle的代码。
  • 第8-9行在初始化着色器的方法中增加了获取定向光方向一致变量引用值的代码。
  • 第11-17行为绘制球的drawSelf方法,其中增加了将定向光方向传送进渲染管线的代码。

(3)完成了Ball类的修改后,对Java代码的修改就基本完成了,下面就可以修改着色器的代码了。由于本案例中的片元着色器与Sample6_5中的相同,因此这里仅介绍顶点着色器,其具体代码如下。

1  uniform mat4 uMVPMatrix;         //总变换矩阵
2  uniform mat4 uMMatrix;          //变换矩阵
3  uniform vec3 uLightDirection;      //定向光方向
4  uniform vec3 uCamera;          //摄像机位置
5  attribute vec3 aPosition;        //顶点位置
6  attribute vec3 aNormal;         //法向量
7  varying vec3 vPosition;         //用于传递给片元着色器的顶点位置
8  varying vec4 vAmbient;         //用于传递给片元着色器的环境光最终强度
9  varying vec4 vDiffuse;         //用于传递给片元着色器的散射光最终强度
10  varying vec4 vSpecular;        //用于传递给片元着色器的镜面光最终强度
11  void directionalLight(        //定向光光照计算的方法
12   in vec3 normal,           //法向量
13   inout vec4 ambient,        //环境光最终强度
14   inout vec4 diffuse,        //散射光最终强度
15   inout vec4 specular,        //镜面光最终强度
16   in vec3 lightDirection,      //定向光方向
17   in vec4 lightAmbient,       //环境光强度
18   in vec4 lightDiffuse,       //散射光强度
19   in vec4 lightSpecular       //镜面光强度
20  ){
21   ambient=lightAmbient;            //直接得出环境光的最终强度 
22   vec3 normalTarget=aPosition+normal;  //计算变换后的法向量
23   vec3 newNormal=(uMMatrix*vec4(normalTarget,1)).xyz-(uMMatrix*vec4(aPosition,1)).xyz;
24   newNormal=normalize(newNormal);     //对法向量规格化
25   //计算从表面点到摄像机的向量
26   vec3 eye= normalize(uCamera-(uMMatrix*vec4(aPosition,1)).xyz);  
27   vec3 vp= normalize(lightDirection);   //规格化定向光方向向量
28   vec3 halfVector=normalize(vp+eye);    //求视线与光线的半向量  
29   float shininess=50.0;              //粗糙度,越小越光滑
30   float nDotViewPosition=max(0.0,dot(newNormal,vp)); //求法向量与vp的点积与0的最大值
31   diffuse=lightDiffuse*nDotViewPosition;       //计算散射光的最终强度
32   float nDotViewHalfVector=dot(newNormal,halfVector);//法线与半向量的点积
33   float powerFactor=max(0.0,pow(nDotViewHalfVector,shininess));//镜面反射光强度因子
34   specular=lightSpecular*powerFactor;      //计算镜面光的最终强度
35  }
36  void main(){
37    gl_Position = uMVPMatrix * vec4(aPosition,1);//根据总变换矩阵计算此次绘制此顶点位置  
38    vec4 ambientTemp,diffuseTemp,specularTemp;  //用来接收3个通道最终强度的变量  
39    directionalLight(normalize(aNormal),ambientTemp,diffuseTemp,specularTemp,
    uLightDirection,
40    vec4(0.15,0.15,0.15,1.0),vec4(0.8,0.8,0.8,1.0),vec4(0.7,0.7,0.7,1.0));                   //计算定向光各通道强度
41    vAmbient=ambientTemp;     //将环境光最终强度传给片元着色器
42    vDiffuse=diffuseTemp;     //将散射光最终强度传给片元着色器
43    vSpecular=specularTemp;    //将镜面光最终强度传给片元着色器  
44    vPosition = aPosition;    //将顶点的位置传给片元着色器
45  }
  • 第3行将原来定位光光源位置一致变量的声明替换成了定向光方向向量一致变量的声明。
  • 第11-35行将原来计算定位光光照的pointLight方法替换成了计算定向光光照的directionalLight方法。上述两个方法主要有两点不同,首先是原来表示定位光光源位置的参数lightLocation被换成了表示定向光方向的参数lightDirection。另一个是计算时所需的光方向向量直接从lightDirection参数规格化即可得到,不需要再通过光源位置与被照射点位置进行计算了。

提示
从上述顶点着色器的代码中可以看出,与定位光相同,定向光也是分环境光、散射光、镜面光3个通道进行计算的。

时间: 2024-08-03 16:32:08

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

《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游戏开发(上卷)》一第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 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游戏开发(下卷)》一导读

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

《OpenGL ES 3.x游戏开发(上卷)》一2.7 小结

2.7 小结 本章主要对Android平台下游戏开发中可能会用到的Android平台的相关基础知识进行详细介绍,如文件的I/O操作.SQLite数据库的增.删.改.查,游戏中声音特效.蓝牙互连等.只有学好这些基础知识,开发人员才能开发出更好的Android手机游戏.读者一定要在这些基础上下足工夫,今后的开发道路才会一帆风顺. 提示 Android平台的其他相关知识还有很多,由于本书侧重于介绍OpenGL ES 3.0 3D应用程序的开发,故本章只是简单介绍了一些与3D开发不直接相关的,但在游戏开

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

1.5 Android应用程序运行的机制 前面介绍了如何搭建Android开发环境.如何开发Hello Android应用程序以及Android应用程序的调试,接下来在本节中将简要地介绍Android应用程序的运行机制. 1.5.1 应用程序的系统架构 Android平台由应用程序.应用程序框架.Android运行时.系统库以及底层Linux内核构成,详细结构如图1-68所示. 说明 应用程序层里面包含的就是需要读者去发挥创意开发的.各种各样丰富多彩的应用程序,这也是一般Android应用开发人

《OpenGL ES 3.x游戏开发(上卷)》一1.4 已有Android项目的导入和运行

1.4 已有Android项目的导入和运行 上一节介绍了如何搭建Android开发环境.如何开发Hello Android应用程序以及如何调试Android的应用程序,接下来在本节中将简要介绍已有Android项目的导入和运行. 1.4.1 项目的导入 首先介绍怎样将已有的Android项目导入Eclipse,此处以本书最后部分大案例中的"3D可爱抓娃娃"为例进行讲解,具体步骤如下. (1)启动Eclipse,依次单击"File|Import",系统将弹出导入项目的

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

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

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

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