一.OpenGLES2.0原理浅析
1. OpenGLES通过获取物体的控件顶点信息, 可以实现3维物体到2维物体的坐标转换输出到屏幕,并且可以对物体实现平移,旋转,缩放,而且利用着色器可编程渲染通道实现多种光学作用, 特殊形状变换等效果。从而能实现丰富的物体再现功能。
2. 通过模拟人眼观察物体, 可能引起屏幕图像改变的几种行为和分别对应的几种OpenGL变换:
每种变换都可以由相应的Matrix静态方法生成相应的变换矩阵。 坐标系原点在屏幕中央,z轴正方向垂直屏幕向外。
行为 | 变换 | 记录变换方式的放法 | 记录变换方式的[4*4]矩阵 |
眼睛相对物体的位置改变 | 视图变换 | Matrix.setLookAtM(mVMatrix, 0,//偏移量 cx, cy, cz,//相机位置, tx, ty, tz,//观察点位置 upx, upy, upz//顶部朝向 ) | mVMatrix[16] |
物体平移 | 模型变换 | Matrix.translateM( mMMatrix, 0,//偏移量 x, y, z//平移量 ) | mMMatrix[16] |
物体按坐标缩放比例缩放 | Matrix.scaleM( mMMatrix, sx,sy, sz//缩放因子 ) | ||
物体旋转 | Matrix.rotateM( mMMatrix, 0,//偏移量 angle,//旋转角度 x, y, z//需要旋转的轴 ) | ||
凸透镜眼睛 | 投影变换 | Matrix.frustumM( mPMatrix, 0,//偏移量 left,right, buttom,top, near,far//near>0 ) | mPMatrix[16] |
平面透镜眼睛 | Matrix.orthoM( mPMatrix, 0,//偏移量 left,right, buttom,top, near,far//near>0 ) |
PS: 如果眼睛不是凸透镜而是平面透镜, 则不会产生近小远大的效果, 视图变换可由人眼模拟。函数参数都是float。
变换叠加: 可通过变换矩阵相乘得出想要的变换矩阵: Matrix.multiplyMM(
mMVPMatrix, 存放结果的总变换矩阵
0, 结果矩阵偏移量
0, 左矩阵偏移量
0 右矩阵偏移量
变换技巧: 利用栈存储变换矩阵的算法可实现复杂场景的图形变换和提高渲染效率。
变换顺序: 矩阵相乘是讲究顺序的, 比如先平移再缩放 和 先缩放再平移的效果是不同的。
3. 绘制方式
点 | GLES20.GL_POINTS |
线 | GLES20.GL_LINES |
GLES20.GL_LINE_STRIP | |
GLES20.GL_LINE_LOOP | |
面 | GLES20.TRIANGLES |
GLES20.TRIANGLE_TRIP//条带,可间断 | |
GLES20.TRIANGLE_FAN//扇面 |
非索引法: GLES20.glDrawArrays(GLES20.GL_POINTS, 0, vCount);
索引法 :GLES20.glDrawElements(GLES20.GL_POINTS, iCount, GLES20.GL_UNSIGNED_BYTE,
二.android的openGLES2.0程序设计框架浅析
1.主要实现流程有如下顺序的几点操作:
定义物体顶点信息 –> 编辑着色器代码 –> 编译连接着色器程序 -> 物体顶点信息传入着色器 –> GLSurfaceView加载Renderer –> Activity加载GLSurfaceView -> 渲染物体输出屏幕
操作名称 | 一般实现方法 |
定义物体顶点信息 | 顶点信息一般包括: (1)三维坐标信息; (2)三维法向量信息;(3)颜色信息或二维贴图坐标信息; 复杂物体的坐标通常通过特定的软件绘制生成顶点信息文件,然后进行解读; |
编辑着色器代码 | 着色器语言编写的代码, 通常后缀名.sh, 语法和C相似95%以上,是OpenGLES2.0的可编程渲染通道, 增加了编程难度, 但是能实现更绚丽多彩的效果。 一般编写好的着色器代码放在android工程目录下的assets文件, assets常用轻量资源的读写。 |
编译连接着色器 | 从assets获取着色器脚本, 并返回脚本字符串的过程: 主要方法:(1. InputStream in = rgetAssets .open(fname); 转化为 ByteArrayOutputStream out (3. byte[] bout = out.toByteArray(); 记得close in&out \\r\\n, “\n”); 加载片元着色器和顶点着色器的方法: (1. 申请特定着色器 int shaderId = GLES20.glCreateShader(shaderType);// shaderType GLES20.GL_VERTEX_SHADER(顶点) 片元) 如果申请成功则返回的shaderId不为零 (2. 给申请成功的着色器加载脚本并编译,查错 GLES20.glShaderSource(shaderId, source);/// source是脚本字符串 GLES20.glCompileShader(shaderId);///编译 int compiles[1]; GLES20.glGetShaderiv(shaderId, GLES20._GL_COMPILE_STATUS, compiles, 0);///查看编译情况 if(compiles[0] == 0){//编译失败,释放申请的着色器
} 编译连接并生成着色器程序: (1. 申请着色器程序:int program = GLES20.glCreateProgram(); (2. 加入两种着色器并查错(方法一样):
while((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR){
} (3. 连接着色器程序,并查看连接情况 GLES20.glLinkProgram(program); int LinkStatus[1]; GLES20.glGetPromgramiv(program, GLES20.GL_LINK_STATUS, LinkStatus, 0); if(LinkStatus[0]!= GLES20.GL_TRUE){ GLES20.glDeletePromgram(program); program = 0; } 至此可以利用program进行该着色器的利用。 |
物体顶点信息传入着色器 | 生成顶点信息, 用FloatBuffer类存储的一般方法:
一个float 4个字节 前期设置 作为中介开辟FloatBuffer vertexBuffer.put(vertexs); 存入信息 vertexBuffer.position(0); 设置起始位置为0; 获取着色器参数ID
传入数据 GLES20.glVertexAttribPointer( maPositionHandle,
GLES20.GL_FLOAT, false, 3*4,
使用数据:GLES20.glEnableVertexAttribArray(maPositionHandle); |
GLSurfaceView加载Renderer | 初始化GLSurfaceView setEGLContextClientVersion(2);//设置版本 setRanderer(new MyRenderer());//增添渲染器 setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);//主动渲染模式 自定义渲染器,实现3个渲染接口,例如: private class MyRenderer implements GLSurfaceView.Renderer{ tle; public void onDrawFrame(GL10 gl)
//清除深度缓冲与颜色缓冲 GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); //绘制三角形对 tle.drawSelf();
public void onSurfaceChanged(GL10 gl, int width, int height)
//设置视窗大小及位置 GLES20.glViewport(0, 0, width, height); //计算GLSurfaceView的宽高比 float ratio = (float) width / height; //调用此方法计算产生透视投影矩阵 Matrix.frustumM(Triangle.mProjMatrix, 0, -ratio, ratio, -1, 1, 1, 100); //调用此方法产生摄像机9参数位置矩阵 Matrix.setLookAtM(Triangle.mVMatrix, 0, 0,0,3,0f,0f,0f,0f,1.0f,0.0f);
public void onSurfaceCreated(GL10 gl, EGLConfig config)
//设置屏幕背景色RGBA GLES20.glClearColor(0,0,0,1.0f); //创建三角形对对象 tle=new Triangle(MyTDView.this); //打开深度检测 GLES20.glEnable(GLES20.GL_DEPTH_TEST); rthread=new RotateThread(); rthread.start();
}
|