近期由于兴趣所向。開始学习OpenGL绘图。

本文以“画球体”为点,小结一下近期所学。

> 初识OpenGL ES

接触OpenGL是从Android開始的。众所周知,Android View 是线程不安全的,于是仅仅同意在主线程中对View进行操作。然而假如我们须要实现复杂的界面。特别是开发游戏,在主线程中画大量图像,会耗费比較长的时间。使得主线程没能及时响应用户输入,甚至出现ANR。

于是Android提供了一个 SurfaceView类,通过双缓冲机制(两块画布?三块画布?),同意用户非主线程操作Canvas。实现View的“异步”刷新。

Canvas类提供了非常多绘图方法:
drawPoint(...)
drawCircle(...)
drawBitmap(...)
drawRect(...)
drawText(...)
drawOval(...)

然后,假设想要实现比較复杂的效果(比方3D),Canvas就非常难胜任了。了解了一下,眼下大部分Android游戏都是用OpenGL来实现。

OpenGL是何方神圣?实际上,终于图像(无论是2D还是3D)都是显示在显示屏上,所以终于操作肯定是对一个2D的显示内存进行操作的。而OpenGL就是提供了非常多方法。帮助我们定义空间立体模型,然后通过我们输入的各种參数,计算出映射矩阵,终于在显示屏幕上体现出效果。

OpenGL ES (OpenGL for Embedded Systems)是专门OpenGL的API子集,专门用于手机等嵌入式平台。简单理解就是,专门开发给“低端”的环境。删减了非常多不必要的方法。留下了最主要的。

> 使用OpenGL ES绘图

OpenGL ES提供了两个方法去绘制空间几何图形。
1. glDrawArrays (int mode, int first, int count);
2. glDrawElements (int mode, int count, int type, Buffer indices);
參数mode有下面取值:
    GL_POINTS,
    GL_LINE_STRIP,
    GL_LINE_LOOP,
    GL_LINES,
    GL_TRIANGLES,
    GL_TRIANGLE_STRIP,
    GL_TRIANGLE_FAN.
画点,画线,画三角形。就这么多了。我们觉得,不论什么空间图形都能够由点,线,或者三角形来表示。

3. glVertexPointer( ... )
定义几何图形的全部顶点方法。调用此方法后,glDrawArrays,glDrawElements方法便会依照顶点画出图形。
由于我们接下来要画球体。是通过画非常多的三角形拼接而成(听起来挺有意思的)。所以先简单了解一下画三角形的三种模式:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWFya2V5MDk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" width="200" height="200" />

依据顶点的顺序,
GL_TRIANGLES按三个顶点为一组独自画三角形。
GL_TRIANGL_STRIP总是以最后三个顶点组成三角形,
GL_TRIANGLE_FAN则是以第一个顶点为中心。兴许顶点分别形成三角形。
我们接下来使用 GL_TRIANGLE_STRIP这样的模式画球体。

> 使用三角形构成空间球体

我们这里利用的是极限逼近的思想。想当年。祖冲之不也是用这样的思想计算出圆周率π吗。当正多边形的边数够多,看起来非常像一个圆!

于是,我们相同觉得,当正多面体的边数够多,看起来非常像一个球!
好了。思想是有了,可是我们终于并非通过画正多面体来画。由于看起来,利用正多面体来分割一个球算起来比較麻烦。
假设用经纬线的纵横分割方法,算起来要简单非常多!
左右两条经线。上下两条纬线构成一个正方形(近似)。正方形能够看做是两个三角形构成。
途中土黄色的箭头,代表使用GL_TRIANGLE_STRIP模式绘图时採用的顶点顺序。
这样的分割方法,看起来清晰非常多,纵横经纬两层循环遍历全部顶点。
关键是:怎么计算球面的顶点坐标?(x, y, z)

> 球面顶点坐标计算

首先,我们确认两个遍历方向:
第一层:从Y轴负方向開始,角度不断添加直到Y轴正方向。(时钟6点->5点->4点->3点->2点->1点->12点)
第二层:固定Y值,以Y轴为旋转轴,360度旋转。就可以遍历全部顶点。
例如以下图。a角递增。b角做一个360度变化。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWFya2V5MDk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />

如上图。随意球面上的点。三维坐标 (x0, y0, z0) 计算:(R为球半径)
x0 = R * cos(a) * sin(b);
y0 = R * sin(a);
z0 = R * cos(a) * cos(b);

