OpenGL ES 2.0入手

一曲冷凌霜 提交于 2020-11-24 13:24:21

    本人从事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),我个人觉得可以再这几点来把握:

  1. 坐标变换。

  2. 视角设置。

  3. 着色器(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/

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!