OpenGL学习笔记

1 概述

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    

2 OpenGL ES 管道

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。

以上可以通过设置来打开或关闭某些功能。也可以设置某个工序的参数。

3 命名说明

常量都以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字符串。

4 EGL

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即可。

5 GLSurfaceView

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。

6 Renderer

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)

7 3D绘图基本概念

7.1 定点(Vertex)

顶点是两条或多条边交汇的地方。第一顶点也可以代表一个点光源或者是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数组(必须关闭)

7.2 Edge

边,定义的是两顶点之间的连线,边式面和面的边界线。通常在openGL中通过三个顶点定义一个面,然后由面获取其三条边。

7.3 Face

面,在OpenGL中,面特指一个三角形。对一个面所做的变化影响到连接面的所有顶点和边。

定义面的顶点顺序很重要:因为顶点顺序定义了面的朝向(前向或后向)。一般为性能考虑,只绘制面的前面。所有的“前面”定义统一的顶点顺序(顺时针或逆时针,程序指定)

gl.glFrontFace(GL10.GL_CCW);    //设置逆时针方法为面的“前面”
gl.glFrontFace(GL10.GL_CW); //设置顺时针方法为面的“前面”
gl.glEnable(GL10.GL_CULL_FACE); //打开忽略面开关。
gl.glCullFace(GL10.GL_BACK);    //设置忽略“后面”,即只绘制“前面”

7.4 Polygon

多边形,由多个面(三角形)拼接而成。多边形并不一定表示其在同一平面内。

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);  

8 顶点模式绘图

关于绘制部分,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->AD->E->F->D
GL_TRIANGLE_STRIP   每相连三个顶点组成一个三角形,由一系列三角形组成一个图形
    例如:A->B->C->AB->C->D->B. C->D->E->C
GL_TRIANGLE_FAN 以一个点为公共顶点,组成一系列相邻的三角形
    例如:A->B->C->AA->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可以启用的功能列表:

CommanMethodValid Data TypeElement
GL_COLOR_ARRAY(RGBA颜色)1glColorPointer中颜色取值数组;GL_BYTE;GL_UNSIGNED_BYTE;GL_SHORT;GL_UNSIGNED_SHORT;GL_INT;GL_UNSIGNED_INT;GL_FLOAT;GL_DOUBLE3/4
GL_SECONDARY_COLOR_ARRAY(RGBA颜色)对应glSecondaryColorPointer中颜色取值数组;GL_BYTE;GL_UNSIGNED_BYTE;GL_SHORT;GL_INT;GL_UNSIGNED_INT;GL_FLOAT;GL_DOUBLE3
GL_INDEX_ARRAY(索引颜色)对应glIndexPointerGL_UNSIGNED_BYTE;GL_SHORT;GL_INT;GL_FLOAT;GL_DOUBLE
GL_NORMAL_ARRAY(法线)对应glNormalPointer中法线数组,用于光照处理;GL_BYTE;GL_SHORT;GL_INT;GL_FLOAT;GL_DOUBLE3
GL_FOG_COORDINATE_ARRAY对应glFogCoordPointerGL_FLOAT;GL_DOUBLE1
GL_TEXTURE_COORD(材质)对应glTexCoordPointer中材质数组;GL_SHORT;GL_INT;GL_FLOAT;GL_DOUBLE1/2/3/4
GL_EDGE_FLAG_ARRAY对应glEdgeFlagPointerGLboolean1
GL_VERTEX_ARRAY(顶点)对应glVertexPointer中顶点坐标数组;GL_SHORT;GL_INT;GL_FLOAT;GL_DOUBLE2/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)

10 绘制

10.1 绘制前准备

OpenGL ES内部存放图形数据的Buffer有COLOR/DEPTH等,在绘图前,一般只需要清空COLOR和DEPTH Buffer。

10.2 绘制点Point

绘制独立的点,源码参考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

10.3 绘制线段 Line

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);

10.4 绘制

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);

10.5 绘制一个20面体

一个正20面体有12个顶点,20个面,30条边构成。
源码请查看DrawIcosahedronActivity。

11 三维坐标系及坐标变换

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轴正方向就是右手坐标系。

11.1 modelview变换

ModelView变换相当于改变眼睛和被观察物体的位置和角度。
坐标变换流程如下:
这里写图片描述

1、Object Coordinate System:也称为Local Coordinate System,用来定义一个模型本身的坐标系。
2、World Coordinate System:3D虚拟世界中的绝对坐标系,定义好这个坐标系的原点就可以用来描述模型的实现的位置,Camera的位置,光源的位置。
3、Viewing Coordinate System:一般用来计算光照效果
4Clip 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:三组三维参数分别表示在世界坐标系中:相机(眼睛)位置;瞄准方向的参考点;相机向上的方向。
    改变第一组三个参数相当于模拟人在实际环境中的移动
    改变第二组三个参数相当于人眼睛的转动,即所观察场景的变化(眼睛看物体的位置)
    改变第三组三个参数相当于确定人眼睛的方向(头顶的朝向)。

11.2投影变换Projection

相当于调整眼睛睁开的大小以看到物体的大小。
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同符号。所有处在裁切面外的顶点都会被裁切掉。

这里写图片描述

11.3 Viewport变换

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

11.4 颜色

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给物体添加颜色,后续介绍。

11.5 Buffer

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定义的区域。

11.6 OpenGL 光照模型

一般未添加光源的图看不出3D效果,故需要添加光源以看出3D效果。OpenGL光照模型中光源和光照效果可以细分为红、蓝、绿三部分。光源由红蓝绿强度来定义;而物体表面材料由其反射红蓝绿的程度和方向定义。OpenGL中光源可以分别控制、打开或关闭,支持最多八个光源,且光源可以设置红蓝绿三个元素的值。

光照效果分为:

Emitted:光源,一般指发光物体或光源,这种光不受其他光源的影响。
Ambient:环境光,不依赖光源方向。指光线经过多次反射后已经无法得知其方向的光,可以看做来自所有方向的光。
    该光如果射到某个平面上,其反射方向为所有方向。
Diffuse:漫射光,依赖于光源的方向和法线方向。
    当一束平行光射到粗糙的表面时,因面上凹凸不平,各点法线不一致,造成反射光无规则的反射,称为漫反射。漫射光射到某个平面时,其反射方向也未所有方向。
Specular:镜面反射光,依赖于光源的方向,法线方向和视角方向。指物体被光源直射的高亮区域,也可以称为镜面反射区。

最终决定看到物体的颜色除了光源的颜色和方向外,还取决于物体本身的颜色。物体的颜色也可以有不同的Ambient、Diffuse和Specular,表现为反射这些光的比例。Ambient和Diffuse通常为反射同样的颜色,Specular常表现为反射白色或灰色光。例如,白光照红色的球,球大部分显示红色,高亮区域显示白色。

11.7 设置光照效果

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)

11.8 材质设置

设置物体表面材料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,地址

本文参考了这篇文章

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值