> 源代码

下面部分參考或者是抄写于:http://blog.csdn.net/wuzongpo/article/details/7230285
使用OpenGL ES绘图的一般步骤是:
1,获取EGLDisplay对象
2,初始化与EGLDisplay之间的连接
3,获取EGLConfig对象
4,创建EGLContext对象
5。创建EGLSurface实例
6。连接EGLContext与EGLSurface
7,使用GL指令绘图
8,断开释放EGLContext对象
9,删除EGLSurface
10,删除EGLContext
11。终止与EGLDisplay之间的连接
Android GLSurfaceView 类,对OpenGL Api 进行了一层封装。帮忙我们管理Display,Context,Surface。我们仅仅要实现android.opengl.GLSurfaceView.Renderer接口就可以。
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer; import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10; import android.opengl.GLU;
import android.opengl.GLSurfaceView.Renderer; public class OpenGLRenderer4 implements Renderer { // 环境光
private final float[] mat_ambient = { 0.2f, 0.3f, 0.4f, 1.0f };
private FloatBuffer mat_ambient_buf;
// 平行入射光
private final float[] mat_diffuse = { 0.4f, 0.6f, 0.8f, 1.0f };
private FloatBuffer mat_diffuse_buf;
// 高亮区域
private final float[] mat_specular = { 0.2f * 0.4f, 0.2f * 0.6f, 0.2f * 0.8f, 1.0f };
private FloatBuffer mat_specular_buf; private Sphere mSphere = new Sphere(); public volatile float mLightX = 10f;
public volatile float mLightY = 10f;
public volatile float mLightZ = 10f; @Override
public void onDrawFrame(GL10 gl) {
// 清晰屏幕和深度缓存
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// 重置当前的模型观察矩阵
gl.glLoadIdentity(); gl.glEnable(GL10.GL_LIGHTING);
gl.glEnable(GL10.GL_LIGHT0); // 材质
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, mat_ambient_buf);
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, mat_diffuse_buf);
gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR, mat_specular_buf);
// 镜面指数 0~128 越小越粗糙
gl.glMaterialf(GL10.GL_FRONT_AND_BACK, GL10.GL_SHININESS, 96.0f); //光源位置
float[] light_position = {mLightX, mLightY, mLightZ, 0.0f};
ByteBuffer mpbb = ByteBuffer.allocateDirect(light_position.length*4);
mpbb.order(ByteOrder.nativeOrder());
FloatBuffer mat_posiBuf = mpbb.asFloatBuffer();
mat_posiBuf.put(light_position);
mat_posiBuf.position(0);
gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, mat_posiBuf); gl.glTranslatef(0.0f, 0.0f, -3.0f);
mSphere.draw(gl);
} @Override
public void onSurfaceChanged(GL10 gl, int width, int height) { // 设置输出屏幕大小
gl.glViewport(0, 0, width, height); // 设置投影矩阵
gl.glMatrixMode(GL10.GL_PROJECTION);
// 重置投影矩阵
gl.glLoadIdentity();
// 设置视口大小
// gl.glFrustumf(0, width, 0, height, 0.1f, 100.0f); GLU.gluPerspective(gl, 90.0f, (float) width / height, 0.1f, 50.0f); // 选择模型观察矩阵
gl.glMatrixMode(GL10.GL_MODELVIEW);
// 重置模型观察矩阵
gl.glLoadIdentity(); } @Override
public void onSurfaceCreated(GL10 gl, EGLConfig arg1) {
// 对透视进行修正
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
// 背景:黑色
gl.glClearColor(0, 0.0f, 0.0f, 0.0f);
// 启动阴影平滑
gl.glShadeModel(GL10.GL_SMOOTH); // 复位深度缓存
gl.glClearDepthf(1.0f);
// 启动深度測试
gl.glEnable(GL10.GL_DEPTH_TEST);
// 所做深度測试的类型
gl.glDepthFunc(GL10.GL_LEQUAL); initBuffers();
} private void initBuffers() {
ByteBuffer bufTemp = ByteBuffer.allocateDirect(mat_ambient.length * 4);
bufTemp.order(ByteOrder.nativeOrder());
mat_ambient_buf = bufTemp.asFloatBuffer();
mat_ambient_buf.put(mat_ambient);
mat_ambient_buf.position(0); bufTemp = ByteBuffer.allocateDirect(mat_diffuse.length * 4);
bufTemp.order(ByteOrder.nativeOrder());
mat_diffuse_buf = bufTemp.asFloatBuffer();
mat_diffuse_buf.put(mat_diffuse);
mat_diffuse_buf.position(0); bufTemp = ByteBuffer.allocateDirect(mat_specular.length * 4);
bufTemp.order(ByteOrder.nativeOrder());
mat_specular_buf = bufTemp.asFloatBuffer();
mat_specular_buf.put(mat_specular);
mat_specular_buf.position(0);
}
}

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer; import javax.microedition.khronos.opengles.GL10; // 计算球面顶点
public class Sphere { public void draw(GL10 gl) { float angleA, angleB;
float cos, sin;
float r1, r2;
float h1, h2;
float step = 30.0f;
float[][] v = new float[32][3];
ByteBuffer vbb;
FloatBuffer vBuf; vbb = ByteBuffer.allocateDirect(v.length * v[0].length * 4);
vbb.order(ByteOrder.nativeOrder());
vBuf = vbb.asFloatBuffer(); gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_NORMAL_ARRAY); for (angleA = -90.0f; angleA < 90.0f; angleA += step) {
int n = 0; r1 = (float)Math.cos(angleA * Math.PI / 180.0);
r2 = (float)Math.cos((angleA + step) * Math.PI / 180.0);
h1 = (float)Math.sin(angleA * Math.PI / 180.0);
h2 = (float)Math.sin((angleA + step) * Math.PI / 180.0); // 固定纬度, 360 度旋转遍历一条纬线
for (angleB = 0.0f; angleB <= 360.0f; angleB += step) { cos = (float)Math.cos(angleB * Math.PI / 180.0);
sin = -(float)Math.sin(angleB * Math.PI / 180.0); v[n][0] = (r2 * cos);
v[n][1] = (h2);
v[n][2] = (r2 * sin);
v[n + 1][0] = (r1 * cos);
v[n + 1][1] = (h1);
v[n + 1][2] = (r1 * sin); vBuf.put(v[n]);
vBuf.put(v[n + 1]); n += 2; if(n>31){
vBuf.position(0); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vBuf);
gl.glNormalPointer(GL10.GL_FLOAT, 0, vBuf);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, n); n = 0;
angleB -= step;
} }
vBuf.position(0); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vBuf);
gl.glNormalPointer(GL10.GL_FLOAT, 0, vBuf);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, n);
} gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
}
}
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.view.MotionEvent; public class OpenGLView extends GLSurfaceView { private OpenGLRenderer4 mRenderer; private float mDownX = 0.0f;
private float mDownY = 0.0f; public OpenGLView(Context context) {
super(context); mRenderer = new OpenGLRenderer4();
this.setRenderer(mRenderer);
} @Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN:
mDownX = event.getX();
mDownY = event.getY();
return true;
case MotionEvent.ACTION_UP:
return true;
case MotionEvent.ACTION_MOVE:
float mX = event.getX();
float mY = event.getY();
mRenderer.mLightX += (mX-mDownX)/10;
mRenderer.mLightY -= (mY-mDownY)/10;
mDownX = mX;
mDownY = mY;
return true;
default:
return super.onTouchEvent(event);
}
}
}

