1、本章要求本章要求:第第1212章章利用利用OpenGLOpenGL实现实现3D3D图形图形OpenGL的基本概念构建3D开发的基本框架绘制3D模型为3D图形添加纹理贴图效果为3D图形添加旋转效果为3D图形添加光照效果为3D图形添加透明效果12.1 OpenGL简介12.2 绘制3D图形12.3 添加效果12.4 综合实例绘制一个不断旋转的金字塔第第1212章章利用利用OpenGLOpenGL实现实现3D3D图形图形 在现在这个网络游戏逐渐盛行的时代,2D游戏已经不能完全满足用户的需求,3D技术已经被广泛的应用在PC游戏中,3D技术下一步将会肯定向手机平台发展,而Android系统作为当前最流行
2、的手机操作系统,完全内置3D技术OpenGl支持,本章将对如何在Android中利用OpenGL实现3D图形进行详细讲解。第第1212章章利用利用OpenGLOpenGL实现实现3D3D图形图形 OpenGL(Open Graphics Library)是由SGI公司于1992年发布的,一个功能强大、调用方便的底层图形库,它为编程人员提供了统一的操作,以便充分利用任何制造商提供的硬件。OpenGL的核心实现了视区和光照等我们熟知的概念,并试图向开发人员隐藏大部分硬件层。 由于OpenGL是专门为工作站设计的,它太大了,无法安装在移动设备上,所以Khronos Group为OpenGL提供了一个
3、子集OpenGL ES(OpenGL for Embedded System)。OpenGL ES是免费的、跨平台的、功能完善的2D/3D图形库接口API,它专门针对多种嵌入式系统(包括手机、PDA和游戏主机等)而设计的,提供一种标准方法来描述在图形处理器或主CPU上渲染这些图像的底层硬件。说明:Khronos Group是一个图形软硬件行业协会,该协会主要关注图形和多媒体方向的开放标准。OpenGL ES去除了OpenGL中的glBegin/glEnd,四边形(GL_QUADS)、多边形(GL_POLYGONS)等复杂图元等许多非绝对必要的特性。经过多年发展,目前的OpenGL ES现在主要
4、有OpenGL ES 1.x(针对固定管线硬件)和12.1 OpenGL12.1 OpenGL简介简介OpenGL ES 2.x(针对可编程管线硬件)两个版本。OpenGL ES 1.0是以OpenGL 1.3规范为基础的,OpenGL ES 1.1是以OpenGL 1.5规范为基础的,OpenGL ES 2.0 则是参照OpenGL 2.0规范定义的,它补充和修改了OpenGL ES 1.1标准着色器语言及API,将OpenGL ES 1.1中所有可以用着色器程序替换的功能全部删除了,这样可以节约移动设备的开销及电力消耗。说明:OpenGL ES可以应用于很多主流移动平台上,包括Androi
5、d、Symbian和iPhone等。Android为OpenGL提供了相应的支持,它专门为支持OpenGL提供了android.opengl包。在该包中,GLES10类是为支持OpenGL ES 1.0而提供的;GLES11类是为支持OpenGL ES 1.1而提供的,GLES20类是为支持OpenGL ES 2.0而提供的。其中,OpenGL ES 2.0是从Android 2.2(API Level 8)版本才开始使用的。如果你的应用只支持OpenGL ES 2.0,你必须在该项目的AndroidManifest.xml文件中添加下列设置:12.2.1 构建3D开发的基本框架12.2.2
6、绘制一个模型12.2 12.2 绘制绘制3D3D图形图形 OpenGL ES一个最常用的功能就是绘制3D图形。要绘制3D图形,大致可以分为两个步骤,下面分别进行讲解。12.2 12.2 绘制绘制3D3D图形图形构建一个3D开发的基本框架大致可以分为以下几个步骤:(1)创建一个Activity,并指定该Activity显示的内容是一个指定了Renderer对象的GLSurfaceView对象。例如,创建一个名称为MainActivity的Activity,在重写的onCreate()方法中,创建一个GLSurfaceView对象,并为其指定使用的Renderer对象,再将其设置为Activity
7、要显示的内容,可以使用下面的代码:Overrideprotected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState);GLSurfaceView mGLView = new GLSurfaceView(this);/创建一个GLSurfaceView对象mGLView.setRenderer(new CubeRenderer();/为GLSurfaceView指定使用的Renderer对象setContentView(mGLView);/设置Activity显示的内容为GLSurfaceVi
8、ew对象通常情况下,考虑到当Activity恢复和暂停时,GLSurfaceView对象也恢复或者暂停,还要重写Activity的onResume()方法和onPause()方法。例如,12.2.1 12.2.1 构建构建3D3D开发的基本框架开发的基本框架如果一个Activity使用的GLSurfaceView对象为mGLView,那么,可以使用以下的重写onResume()和onPause()方法的代码:Overrideprotected void onResume() super.onResume();mGLView.onResume();Overrideprotected void o
9、nPause() super.onPause();mGLView.onPause();(2)创建实现GLSurfaceView.Renderer接口的类。在创建该类时,需要实现接口中的以下3个方法:public void onSurfaceCreated(GL10 gl, EGLConfig config):当GLSurfaceView被创建时回调该方法。public void onDrawFrame(GL10 gl):Renderer对象调用该方法绘制GLSurfaceView的当前帧。public void onSurfaceChanged(GL10 gl, int width, int
10、height):当GLSurfaceView的大小改变时回调该方法。例如,创建一个实现GLSurfaceView.Renderer接口的类EmptyRenderer,并实现onSurfaceCreated()、onDrawFrame()和onSurfaceChanged()方法,为窗体设置背景颜色,具体代码如下:import javax.microedition.khronos.egl.EGLConfig;import javax.microedition.khronos.opengles.GL10;import android.opengl.GLSurfaceView;public clas
11、s EmptyRenderer implements GLSurfaceView.Renderer public void onSurfaceCreated(GL10 gl, EGLConfig config) / 设置窗体的背景颜色gl.glClearColor(0.7f, 0.7f, 0.9f, 1.0f);public void onDrawFrame(GL10 gl) / 重设背景颜色gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);public void onSurfaceChanged(GL10 gl,
12、int width, int height) gl.glViewport(0, 0, width, height); 当窗口被创建时,需要调用onSurfaceCreated()方法,进行一些初始化操作。onSurfaceCreated()方法有一个GL10类型的参数gl,gl相当于OpenGL ES的画笔。通过它提供的方法不仅可以绘制3D图形,也可以对OpenGL进行初始化。下面将以表格的形式给出GL10提供的用于进行初始化的方法,对于GL10提供的用于绘制3D图形的方法将在12.2.2节进行介绍。GL10提供的用于进行初始化的方法如表12-1所示。表12-1 GL10提供的用于进行初始化的
13、方法方 法描 述glClearColor(float red, float green, float blue, float alpha)用于指定清除屏幕时使用的颜色,4个参数分别用于设置红、绿、蓝和透明度的值,值的范围是0.0f1.0fglDisable(int cap)用于禁用OpenGL ES某个方面的特性。例如,要关闭抗抖动功能,可以使用gl.glDisable(GL10.GL_DITHER);语句glEnable(int cap)用于启用OpenGL ES某个方面的特性glFrustumf(float left, float right, float bottom, float to
14、p, float zNear, float zFar)用于设置透视视窗的空间大小glHint(int target, int mode)用于对OpenGL ES某个方面进行修正glLoadIdentity()用于初始化单位矩阵glMatrixMode(int mode)用 于 设 置 视 图 的 矩 阵 模 式 。 通 常 可 以 使 用GL10.GL_MODELVIEW和GL10.GL_PROJECTION两个常量值glShadeModel(int mode)用于设置OpenGL ES的阴影模式。例如,要设置为平滑模式,可以使用gl.glShadeModel(GL10.GL_SMOOTH);
15、语句glViewport(int x, int y, int width, int height)用于设置3D场景的大小在基本框架构建完成后,我们就可以在该框架的基础上绘制3D模型了。在OpenGL ES中,任何模型都会被分解为三角形。下面将以绘制一个2D的三角形为例介绍绘制3D模型的基本步骤。(1)在onSurfaceCreated()方法中,定义顶点坐标数组。例如,要绘制一个2维的三角形,可以使用以下代码定义顶点坐标数组:private final IntBuffer mVertexBuffer;public GLTriangle() int one = 65536;int vertice
16、s = 0, one, 0,/上顶点-one, -one, 0,/左下点one, -one, 0/右下点;ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);vbb.order(ByteOrder.nativeOrder();mVertexBuffer = vbb.asIntBuffer();mVertexBuffer.put(vertices);mVertexBuffer.position(0);12.2.2 12.2.2 绘制一个模型绘制一个模型说明:在默认的情况下,OpenGL ES采取的坐标是0,0,0(X,Y
17、,Z)表示GLSurfaceView的中心;1,1,0表示GLSurfaceView的右上角;-1,-1,0表示GLSurfaceView的左下角。(2)在onSurfaceCreated()方法中,应用以下代码启用顶点坐标数组。gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);/ 启用顶点坐标数组(3)在onDrawFrame()方法中,应用步骤(1)定义的顶点坐标数组绘制图形。例如,要绘制一个三角形可以使用下面的代码。gl.glVertexPointer(3, GL10.GL_FIXED, 0, mVertexBuffer);/ 为画笔指定顶点坐标数
18、据gl.glColor4f(1, 0, 0, 0.5f);/ 设置画笔颜色gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);/ 绘制图形在了解了应用OpenGL ES绘制3D图形的基本步骤后,下面将介绍一个具体的实例来介绍如何绘制一个立方体。【例12-1】 在Eclipse中创建Android项目,实现绘制一个6个面采用不同颜色的立方体。(实例位置:光盘MR源码第12章12-1)(1)在默认创建的MainActivity中,创建一个GLSurfaceView类型的成员变量,关键代码如下:private GLSurfaceView mGLView;(2)
19、在重写的onCreate()方法中,首先创建一个GLSurfaceView对象,然后为GLSurfaceView指定使用的Renderer对象,最后再设置Activity显示的内容为GLSurfaceView对象,代码如下:Overrideprotected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState);mGLView = new GLSurfaceView(this);/创建一个GLSurfaceView对象/为GLSurfaceView指定使用的Renderer对象mGLView.se
20、tRenderer(new CubeRenderer();/设置Activity显示的内容为GLSurfaceView对象setContentView(mGLView);(3)重写onResume()和onPause()方法,具体代码如下:Overrideprotected void onResume() super.onResume();mGLView.onResume();Overrideprotected void onPause() super.onPause();mGLView.onPause();(4)创建一个实现GLSurfaceView.Renderer接口的类CubeRend
21、erer,并实现onSurfaceCreated()、onDrawFrame()和onSurfaceChanged()方法,具体代码如下:import javax.microedition.khronos.egl.EGLConfig;import javax.microedition.khronos.opengles.GL10;import android.opengl.GLSurfaceView;public class CubeRenderer implements GLSurfaceView.Renderer Overridepublic void onDrawFrame(GL10 gl
22、) Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) (5)在onSurfaceCreated()方法中,应用以下代码进行初始化操作,主要包括设置窗体背景颜色、启用顶点坐标数组、关闭抗抖动功能、设置系统对透视进行修正、设置阴影平滑模式、启用深度测试及设置深度测试的类型等。具体代码如下:public void onSurfaceCreated(GL10 gl, EGLConfig c
23、onfig) gl.glClearColor(0.7f, 0.9f, 0.9f, 1.0f);/ 设置窗体背景颜色gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);/启用顶点坐标数组gl.glDisable(GL10.GL_DITHER);/ 关闭抗抖动 / 设置系统对透视进行修正gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST); gl.glShadeModel(GL10.GL_SMOOTH);/ 设置阴影平滑模式gl.glEnable(GL10.GL_DEPTH_TEST);
24、 /启用深度测试gl.glDepthFunc(GL10.GL_LEQUAL); / 设置深度测试的类型说明:深度测试就是让OpenGL ES负责跟踪每个物体在Z轴上的深度,这样可避免后面的物体遮挡前面的物体。(6)在onSurfaceChanged()方法中,首先设置OpenGL场景的大小,并计算透视视窗的宽度、高度比,然后将当前矩阵模式设为投影矩阵,再初始化单位矩阵,最后设置透视视窗的空间大小。具体代码如下:public void onSurfaceChanged(GL10 gl, int width, int height) gl.glViewport(0, 0, width, heigh
25、t);/ 设置OpenGL场景的大小float ratio = (float) width / height; / 计算透视视窗的宽度、高度比gl.glMatrixMode(GL10.GL_PROJECTION); / 将当前矩阵模式设为投影矩阵gl.glLoadIdentity(); / 初始化单位矩阵GLU.gluPerspective(gl, 45.0f, ratio, 1, 100f); / 设置透视视窗的空间大小(7)在onDrawFrame()方法中,首先清除颜色缓存和深度缓存,并设置使用模型矩阵进行变换,然后初始化单位矩阵,再设置视点,并旋转总坐标系,最后绘制立方体。具体代码如下
26、:public void onDrawFrame(GL10 gl) / 清除颜色缓存和深度缓存gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); gl.glMatrixMode(GL10.GL_MODELVIEW);/ 设置使用模型矩阵进行变换gl.glLoadIdentity(); / 初始化单位矩阵/ 当使用GL_MODELVIEW模式时,必须设置视点,也就是观察点GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);gl.glRotatef(1000,
27、-0.1f, -0.1f, 0.05f); / 旋转总坐标系cube.draw(gl); / 绘制立方体(8)创建一个用于绘制立方体模型的Java类,名称为GLCube,在该类中,首先定义一个用于记录顶点坐标数据缓冲的成员变量,关键代码如下:public class GLCube private final IntBuffer mVertexBuffer;/ 顶点坐标数据缓冲,(9)定义GLCube类的构造方法,在构造方法中创建一个记录顶点位置的数组,并根据该数组创建顶点坐标数据缓冲,具体代码如下:public GLCube() int one = 65536;int half = one /
28、 2;int vertices = / 前面 -half, -half, half, half, -half, half, -half, half, half, half, half, half, / 背面 -half, -half, -half, -half, half, -half, half, -half, -half, half, half, -half, / 左面 -half, -half, half, -half, half, half, -half, -half, -half, -half, half, -half, / 右面 half, -half, -half, half,
29、half, -half, half, -half, half, half, half, half, / 上面-half, half, half, half, half, half, -half, half, -half, half, half, -half, / 下面 -half, -half, half, -half, -half, -half, half, -half, half, half, -half, -half, ;/定义顶点位置/创建顶点坐标数据缓冲ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);vb
30、b.order(ByteOrder.nativeOrder();/设置字节顺序mVertexBuffer = vbb.asIntBuffer();/转换为int型缓冲mVertexBuffer.put(vertices);/向缓冲中放入顶点坐标数据mVertexBuffer.position(0);/设置缓冲区的起始位置(10)在GLCube类中,编写用于绘制立方体的draw()方法,在该方法中,首先为画笔指定顶点坐标数组,然后分别绘制立方体的6个面,每个面使用的颜色是不同的。draw()方法的具体代码如下:public void draw(GL10 gl) gl.glVertexPointe
31、r(3, GL10.GL_FIXED, 0, mVertexBuffer);/为画笔指定顶点坐标数据/ 绘制FRONT和BACK两个面gl.glColor4f(1, 0, 0, 1);gl.glNormal3f(0, 0, 1);gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);/绘制图形gl.glColor4f(1, 0, 0.5f, 1);gl.glNormal3f(0, 0, -1);gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 4, 4);/绘制图形/ 绘制LEFT和RIGHT两个面gl.glColor4f(0
32、, 1, 0, 1);gl.glNormal3f(-1, 0, 0);gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 8, 4);/绘制图形gl.glColor4f(0, 1, 0.5f, 1);gl.glNormal3f(1, 0, 0);gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 12, 4);/绘制图形/ 绘制TOP和BOTTOM两个面gl.glColor4f(0, 0, 1, 1);gl.glNormal3f(0, 1, 0);gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 16, 4)
33、;/绘制图形gl.glColor4f(0, 0, 0.5f, 1);gl.glNormal3f(0, -1, 0);gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 20, 4);/绘制图形(11)打开CubeRenderer类,在该类中创建一个代表立方体对象的成员变量,并为CubeRenderer类创建无参的构造方法,在该构造方法中,实例化立方体对象。关键代码如下:private final GLCube cube;/立方体对象public CubeRenderer() cube = new GLCube();/实例化立方体对象实例运行结果如图12-1所示。图1
34、2-1 绘制一个立方体12.3 12.3 添加效果添加效果12.3.1 应用纹理贴图12.3.2 旋转12.3.3 光照效果12.3.4 透明效果 上一节中介绍了如何绘制3D模型,在实际应用开发时,经常需要为其添加纹理贴图、光照、旋转等效果,本节将介绍如何为3D模型添加纹理贴图、光照、旋转以及透明效果等。12.3 12.3 添加效果添加效果为了让3D图形更加逼真,我们需要为这些3D图形应用纹理贴图。例如,要在场景中放置一个木箱,那么就需要为场景中的绘制的立方体应用木材纹理进行贴图。为3D模型添加纹理贴图大致可以分为以下3个步骤:(1)设置贴图坐标的数组信息,这与设置顶点坐标数组类似。(2)设置
35、启用贴图坐标数组。(3)调用GL10的texImage2D()方法生成纹理。说明:在使用纹理贴图时,需要准备一张纹理图片,建议该图片的长宽是2的N次方,例如,可以是256*256的图片,也可以是512*512的图片。【例12-2】 在例12-1的基础上为绘制的立方体进行纹理贴图。(实例位置:光盘MR源码第12章12-2)(1)打开GLCube类文件,在该类中定义,用于保存纹理贴图数据缓冲的成员变量,具体代码如下:private IntBuffer mTextureBuffer; / 纹理贴图数据缓冲(2)打开GLCube类文件,在构造方法中,定义贴图坐标数组,并根据该数组创建贴图坐标数据缓冲,
36、具体代码如下:12.3.1 12.3.1 应用纹理贴图应用纹理贴图int texCoords = / 前面0, one, one, one, 0, 0, one, 0,/ 后面one, one, one, 0, 0, one, 0, 0,/ 左面one, one, one, 0, 0, one, 0, 0,/ 右面one, one, one, 0, 0, one, 0, 0,/ 上面one, 0, 0, 0, one, one, 0, one,/ 下面0, 0, 0, one, one, 0, one, one, ;/定义贴图坐标数组ByteBuffer tbb = ByteBuffer.al
37、locateDirect(texCoords.length * 4);tbb.order(ByteOrder.nativeOrder(); / 设置字节顺序mTextureBuffer = tbb.asIntBuffer();/ 转换为int型缓冲mTextureBuffer.put(texCoords); / 向缓冲中放入贴图坐标数组mTextureBuffer.position(0);/ 设置缓冲区的起始位置(3)GLCube类的draw()方法的最后,首先应用GL10的glTexCorrdPointer()方法为画笔指定贴图坐标数据,关键代码如下:gl.glTexCoordPointer
38、(2, GL10.GL_FIXED, 0, mTextureBuffer);/为画笔指定贴图坐标数据(4)编写loadTexture()方法,用于进行纹理贴图,具体代码如下:/* * * 功能:进行纹理贴图 * * param gl * param context * param resource */void loadTexture(GL10 gl, Context context, int resource) Bitmap bmp = BitmapFactory.decodeResource(context.getResources(),resource);/加载位图GLUtils.tex
39、Image2D(GL10.GL_TEXTURE_2D, 0, bmp, 0);/使用图片生成纹理bmp.recycle();/释放资源(5)打开CubeRenderer类文件,在onSurfaceCreated()方法中,添加以下代码,首先启用贴图坐标数组,然后启用纹理贴图,最后调用GLCube类的loadTexture()方法进行纹理贴图。gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);/启用贴图坐标数组gl.glEnable(GL10.GL_TEXTURE_2D);/启用纹理贴图cube.loadTexture(gl, context
40、, R.drawable.mr);/进行纹理贴图运行本实例,将显示如图12-2所示的运行结果。图12-2 为立方体进行纹理贴图12.3.2 12.3.2 旋转旋转到目前为止,绘制的3D物体还是静止的,为了更好的看到3D效果,还可以为其添加旋转效果。这样就可达到动画效果了。要实现旋转比较简单,只需要使用GL10的glRotatef()方法不断的旋转要放置的对象即可。glRotatef()方法的语法格式如下:glRotatef(float angle, float x, float y, float z)其中,参数angle通常是一个变量,表示对象转过的角度;x表示X轴的旋转方向(值为1表示顺时针
41、、-1表示逆时针方向、0表示不旋转);y表示Y轴的旋转方向(值为1表示顺时针、-1表示逆时针方向、0表示不旋转);z表示Z轴的旋转方向(值为1表示顺时针、-1表示逆时针方向、0表示不旋转)。例如,要将对象经过X轴旋转n角度,可以使用下面的代码:gl.glRotatef(n, 1, 0, 0);【例12-3】 在例12-2的基础上实现一个不断旋转的立方体。(实例位置:光盘MR源码第12章12-3)(1)打开CubeRenderer类文件,在该类中定义,用于保存开始时间的成员变量,具体代码如下:private long startTime;/保存开始时间(2)在构造方法中,为成员变量startTi
42、me赋初始值为当前时间,具体代码如下:startTime=System.currentTimeMillis();(3)在onDrawFrame()方法的绘制立方体的代码之前,添加以下代码,完成旋转立方体的操作。/旋转long elapsed = System.currentTimeMillis() - startTime;/计算逝去的时间gl.glRotatef(elapsed * (30f / 1000f), 0, 1, 0);/在y轴上旋转30度gl.glRotatef(elapsed * (15f / 1000f), 1, 0, 0);/在x轴上旋转15度运行本实例,将显示如图12-3所
43、示的运行结果。图12-3 旋转的立方体为了使用程序效果更加美观、逼真,还可以让其模拟光照效果。在为物体添加光照效果前,我们先来了解一下3D图形支持的光照类型。所有的3D图形都支持以下3种光照类型:环境光:一种普通的光线,光线会照亮整个场景,即使对象背对着光线也可以。散射光:柔和的方向性光线.例如,荧光板上发出的光线就是这种散射光。场景中的大部分光线通常来源于散射光源。镜面高光:耀眼的光线,通常来源于明亮的点光源。与有光泽的材料结合使用时,这种光会带来高光效果,增加场景的真实感。在OpenGL中,添加光照效果,通常分为以下两个步骤进行。1光线在定义光照效果时,通常需要定义光线,也就是为场景添加光
44、源。这可以通过GL10提供的glLightfv()方法实现。glLightfv()方法的语法格式如下:glLightfv(int light, int pname, float params, int offset)其中,light表示光源的ID,当程序中包含多个光源时,可以通过这个ID来区分光源;pname表示光源的类型(参数值为GL10.GL_AMBIENT表示环境光、12.3.3 12.3.3 光照效果光照效果参数值为GL10.GL_DIFFUSE表示散射光);params:表示光源数组;offset:表示偏移量。例如,要定义一个发出白色的全方向的光源,可以使用下面的代码:float l
45、ightAmbient=new float0.2f,0.2f,0.2f,1;/定义环境光float lightDiffuse=new float1,1,1,1;/定义散射光float lightPos=new float1,1,1,1;/定义光源的位置gl.glEnable(GL10.GL_LIGHTING);/启用光源gl.glEnable(GL10.GL_LIGHT0);/启用0号光源gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, lightAmbient,0);/设置环境光gl.glLightfv(GL10.GL_LIGHT0, GL10.GL
46、_DIFFUSE, lightDiffuse, 0);/设置散射光gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPos, 0); /设置光源的位置注意:在定义和设置光源后,还需要使用glEnable()方法启用光源,否则,设置的光源将不起作用。2被照射的物体在定义光照效果时,通常需要定义被照射物体的制作材料,因为不同材料的光线反射情况是不同的。使用GL10提供的glMaterialfv()方法可以设置材质的环境光和散射光。glMaterialfv()方法的语法格式如下:glMaterialfv(int face, int pname,
47、float params, int offset)其中,face表示是为正面还是背面材质设置光源;pname表示光源的类型(参数值为GL10.GL_AMBIENT表示环境光、参数值为GL10.GL_DIFFUSE表示散射光);params:表示光源数组;offset:表示偏移量。例如,定义一个不是很亮的纸质的物体,可以使用下面的代码:float matAmbient=new float1,1,1,1;/定义材质的环境光float matDiffuse=new float1,1,1,1;/定义材质的散射光gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.G
48、L_AMBIENT, matAmbient,0);/设置环境光gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, matDiffuse,0);/设置散射光下面通过一个具体的实例来说明为物体添加光照效果的具体步骤。【例12-4】 在例12-3的基础上实现为旋转的立方体添加光照效果的功能。(实例位置:光盘MR源码第12章12-4)(1)打开CubeRenderer类文件,在onSurfaceCreated()方法中为被照射的物体设置材质。首先定义材质的环境光和散射光,然后设置材质的环境光和散射光,具体代码如下:float matAmbi
49、ent=new float1,1,1,1;/定义材质的环境光float matDiffuse=new float1,1,1,1;/定义材质的散射光gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, matAmbient,0);/设置环境光gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, matDiffuse,0);/设置散射光(2)在onSurfaceCreated()方法中添加场景光线。首先定义环境光和散射光,并定义光源的位置,然后启用光源和0号光源,最后设置环境光、
50、散射光和光源的位置,具体代码如下:float lightAmbient=new float0.2f,0.2f,0.2f,1;/定义环境光float lightDiffuse=new float1,1,1,1;/定义散射光float lightPos=new float1,1,1,1;/定义光源的位置gl.glEnable(GL10.GL_LIGHTING);/启用光源gl.glEnable(GL10.GL_LIGHT0);/启用0号光源gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, lightAmbient,0);/设置环境光gl.glLightfv