赞
踩
OpenGL ES是基于OpenGL裁剪的用于Android平台的3D绘图库。主要涉及到的API包有
android.opengl //Android GL辅助类,连接OpenGL与Activity View
javax.microedition.khronos.egl //EGL管理Display/surface等
javax.microedition.khronos.opengles //GL绘制指令
java.nio
OpenGL ES 1.x的工序是固定的,称为Fix-Function Pipeline,可以通过打开和关闭设置参数的开关来打开或关闭功能。 2.0允许部分工序可变。
如下,为1.x的固定管道结构图
大致可以分为Transformation Stage和Rasterization Stage两部分。
OpenGL ES支持的基本图形为:点Point;线Line;三角形Triangle;
所有其他图形都是由以上三个组合而成。
在发出绘图指令后,会对顶点(Vertices)数组进行指定的坐标变换或光照处理。
顶点处理完成后,通过Rasterizer来生成像素信息,称为Fragments
对Fragments在经过Texture Processing,Color Sum,Fog(雾化)等处理,
并最终将结果存放在内存(FrameBuffer)中。
2.0可以通过编程来修改蓝色的步骤,称为Programmable Shader。
以上可以通过设置来打开或关闭某些功能。也可以设置某个工序的参数。
常量都以
GL_
为前缀.指令以gl开头,某些指令以3f/4f结尾,其中3/4表示参数个数,指令结尾f表示参数类型float,对应FloatBuffer。I/x代表int类型,对应IntBuffer。
v结尾的指令,代表参数是一个Vector
所有8bit整数对应到byte类型,16bit对应short类型,32bit整数(包括GLFixed)对应int类型,32bit 浮点数对应float类型
GL_TRUE/GL_FALSE 对应到boolean类型
字符串(char*) 对应Java的UTF-8字符串。
OpenGL ES 的javax.microedition.khronos.opengles
包定义了平台无关的GL绘制指令EGL。
使用EGL绘图一般步骤:
1、获取EGLDisplay对象
2、初始化与EGLDisplay之间的连接
3、获取EGLConfig对象
4、创建EGLContext实例
5、创建EGLSurface实例
6、连接EGLContext与EGLSurface
7、使用GL指令绘制图形
8、断开并释放与EGLSurface关联的EGLContext对象
9、删除EGLSurface对象
10、删除EGLContext对象
11、终止与EGLDisplay之间的连接
Android平台提供了对以上对以上的封装类android.opengl包,提供了GLSurfaceView对以上Display、Surface、Context进行封装管理。只需设置Render即可。
GLSurfaceView为android.opengl中的核心类。作用描述如下:
1、GLSurfaceView是连接OpenGL ES 与Android的View层次结构见的桥梁
2、GLSurfaceView使得OpenGL ES库适用Activity生命周期
3、使得选择合适的FrameBuffer像素格式变得容易
4、创建和管理单独绘制线程以达到平滑动画效果
5、提供了方便使用的调试工具来跟踪OpenGL ES函数以排错。
GLSurfaceView封装了绘图的操作,对外提供了一个Render接口,只需实现此接口,完成相应的方法即可。最后调用setRenderer(GLSurfaceView.Renderer renderer)
GLSurfaceView缺省创建RGB_565颜色格式的Surface,设置支持透明度,调用getHolder().setFormat(PixelFormat.TRANSLUCENT)
GLSurfaceView支持两种渲染方式:一种是RENDERMODE_CONTINUOUSLY,持续不断更新屏幕(默认);另一种是on-demand,只在调用requestRender()时更新屏幕。
setDebug(int) 设置debug标志
setEGLConfigChooser(boolean) 选择一个Config接近16bitRGB颜色模式,可以打开或关闭(Depth)Buffer,缺省为RGB_565并打开至少有16bit的depth Buffer
setEGLConfigChooser(EGLConfigChooser) 选择自定义的EGLConfigChooser
setEGLConfigChooser(int, int, int, int, int, int) 指定R G B A depth stencil支持的位数,缺省为RGB_565,16bit depth buffer。
GLSurfaceView.Renderer 定义了一个统一的图形绘制接口,类似surfaceView包含三个回调
//在Surface创建(Created)或者重新创建(reCreated)的时候调用。
//当rendering线程启动状态而EGL context丢失的时候也会调用此方法。
//故这个方法里面一般放置不经常变化的参数,以及重新获取EGL context后需要初始化的参数。
void onSurfaceCreated(GL10 gl, EGLConfig config)
//当surface改变大小的时候会被调用
//当onSurfaceCreated方法执行后,此方法一定会被执行
//一般在这里设置viewport和matrixMode
void onSurfaceChanged(GL10 gl, int width, int height)
//用于绘制当前帧
void onDrawFrame(GL10 gl)
顶点是两条或多条边交汇的地方。第一顶点也可以代表一个点光源或者是Camera的位置。Android系统使用浮点数组定义一个顶点。
private float vertices[] = {
-1.0f, 1.0f, 0.0f,
1.0f, -1.0f, 0.0f
}
一般使用java.nio中的buffer存储这些数据,以提高效率
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 2);
vbb.order(ByteOrder.nativeOrder());
FloatBuffer vertexBuffer = vbb.asFloatBuffer();
vertexBuffer.put(vertices);
vertedxBuffer.position(0);
传递该坐标信息给OpenGL ES库:
// Enabled the vertex buffer for writing and to be used during rendering.
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);// 启动GL_VERTEX_ARRAY数组
// Specifies the location and data format of an array of vertex
// coordinates to use when rendering.
//指定数组类型,当前为GL_FLOAT,指定字节缓存,当前为vertexBuffer(必须是nio.Buffer类型)
//指定数组大小,1表示大小为2
gl.glVertexPointer(1, GL10.GL_FLOAT, 0, vertexBuffer);
// Disable the vertices buffer.
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);//关闭 GL_VERTEX_ARRAY数组(必须关闭)
边,定义的是两顶点之间的连线,边式面和面的边界线。通常在openGL中通过三个顶点定义一个面,然后由面获取其三条边。
面,在OpenGL中,面特指一个三角形。对一个面所做的变化影响到连接面的所有顶点和边。
定义面的顶点顺序很重要:因为顶点顺序定义了面的朝向(前向或后向)。一般为性能考虑,只绘制面的前面。所有的“前面”定义统一的顶点顺序(顺时针或逆时针,程序指定)
gl.glFrontFace(GL10.GL_CCW); //设置逆时针方法为面的“前面”
gl.glFrontFace(GL10.GL_CW); //设置顺时针方法为面的“前面”
gl.glEnable(GL10.GL_CULL_FACE); //打开忽略面开关。
gl.glCullFace(GL10.GL_BACK); //设置忽略“后面”,即只绘制“前面”
多边形,由多个面(三角形)拼接而成。多边形并不一定表示其在同一平面内。
private short[] indices = { 0, 1, 2, 0, 2, 3 };
To gain some performance we also put this ones in a byte buffer.
// short is 2 bytes, therefore we multiply the number if vertices with 2.
ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2);
ibb.order(ByteOrder.nativeOrder());
ShortBuffer indexBuffer = ibb.asShortBuffer();
indexBuffer.put(indices);
indexBuffer.position(0);
关于绘制部分,OpenGL提供了两种方式来绘制(渲染)多边形。
//使用VetexBuffer 来绘制,顶点的顺序由vertexBuffer中的顺序指定。
//mode指定所需绘制的几何图形的基本类型,下同。
public abstract void glDrawArrays(int mode, int first, int count)
//可以重新定义顶点的顺序,顶点的顺序由indices Buffer 指定。
public abstract void glDrawElements(int mode, int count, int type, Buffer indices)
model(顶点模式)有以下几种类型:
GL_POINTS 绘制独立的点
GL_LINE_STRIP 绘制一系列线段,顶点互相连接起来,例如A->B->C->D
GL_LINE_LOOP 同上,只是这里绘制的线段首尾相连。例如A->B->C->D->A
GL_LINES 顶点两两相连,构成多条线段,例如 A->B C->D E->F
GL_TRIANGLES 每隔三个顶点构成一个三角形,组成多个三角形, 例如A->B->C->A,D->E->F->D
GL_TRIANGLE_STRIP 每相连三个顶点组成一个三角形,由一系列三角形组成一个图形
例如:A->B->C->A,B->C->D->B. C->D->E->C
GL_TRIANGLE_FAN 以一个点为公共顶点,组成一系列相邻的三角形
例如:A->B->C->A, A->C->D->A, A->D->E->A
打开和关闭顶点模式的方法分别为:
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
打开顶点模式开关后,将坐标传递给OpenGL ES方法为
void glVertexPointer(
int size, 每个顶点坐标维数:可以是2/3/4.
int type, 顶点的数据类型,可以是GL_BYTE/GL_SHORT/GL_FIXED/GL_FLOAT(缺省),
分别对应数组类型 byte[]/short[]/int[]/float[]
int stride, 每个相邻顶点之间在数组之间的间隔(单位为字节数),缺省为0,表示无间隔,即两个顶点之间若有其他数据,则stride长度为数据长度。
java.nio.Buffer pointer 存储顶点的数组,类型由type指定。
);
gl.glEnableClientState可以启用的功能列表:
Comman | Method | Valid Data Type | Element |
---|---|---|---|
GL_COLOR_ARRAY (RGBA颜色) | 对1glColorPointer 中颜色取值数组; | GL_BYTE;GL_UNSIGNED_BYTE;GL_SHORT;GL_UNSIGNED_SHORT;GL_INT;GL_UNSIGNED_INT;GL_FLOAT;GL_DOUBLE | 3/4 |
GL_SECONDARY_COLOR_ARRAY (RGBA颜色) | 对应glSecondaryColorPointer 中颜色取值数组; | GL_BYTE;GL_UNSIGNED_BYTE;GL_SHORT;GL_INT;GL_UNSIGNED_INT;GL_FLOAT;GL_DOUBLE | 3 |
GL_INDEX_ARRAY (索引颜色) | 对应glIndexPointer | GL_UNSIGNED_BYTE;GL_SHORT;GL_INT;GL_FLOAT;GL_DOUBLE | |
GL_NORMAL_ARRAY (法线) | 对应glNormalPointer 中法线数组,用于光照处理; | GL_BYTE;GL_SHORT;GL_INT;GL_FLOAT;GL_DOUBLE | 3 |
GL_FOG_COORDINATE_ARRAY | 对应glFogCoordPointer | GL_FLOAT;GL_DOUBLE | 1 |
GL_TEXTURE_COORD(材质) | 对应glTexCoordPointer 中材质数组; | GL_SHORT;GL_INT;GL_FLOAT;GL_DOUBLE | 1/2/3/4 |
GL_EDGE_FLAG_ARRAY | 对应glEdgeFlagPointer | GLboolean | 1 |
GL_VERTEX_ARRAY (顶点) | 对应glVertexPointer 中顶点坐标数组; | GL_SHORT;GL_INT;GL_FLOAT;GL_DOUBLE | 2/3/4 |
GL_POINT_SIZE_ARRAY_OES (顶点) | 对应; |
顶点模式中三角形的顺序,涉及到优化,因为一般只会绘制”前面”,故对一个面的前面后面定义还是比较重要的:
设置代码逆时针方法为面的”前面”
gl.glFrontFace(GL10.GL_CCW)
设置代码顺时针方法为面的”后面”
gl.glFrontFace(GL10.GL_CW);
打开忽略”后面”设置
gl.glEnable(GL10.GL_CULL_FACE)
明确指明”忽略”哪个面
gl.glCulFace(GL10.GL_BACK)
OpenGL ES内部存放图形数据的Buffer有COLOR/DEPTH等,在绘图前,一般只需要清空COLOR和DEPTH Buffer。
绘制独立的点,源码参考DrawPointActivity
float[] vertexs = new float[]{
-0.8f, -0.4f * 1.732f, 0.0f,
0.8f, -0.4f * 1.732f, 0.0f,
0.0f, 0.4f * 1.732f, 0.0f
};
ByteBuffer vbb = ByteBuffer.allocateDirect(vertexs.length * 4);
vbb.order(ByteOrder.nativeOrder());
FloatBuffer vfb = vbb.asFloatBuffer();
vfb.put(vertexs);
vfb.position(0);
gl.glColor4f(1.0f, 0.0f, 0.0f, 1.0f); //设置当前颜色为红色
gl.glPointSize(8f); //设置点的大小
gl.glLoadIdentity();//恢复为单位矩阵
gl.glTranslatef(0, 0, -4);//z轴平移-4
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);//打开Vertex开关
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vfb);//告知OpenGl 顶点坐标
gl.glDrawArrays(GL10.GL_POINTS, 0, 3);//绘制GL_POINTS,即点
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);//关闭Vertex
GL_LINE_STRIP
绘制一系列线段,顶点互相连接起来,例如A->B->C->D
GL_LINE_LOOP
同上,只是这里绘制的线段首尾相连。例如A->B->C->D->A
GL_LINES
顶点两两相连,构成多条线段,例如 A->B C->D E->F
源码参考DrawLineActivity
ByteBuffer vbb = ByteBuffer.allocateDirect(vertexs.length * 4);
vbb.order(ByteOrder.nativeOrder());
FloatBuffer vfb = vbb.asFloatBuffer();
vfb.put(vertexs);
vfb.position(0);
gl.glLoadIdentity(); //恢复单位矩阵
gl.glTranslatef(0, 0, -4); //平移
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vfb);
index++;
index %= 300;
if(index < 100){
gl.glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
gl.glDrawArrays(GL10.GL_LINES, 0, 4);
}else if( index < 200){
gl.glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
gl.glDrawArrays(GL10.GL_LINE_STRIP, 0, 4);
}else {
gl.glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
gl.glDrawArrays(GL10.GL_LINE_LOOP, 0, 4);
}
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
GL_TRIANGLES
每隔三个顶点构成一个三角形,组成多个三角形, 例如A->B->C->A,D->E->F->D
GL_TRIANGLE_STRIP
每相连三个顶点组成一个三角形,由一系列三角形组成一个图形
例如:A->B->C->A,B->C->D->B. C->D->E->C
GL_TRIANGLE_FAN
以一个点为公共顶点,组成一系列相邻的三角形
例如:A->B->C->A, A->C->D->A, A->D->E->A
源码请参考DrawTriangleActivity
ByteBuffer vbb = ByteBuffer.allocateDirect(vertexs.length*4);
vbb.order(ByteOrder.nativeOrder());
FloatBuffer vfb = vbb.asFloatBuffer();
vfb.put(vertexs);
vfb.position(0);
gl.glLoadIdentity();
gl.glTranslatef(0, 0, -4);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vfb);
index++;
index %= 300;
if(index < 100){
gl.glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 6);
}else if( index < 200){
gl.glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 6);
}else {
gl.glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_FAN, 0, 6);
}
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
一个正20面体有12个顶点,20个面,30条边构成。
源码请查看DrawIcosahedronActivity。
openGL库的最终目的还是在二维平面上显示3D物体。坐标变换使用矩阵来描述;三维坐标通常使用齐次坐标来(使用四个分量(x,y,z,w)来描述3D坐标称为齐次坐标)定义,具体的流程可以分为如下:
1、视角 viewing transformation(平移或选择Camera)
2、模型 Modeling transformation(调整所绘制模型的位置,角度或缩放比例)
3、投影 Projection transformation(裁剪投影场景)
4、将坐标做规范化处理NDC
5、viewport transformation(对最终的图像缩放显示等)
OpenGL ES右手坐标系统:右手拇指指向x轴正方向,食指指向y轴正方向,中指能指向z轴正方向就是右手坐标系。
ModelView变换相当于改变眼睛和被观察物体的位置和角度。
坐标变换流程如下:
1、Object Coordinate System:也称为Local Coordinate System,用来定义一个模型本身的坐标系。
2、World Coordinate System:3D虚拟世界中的绝对坐标系,定义好这个坐标系的原点就可以用来描述模型的实现的位置,Camera的位置,光源的位置。
3、Viewing Coordinate System:一般用来计算光照效果
4、Clip Coordinate System:对3D场景使用投影变换裁剪frustum(锥台,即视景体)
5、Normalized device Coordinate System:即NDC,规范后台坐标
6、Window Coordinate System:最后屏幕显示的2D坐标系统,一般原点定义在屏幕左上角。
在OpenGL ES中
使用GL10.GL_MODELVIEW来同时指定viewing matrix和modeling matrix;
使用GL10.GL_PROJECTION指定投影变换。OpenGL ES支持透视投影和正侧投影。
使用glViewport指定Viewport变换。
矩阵相关API介绍:
glLoadIdentity():将当前矩阵设置为单位矩阵(单位矩阵对角线全1,其他为0)
glMultMatrixf()/glMultMatrixx():允许指定任意矩阵和当前矩阵相乘。
glMatrixMode():选择当前矩阵种类。
glLoadMatrixf/glLoadMatrixx: 将当前矩阵设置为任意指定的矩阵
glPushMatrix/glPopMatrix: 将当前矩阵入栈保存/出栈恢复
glTranslatef :矩阵平移
glRotatef(float angle,float x, float y, float z): 矩阵旋转,以(x,y,z)为参照矢量方向逆时针旋转angle角度,即绕(x,y,z)旋转angle角度。
其中glRotatef(angle,-x,-y,-z)等价于glRotatef(-agnle,x,y,z)
glScalef:矩阵缩放
例如,glScale(2f,2f,2f):将当前矩阵每个坐标值都乘以2.
glLookAt:三组三维参数分别表示在世界坐标系中:相机(眼睛)位置;瞄准方向的参考点;相机向上的方向。
改变第一组三个参数相当于模拟人在实际环境中的移动
改变第二组三个参数相当于人眼睛的转动,即所观察场景的变化(眼睛看物体的位置)
改变第三组三个参数相当于确定人眼睛的方向(头顶的朝向)。
相当于调整眼睛睁开的大小以看到物体的大小。
glMatrixMode(GL_PROJECTION)
设置Matrix模式为投影模式。表示后续坐标变换针对投影矩阵。投影变换的目的是定义视锥(viewing volume),视锥一方面定义了物体如何投影到屏幕(透视投影或正侧投影),另一方面也定义了裁剪场景区域的大小。
1、透视投影(Perspective Project)
特点:近大远小;
函数:
glFrustum(float left,float right,float bottom,float top,float near,float far)
视锥由(left,bottom,-near)和(right,top,-near)定义了靠近观测点的近裁剪面。far表示远裁剪平面离视点的距离值。
OpenGL ES提供了一个辅助函数简化以上操作。
gluPerspective(float fovy, float aspect, float zNear, float zFar)
:
透视投影,fovy:y方向上的视角度数;aspect:纵横比(宽高比),即x/y;后两个参数(要求必须正数)表示近的面和远的面离眼睛的距离。
2、正侧投影
特点:视锥为长方体,物体的大小不随到观测点的距离而变化,投影后可以保持物体之间的距离和夹角。
函数为:
glOrtho(float left,float right,float bottom,float top, float near,float far)
前两个参数,x轴最小最大坐标;中间两个参数,y轴最小最大坐标;最后两个,z轴最小和最大坐标。其近裁切面是一个矩形:左下角坐标为(left,bottom,-near),右上角坐标(right, top, -near);其远裁切面也是一个矩形:左下角(left, bottom, -far),右上角(right,top,far).其中,所有的near和far同符号。所有处在裁切面外的顶点都会被裁切掉。
Viewport显示区域是一个长方形区域,使用屏幕坐标系来定义。即屏幕左下为原点,向上为y轴正方向,向右为x轴正方向。
定义显示视窗大小的方法:
glViewport(int x, int y, int width, int height)
缺省的Viewport大小和屏幕大小一致。其中(x,y)表示左下角的位置,width和height表示宽和高。
若投影变换的宽高比aspect和viewport的width/height不一致,则显示的图形需要拉伸以适应viweport,因而可能导致图像变形。
z坐标变换
modelview/projection变换同样适用于Z轴坐标,但Z轴坐标的取值范围是0.0-1.0,作为OpenGL ES深度测试的依据。
参考示例见源码:DrawSolarSystemActivity
OpenGL ES支持两种颜色模式:
Flat Color(单色):通知OpenGL使用单一颜色渲染,会一直使用此颜色直到指定新的颜色。
指定颜色方法glColor4f(float red,float green,float blue,float alpha);缺省为白色。
Smooth Color(平滑颜色过渡):给每个顶点定义一个颜色,OpenGL将使用不同顶点间的过渡色(渐变色)
方法glColorPointer(int size, int type, int stride, Buffer pointer)
其中,pointer是存储颜色的数组,数组项即颜色取值范围0-1
使用光照Lighting给物体添加颜色,后续介绍。
Color Buffer 存储颜色
Depth Buffer (z Buffer)存储像素与观测点之间的距离,绘制过程中,为准确绘制物体与Z轴的前后关系,需要先绘制距离观测点最远的物体,再绘制近的物体。OpenGL ES中使用了The depth buffer algorithm,此算法描述如下:
1、使用最大值清空Depth Buffer。
最大值缺省为1.0,表示距离ViewPoint最远的裁剪距离(相对距离);
最小值为0,表示距离ViewPoint最近的裁剪距离。(相对距离);
通常使用gl.Clear(GL10.GL_DEPTH_BUFFER_BIT)清空depth,赋值为1.0 Depth,gl.Clear(GL10.GL_COLOR_BUFFER_BIT)清空color(通常以上两个会同时进行)
gl.glClearDepthf(float depth):指定清空Depth Buffer使用的值。就是修改上面默认的1.0
gl.glEnable/glDisable(GL10.GL_DEPTH_TEST):打开/关闭depth test
2、当OpenGL栅格化所绘制的基本图形Primitive,将计算该Primitive与ViewPoint之间的距离,
保存在Depth Buffer中。
3、然后比较所要绘制的图形的距离和当前Depth Buffer中的值,
若前者小,则物体离viewpoint近,OpenGL则更新相应的Color Buffer并使用这个距离更新Depth Buffer;
否则若后者小,表示当前需要绘制的图形,在已被绘制的图形后面,则无需绘制该图形(删除)。
Stencil Buffer 遮罩层,可以将屏幕局限在Stencil Buffer定义的区域。
一般未添加光源的图看不出3D效果,故需要添加光源以看出3D效果。OpenGL光照模型中光源和光照效果可以细分为红、蓝、绿三部分。光源由红蓝绿强度来定义;而物体表面材料由其反射红蓝绿的程度和方向定义。OpenGL中光源可以分别控制、打开或关闭,支持最多八个光源,且光源可以设置红蓝绿三个元素的值。
光照效果分为:
Emitted:光源,一般指发光物体或光源,这种光不受其他光源的影响。
Ambient:环境光,不依赖光源方向。指光线经过多次反射后已经无法得知其方向的光,可以看做来自所有方向的光。
该光如果射到某个平面上,其反射方向为所有方向。
Diffuse:漫射光,依赖于光源的方向和法线方向。
当一束平行光射到粗糙的表面时,因面上凹凸不平,各点法线不一致,造成反射光无规则的反射,称为漫反射。漫射光射到某个平面时,其反射方向也未所有方向。
Specular:镜面反射光,依赖于光源的方向,法线方向和视角方向。指物体被光源直射的高亮区域,也可以称为镜面反射区。
最终决定看到物体的颜色除了光源的颜色和方向外,还取决于物体本身的颜色。物体的颜色也可以有不同的Ambient、Diffuse和Specular,表现为反射这些光的比例。Ambient和Diffuse通常为反射同样的颜色,Specular常表现为反射白色或灰色光。例如,白光照红色的球,球大部分显示红色,高亮区域显示白色。
1、设置光源
OpenGL中最多使用8个光源,编号0-7。光源分为:
Parallel light source:平行光,代表由位于无穷远初均匀发光体,太阳可以近似为平行光源
Spot light source:点光源,发出的光可以360度,如灯泡。
可以为点光源设置光衰减属性(attenuation)或者让点光源只能射向某个方向。
可以为图像不同部分设置不同光源。
打开光源总开关gl.glEnable(GL10.GL_LIGHTING)
打开某个光源gl.glEnable(GL10.GL_LIGHT0)
,这里必须先打开光源总开关才能设置打开某个光源。
设置光源方法void glLightf(int light,int pname, float param);
等系列方法。
light 指光源序号,0-7
pname 光源参数名称,取值如下:
GL_AMBIENT/GL_DIFFUSE/GL_SPECULAR:设置光源颜色,可以指定为RGBA。
GL_POSIITON:设置光源位置,值为(x,y,z,w),
平行光:w设为0.0,(x,y,z)为平行光方向。
点光源:w设置为非0值,通常设置为1.0.(x,y,z)为点光源坐标位置。
聚光灯,w设置为非0值,通常设置为1.0.(x,y,z)为点光源坐标位置。
需同时设置GL_SPOT_DIRECTION,(x,y,z)为聚光方向。
需同时设置GL_CUTOFF,参数为聚光灯发散角(0-90度)。
GL_SPOT_EXPONENT,设置聚光灯的聚光能力,值越大,聚光区域越小,聚光能力越强。
其他参数,设置点光源(包括聚光灯)的光线衰减参数。
param 参数的值
具体可参考如下图片
2、设置法线
只有设置了法线Normal,光源才能在物体上出现光照效果。三维平面的法线是垂直于该平面的三维向量。曲面的某点的法线为垂直于该点切平面的向量。
为平面设置法线方法glNormal(float nx,float ny,float nz)
:该方法为后续所有平面设置同样的法线方向,直到从新设置新的法线。
为某个顶点设置法线glNormalPointer(int type, int stride,Buffer pointer)
,其中type是Buffer的类型;stride为每个参数之间的间隔;pointer为法线值数组。
打开法线数组glEnableClientState(GL10.GL_NORMAL_ARRAY)
同color/array类似。
规范化法线设置glEnable(GL10.GL_NORMALIZE)
;使用坐标变换/缩放时,若三个方向缩放比例不同,顶点或平面的法线可能就有变化,此时需要打开规范法线设置。规范化后法向量为单位向量,此时可同时打开缩放法线设置glEnable(GL10.GL_RESCALE_NORMAL)
设置物体表面材料Material的反光属性(颜色/材质)的方法
glMaterialf(int face, int pname, float params)
face只能是GL_FRONT_AND_BACK,表示修改物体前面和后的材质光线属性;
pname参数类型
GL_AMBIENT/GL_DIFFUSE/GL_SPECULAR/GL_EMISSION为颜色RGBA值
GL_SHININESS为光散射值,0-128,值越大,光的散射越小。
param 参数说明
设置光照模型参数方法:
glLightModelf(int pname, float param)
pname:参数类型
params 参数值
顶点的颜色由以上提到的光源、材质光线属性、光照模型综合决定,由光照方程计算出来。
示例见LigntingActivity
本篇完整示例,包含注释已上传github,地址
本文参考了这篇文章
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。