import android.os.Bundle;
import android.app.Activity;
import android.view.Window;
import android.view.WindowManager; public class MainActivity extends Activity { private OpenGLView mOpenGLView; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 去标题栏
requestWindowFeature(Window.FEATURE_NO_TITLE);
//设置全屏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); mOpenGLView = new OpenGLView(this);
setContentView(mOpenGLView);
}
}

> 效果图

step = 30.0f

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWFya2V5MDk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" width="454" height="806" alt="" />

step = 2.0f
关于光照效果。我们以后有空再讨论。

Android OpenGL ES 画球体的更多相关文章

  1. Android OpenGL ES 开发教程 从入门到精通

    感谢,摘自:http://blog.csdn.net/mapdigit/article/details/7526556 Android OpenGL ES 简明开发教程 Android OpenGL ...

  2. [工作记录] Android OpenGL ES: non-square texture - continue

    previous: [工作记录] Android OpenGL ES 2.0: square texture not supported on some device recently I found ...

  3. Android OpenGL ES(十三)通用的矩阵变换指令 .

    Android OpenGL ES 对于不同坐标系下坐标变换,大都使用矩阵运算的方法来定义和实现的.这里介绍对应指定的坐标系(比如viewmodel, projection或是viewport) An ...

  4. Android OpenGL ES(十二):三维坐标系及坐标变换初步 .

    OpenGL ES图形库最终的结果是在二维平面上显示3D物体(常称作模型Model)这是因为目前的打部分显示器还只能显示二维图形.但我们在构造3D模型时必须要有空间现象能力,所有对模型的描述还是使用三 ...

  5. Android OpenGL ES(八)绘制点Point ..

    上一篇介绍了OpenGL ES能够绘制的几种基本几何图形:点,线,三角形.将分别介绍这几种基本几何图形的例子.为方便起见,暂时在同一平面上绘制这些几何图形,在后面介绍完OpenGL ES的坐标系统和坐 ...

  6. Android OpenGL ES .介绍

    引自:http://blog.csdn.net/hgl868/article/details/6971624 1.    OpenGL ES 简介 Android 3D引擎采用的是OpenGL ES. ...

  7. Android OpenGL ES(七)基本几何图形定义 .

    在前面Android OpenGL ES(六):创建实例应用OpenGLDemos程序框架 我们创建了示例程序的基本框架,并提供了一个“Hello World”示例,将屏幕显示为红色. 本例介绍Ope ...

  8. Android OpenGL ES(六)创建实例应用OpenGLDemos程序框架 .

    有了前面关于Android OpenGL ES的介绍,可以开始创建示例程序OpenGLDemos. 使用Eclipse 创建一个Android项目 Project Name: OpenGLDemos ...

  9. Android OpenGL ES(五)GLSurfaceView .

    Android OpenGL ES 相关的包主要定义在 javax.microedition.khronos.opengles    GL 绘图指令 javax.microedition.khrono ...

