《Android 3D游戏开发技术宝典——OpenGL ES 2.0》第6章光照,本章将向读者介绍光照效果的开发,通过本章的学习,读者可以为场景中的物体增加逼真的光照效果,大大提升了场景的真实感。本节为大家介绍散射光。
6.2.3 散射光(2)
说明 图6-11左侧的图表示光源位于场景左侧进行照射的情况,右侧的图表示光源位于右侧进行照射的情况。从左右两幅效果图的对比中可以看出,正对光源(入射角小)的位置看起来较亮,而随着入射角的增大越来越暗,直至入射角大于90 后完全不能照亮。
了解了散射光的基本原理及案例的运行效果后,就可以进行案例的开发了。由于实际上本案例仅仅是将案例Sample6_2复制了一份并进行了修改,因此这里仅给出修改的主要步骤,具体如下所列。
(1)由于散射光效果与光源的位置密切相关,因此需要将光源的位置传递进着色器以进行光照的计算。为了方便起见,首先需要对工具类MatrixState进行升级,增加存储当前光源位置的相关成员变量以及设置光源位置的方法,具体代码如下。
代码位置:见随书光盘中源代码/第6章/Sample6_3/com/bn/Sample6_3目录下的MatrixState.java。
- 1 public static float[] lightLocation = new float[] { 0, 0, 0 }; //光源位置数组
- 2 public static FloatBuffer lightPositionFB; //光源位置的缓冲
- 3 static ByteBuffer llbbL = ByteBuffer.allocateDirect(3 * 4); //待用的字节缓冲
- 4 public static void setLightLocation(float x, float y, float z) { //设置光源位置的方法
- 5 llbbL.clear(); //清除缓冲中原有的数据
- 6 lightLocation[0] = x;lightLocation[1] = y;lightLocation[2] = z; //将新的光源位置存入数组
- 7 llbbL.order(ByteOrder.nativeOrder()); //设置字节顺序
- 8 lightPositionFB = llbbL.asFloatBuffer(); //转换为float型缓冲
- 9 lightPositionFB.put(lightLocation); //将光源位置放入缓冲
- 10 lightPositionFB.position(0); //设置缓冲区起始位置
- 11 }
第1-3行为该类中新增加的成员变量,主要有光源位置数组、光源位置缓冲和待用字节缓冲。
第4-11行为设置光源位置的方法,该方法主要功能为将参数中传递过来的光源位置首先存入数组,然后再存放进对应的float型缓冲供渲染时传入管线。
(2)接着需要修改的是Ball类,主要是增加初始化法向量数据以及将法向量数据传入渲染管线的相关代码,具体情况如下。
代码位置:见随书光盘中源代码/第6章/Sample6_3/com/bn/Sample6_3目录下的Ball.java。
- 1 public class Ball {
- 2 ……//此处省略了部分代码,请读者自行查看随书光盘中的源代码
- 3 int maNormalHandle; //顶点法向量属性引用
- 4 int maLightLocationHandle; //光源位置属性引用
- 5 FloatBuffer mNormalBuffer; //顶点法向量数据缓冲引用
- 6 public Ball(MySurfaceView mv) {/*代码省略*/}
- 7 public void initVertexData() { //初始化顶点数据的方法
- 8 ……//此处省略了部分代码,请读者自行查看随书光盘中的源代码
- 9 ByteBuffer nbb = ByteBuffer.allocateDirect(vertices.length*4);//创建顶点法向量缓冲
- 10 nbb.order(ByteOrder.nativeOrder()); //设置字节顺序
- 11 mNormalBuffer = nbb.asFloatBuffer(); //转换为float型缓冲
- 12 mNormalBuffer.put(vertices); //向缓冲区中放入顶点法向量数据
- 13 mNormalBuffer.position(0); //设置缓冲区起始位置
- 14 }
- 15 public void initShader(MySurfaceView mv) { //初始化着色器
- 16 ……//此处省略了部分代码,请读者自行查看随书光盘中的源代码
- 17 maNormalHandle= GLES20.glGetAttribLocation( //获取顶点法向量属性变量引用
- 18 mProgram, "aNormal");
- 19 maLightLocationHandle=GLES20.glGetUniformLocation(
- 20 mProgram, "uLightLocation"); //获取光源位置一致变量引用
- 21 }
- 22 public void drawSelf() {
- 23 ……//此处省略了部分代码,请读者自行查看随书光盘中的源代码
- 24 GLES20.glUniform3fv( //将光源位置传入渲染管线
- 25 maLightLocationHandle, 1, MatrixState.lightPositionFB);
- 26 GLES20.glVertexAttribPointer( //将顶点位置数据传入渲染管线
- 27 maPositionHandle, 3, GLES20.GL_FLOAT,false, 3 * 4, mVertexBuffer);
- 28 GLES20.glVertexAttribPointer( //将顶点法向量数据传入渲染管线
- 29 maNormalHandle, 3, GLES20.GL_FLOAT, false,3 * 4, mNormalBuffer);
- 30 GLES20.glEnableVertexAttribArray(maPositionHandle); //启用顶点位置数据
- 31 GLES20.glEnableVertexAttribArray(maNormalHandle); //启用顶点法向量数据
- 32 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount); //绘制球体
- 33 }}
第3-5行增加了声明顶点法向量属性变量引用、光源位置一致变量引用及顶点法向量数据缓冲的代码。
第7-14行为初始化顶点数据的方法,其中增加了初始化顶点法向量数据缓冲的相关代码。由于本案例中原始情况下的球心位于坐标原点,所以每个顶点法 向量的X、Y、Z轴分量与顶点的X、Y、Z坐标是一致的。这样就不必单独计算每个顶点的法向量了,直接将顶点坐标序列看作顶点法向量序列使用即可。
第15-21行在初始化着色器的方法中增加了获取顶点法向量属性变量引用以及光源位置一致变量引用的代码。
第22-33行为绘制球的drawSelf方法,其中增加了将法向量数据与光源位置数据传送进渲染管线的代码,同时也增加了启用顶点法向量数据的代码。
提示 并不是所有的情况下顶点法向量与顶点坐标都有必然联系,很多情况下顶点的法向量需要单独给出,本书中后面会有很多这样的案例。