本人从事android开发已有两年,今年三月份才开始看了一本叫《OpenGL ES 2.0 for android》的书,受益颇大,算是入了一个门。随后,在北京找一个公司实习,参与的是一个基于openfire的IM交易平台android客户端的开发,在1月份恰好简单研究过openfire以及asmark、xmpp。在实习公司呆了两个月,回校做毕业设计,做的是一个基于web的项目,花了一个月的时间。毕业后,由于诸多原因,离开了实习公司。七月份入职一个做室内地图的公司,公司6月份创办,进去时包括我在内四个人:总经理,数据部经理,软件研发部经理外加我这个小喽啰。引擎android sdk开发中用到OpenGL,我也有一段时间没看了,这次有重新拾起。废话到此为止,这也是我的处女座,排版不对、讲解不对之处请多多指教。同时建议入手OpenGL的小伙伴们可以选择OpenGL ES,难度应该较小些。小弟只是初学者,还望各位大神多多指点。
对于一个初学者来时,最难的是入门,而又往往难以抓住一些关键地方。只有反复淌过一条河,才能知道这条河哪个地方有湍流,哪个地方有暗礁。要想入门OpenGL(OpenGL ES 2.0),我个人觉得可以再这几点来把握:
坐标变换。
视角设置。
着色器(Shader)。
下面点到即止的来说一下这几个点。对于OpenGL里面的坐标系主要需关注的是:物体坐标系,世界坐标系,视觉坐标系和设备坐标系。通俗的理解是:物体坐标系是对你要画的物体形状大小而言,世界坐标系是你要把这个物体或多个物体放到哪个位置,视觉坐标系是你从哪个来位置观察这些物体并定义的视觉范围,设备坐标是在屏幕上显示这些物体,坐标系之间的转化和物体的移动,旋转,缩放都是通过矩阵来实现的,称为“矩阵变换”。视角设置:它在openGL 3D中有着举足轻重的地位,它的合理性,直接影响视图效果。着色器:它的作用是传递数据,还可以做出各种特效,最基本的着色器包括:顶点着色器和片元着色器。
介绍到此为止,下面通过一个例子来介绍OpenGL。首先我们确定了目标,画一个长方体定义为Desk,需要确定其顶点位置,面的颜色。
1.坐标变换
顶点坐标和顶点颜色坐标:
/**框架边长*/
public static final float DESK_WIDTH = 0.94f;
public static final float FACTOR = 2f;
//顶点数组
float[] mVertex = new float[]{
//前面xy
-DESK_WIDTH,DESK_WIDTH/FACTOR,DESK_WIDTH,//0
-DESK_WIDTH,-DESK_WIDTH/FACTOR,DESK_WIDTH,//1
DESK_WIDTH,-DESK_WIDTH/FACTOR,DESK_WIDTH,//2
-DESK_WIDTH,DESK_WIDTH/FACTOR,DESK_WIDTH,//0
DESK_WIDTH,-DESK_WIDTH/FACTOR,DESK_WIDTH,//2
DESK_WIDTH,DESK_WIDTH/FACTOR,DESK_WIDTH,//3
//后面
-DESK_WIDTH,DESK_WIDTH/FACTOR,-DESK_WIDTH,//0
-DESK_WIDTH,-DESK_WIDTH/FACTOR,-DESK_WIDTH,//1
DESK_WIDTH,-DESK_WIDTH/FACTOR,-DESK_WIDTH,//2
-DESK_WIDTH,DESK_WIDTH/FACTOR,-DESK_WIDTH,//0
DESK_WIDTH,-DESK_WIDTH/FACTOR,-DESK_WIDTH,//2
DESK_WIDTH,DESK_WIDTH/FACTOR,-DESK_WIDTH,//3
//上面xz
-DESK_WIDTH,DESK_WIDTH/FACTOR,-DESK_WIDTH,//0
-DESK_WIDTH,DESK_WIDTH/FACTOR,DESK_WIDTH,//1
DESK_WIDTH,DESK_WIDTH/FACTOR,DESK_WIDTH,//2
-DESK_WIDTH,DESK_WIDTH/FACTOR,-DESK_WIDTH,//0
DESK_WIDTH,DESK_WIDTH/FACTOR,DESK_WIDTH,//2
DESK_WIDTH,DESK_WIDTH/FACTOR,-DESK_WIDTH,//3
//下面
-DESK_WIDTH,-DESK_WIDTH/FACTOR,-DESK_WIDTH,//0
-DESK_WIDTH,-DESK_WIDTH/FACTOR,DESK_WIDTH,//1
DESK_WIDTH,-DESK_WIDTH/FACTOR,DESK_WIDTH,//2
-DESK_WIDTH,-DESK_WIDTH/FACTOR,-DESK_WIDTH,//0
DESK_WIDTH,-DESK_WIDTH/FACTOR,DESK_WIDTH,//2
DESK_WIDTH,-DESK_WIDTH/FACTOR,-DESK_WIDTH,//3
//左面yz
-DESK_WIDTH,DESK_WIDTH/FACTOR,-DESK_WIDTH,//0
-DESK_WIDTH,-DESK_WIDTH/FACTOR,-DESK_WIDTH,//1
-DESK_WIDTH,-DESK_WIDTH/FACTOR,DESK_WIDTH,//2
-DESK_WIDTH,DESK_WIDTH/FACTOR,-DESK_WIDTH,//0
-DESK_WIDTH,-DESK_WIDTH/FACTOR,DESK_WIDTH,//2
-DESK_WIDTH,DESK_WIDTH/FACTOR,DESK_WIDTH,//3
//右面
DESK_WIDTH,DESK_WIDTH/FACTOR,-DESK_WIDTH,//0
DESK_WIDTH,-DESK_WIDTH/FACTOR,-DESK_WIDTH,//1
DESK_WIDTH,-DESK_WIDTH/FACTOR,DESK_WIDTH,//2
DESK_WIDTH,DESK_WIDTH/FACTOR,-DESK_WIDTH,//0
DESK_WIDTH,-DESK_WIDTH/FACTOR,DESK_WIDTH,//2
DESK_WIDTH,DESK_WIDTH/FACTOR,DESK_WIDTH,//3
};
//顶点对应的颜色数组
float[] mColors = new float[]{//r,g,b,a
1,0,0,1,//红
1,0,0,1,
1,0,0,1,
1,0,0,1,
1,0,0,1,
1,0,0,1,
1,0,0,1,//红
1,0,0,1,
1,0,0,1,
1,0,0,1,
1,0,0,1,
1,0,0,1,
0,1,0,1,//绿
0,1,0,1,
0,1,0,1,
0,1,0,1,
0,1,0,1,
0,1,0,1,
0,1,0,1,//绿
0,1,0,1,
0,1,0,1,
0,1,0,1,
0,1,0,1,
0,1,0,1,
0,0,1,1,//蓝
0,0,1,1,
0,0,1,1,
0,0,1,1,
0,0,1,1,
0,0,1,1,
0,0,1,1,//蓝
0,0,1,1,
0,0,1,1,
0,0,1,1,
0,0,1,1,
0,0,1,1,
};
坐标变换:
我们一般画图之前肯定会把图形的中心点放在世界坐标系的中心点,这样对于方便于顶点取值,这就是所谓的物体坐标。当我们画多个物体时,若不进行坐标变化的话,那么所有的物体都在同一位置上,后画的就会遮挡先画的。所以在画之前,先对物体进行坐标变换,让它们处于不同的位置上,这就是所谓的“模型变换”。同理摄像机的缺省位置也是世界坐标系的中心,所以我们首先得移动摄像机的位置,这就是所谓的“视点变换”。由于我们的显示屏幕往往都是2D的,所以要靠投影来降低维度。投影的实际意义在于定义一个空间范围,称作“视景体”,视景体外的裁减掉,最终进入图像的是视景体内的部分,将图像显示于2D屏幕上,这种变换称为“投影变换”(其实后面还有几步,但我们可以简单的认为就这样)。
所以一个物体要显示到2D屏幕上,需要经过这几次变换:视点变换,模型变换,投影变换。
/**
* 获取具体物体的总变换矩阵
* mVMatrix 视点矩阵
* currMatrix 模型变换矩阵
* mProjMatrix 透视投影矩阵
* @return
*/
public static float[] getFinalMatrix() {
float[] mMVPMatrix = new float[16];
Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, currMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
return mMVPMatrix;
}
2.视角设置
在讲视角之前,先看两种投影:正交投影和透视投影。
图1.0
图1.1
简单来说,透视投影会有近大远小的效果,而正交投影则没有,是多大就多大。在3D世界中,使用的是透视投影。下面设置透视投影的方法。
/**
* 设置透视投影参数
*
* @param left
* 近平面的left
* @param right
* 近平面的right
* @param bottom
* 近平面的bottom
* @param top
* 近平面的top
* @param near
* 近平面距离
* @param far
* 远平面距离
*/
public static void setProjectFrustum(float left, float right, float bottom,
float top, float near, float far) {
Matrix.frustumM(mProjMatrix, 0, left, right, bottom, top, near, far);
}
下面接着讲解视角问题。举个简单的例子,生活中大家都拿过手机拍过照,同一个物体,你近距离拍它和远距离拍它效果肯定不同,而这个拍照的距离就是下面代码摄像机的位置;同样拍照时你的手机的朝向也会影响拍照效果,而这个方向就是下面代码摄像机目标点和UP向量来决定。
/**
* 设置摄像机
*
* @param cx
* 摄像机位置x
* @param cy
* 摄像机位置y
* @param cz
* 摄像机位置z
* @param tx
* 摄像机目标点x
* @param ty
* 摄像机目标点y
* @param tz
* 摄像机目标点z
* @param upx
* 摄像机UP向量X分量
* @param upy
* 摄像机UP向量Y分量
* @param upz
* 摄像机UP向量Z分量
*/
public static void setCamera(float cx, float cy, float cz, float tx,
float ty, float tz, float upx, float upy, float upz) {
Matrix.setLookAtM(mVMatrix, 0, cx, cy, cz, tx, ty, tz, upx, upy, upz);
}
3.着色器(Shader)
与OpenGL ES1.x渲染管线相比,OpenGL ES 2.0渲染管线中“顶点着色器”取代了OpenGL ES 1.x渲染管线中的“变换和光照”;“片元着色器”取代了OpenGL ES 1.x渲染管线中的“纹理环境和颜色求和”、“雾”以及“Alpha测试”。
顶点着色器:
uniform mat4 uMVPMatrix; //总变换矩阵
attribute vec3 aPosition; //顶点位置
attribute vec4 aColor; //顶点颜色
varying vec4 vColor;//用于传递给片元着色器的易变变量
void main(){
//根据总变换矩阵计算此次绘制此顶点位置
gl_Position = uMVPMatrix * vec4(aPosition,1);
//接受顶点颜色,传递给片元着色器
vColor = aColor;
}
片元着色器:
precision mediump float;
varying vec4 vColor;//接收顶点着色器传递过来的易变变量
void main(){
//给此片元赋颜色值
gl_FragColor = vColor;
}
运行效果图:
此节到此结束。图片资源来源《Android 3D游戏开发宝典——OpenGL ES 2.0》吴亚峰著。最后建议小伙伴们准备蓝皮书和红皮书,三者一起看会有意想不到的效果哦。
源码:http://git.oschina.net/zzero.pj/Puzzle
参考资料:
1.Shader: http://fengmm521.blog.163.com/blog/static/2509135820133254936364/
来源:oschina
链接:https://my.oschina.net/u/1257439/blog/364070