随机推荐

  1. 需要了解的几个Java基础点

    关键字 native:表示要调用非Java语言写函数,比如用C语言使用JNI实现的接口.比如windows环境的dll文件.举例:Object.hashcode() 位运算 << n:左移 ...

  2. Django易混淆问题

    1.Django本身提供了runserver 为什么不能用来 部署 runserver 方法是调试 Django 时经常用到的运行方式,它使用 Django 自带的  WSGI Server 运行,主 ...

  3. Python Numpy Array

    Numpy 是Python中数据科学中的核心组件,它给我们提供了多维度高性能数组对象. Arrays Numpy.array   dtype 变量 dtype变量,用来存放数据类型, 创建数组时可以同 ...

  4. 从c到cpp对static 关键字的总结 需要整理下!!!!!!!!!!!!!!!!!!!!!!

    一个完整的程序,在内存中的分布情况如下: 具体分布图     自己看书去!!!!!1.栈区: 由编译器自动分配释放,像局部变量,函数参数,都是在栈区.会随着作用于退出而释放空间.3.堆区:程序员分配并 ...

  5. 数据结构---Java---HashSet

    1.概述 1.1 HashSet不是线程安全的: 1.2 当向HashSet存入元素时,调用该对象的hashCode()值,根据hashCode()值来决定元素的存储位置: 如果hashCode()值 ...

  6. CSAPP阅读笔记-链接-第七章-P464-P500

    链接概述 经预处理器->编译器->汇编器处理后,源文件可被转化为一组可重定位目标文件,链接器将它们组合起来形成可执行文件. 每个可重定位目标文件由不同的“代码节”和“数据节”组成,每一个节 ...

  7. 深入理解C语言函数指针(转)

    本文转自:http://www.cnblogs.com/windlaughing/archive/2013/04/10/3012012.html 示例1: void myFun(int x); //声 ...

  8. Linux的cron服务

    可以用以下命令启动和停止服务: /sbin/service crond start /sbin/service crond stop /sbin/service crond restart /sbin ...

  9. IAR6.1的工程迁移到IAR6.5不能用的解决方法

    1.重命名过时的CMSIS头文件 "... \ CMSIS \ CM3 \ CoreSupport \ core_cm3.h  比如:core_cm3.h.old 2.启用CMSIS:项目- ...

  10. JDBC之Java连接mysql实现增删改查

    使用软件:mysql.eclipse 链接步骤: 1.注册驱动 2.创建一个连接对象 3.写sql语句 4.执行sql语句并返回一个结果或者结果集 5.关闭链接(一般就是connection.stat ...