Android OpenGL ES 画球体
近期由于兴趣所向。開始学习OpenGL绘图。
本文以“画球体”为点,小结一下近期所学。
> 初识OpenGL ES
于是Android提供了一个 SurfaceView类,通过双缓冲机制(两块画布?三块画布?),同意用户非主线程操作Canvas。实现View的“异步”刷新。
然后,假设想要实现比較复杂的效果(比方3D),Canvas就非常难胜任了。了解了一下,眼下大部分Android游戏都是用OpenGL来实现。
OpenGL是何方神圣?实际上,终于图像(无论是2D还是3D)都是显示在显示屏上,所以终于操作肯定是对一个2D的显示内存进行操作的。而OpenGL就是提供了非常多方法。帮助我们定义空间立体模型,然后通过我们输入的各种參数,计算出映射矩阵,终于在显示屏幕上体现出效果。
OpenGL ES (OpenGL for Embedded Systems)是专门OpenGL的API子集,专门用于手机等嵌入式平台。简单理解就是,专门开发给“低端”的环境。删减了非常多不必要的方法。留下了最主要的。
> 使用OpenGL ES绘图
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWFya2V5MDk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" width="200" height="200" />
> 使用三角形构成空间球体

> 球面顶点坐标计算
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWFya2V5MDk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />
> 源代码
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);
}
}
> 效果图
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWFya2V5MDk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" width="454" height="806" alt="" />
Android OpenGL ES 画球体的更多相关文章
- Android OpenGL ES 开发教程 从入门到精通
感谢,摘自:http://blog.csdn.net/mapdigit/article/details/7526556 Android OpenGL ES 简明开发教程 Android OpenGL ...
- [工作记录] Android OpenGL ES: non-square texture - continue
previous: [工作记录] Android OpenGL ES 2.0: square texture not supported on some device recently I found ...
- Android OpenGL ES(十三)通用的矩阵变换指令 .
Android OpenGL ES 对于不同坐标系下坐标变换,大都使用矩阵运算的方法来定义和实现的.这里介绍对应指定的坐标系(比如viewmodel, projection或是viewport) An ...
- Android OpenGL ES(十二):三维坐标系及坐标变换初步 .
OpenGL ES图形库最终的结果是在二维平面上显示3D物体(常称作模型Model)这是因为目前的打部分显示器还只能显示二维图形.但我们在构造3D模型时必须要有空间现象能力,所有对模型的描述还是使用三 ...
- Android OpenGL ES(八)绘制点Point ..
上一篇介绍了OpenGL ES能够绘制的几种基本几何图形:点,线,三角形.将分别介绍这几种基本几何图形的例子.为方便起见,暂时在同一平面上绘制这些几何图形,在后面介绍完OpenGL ES的坐标系统和坐 ...
- Android OpenGL ES .介绍
引自:http://blog.csdn.net/hgl868/article/details/6971624 1. OpenGL ES 简介 Android 3D引擎采用的是OpenGL ES. ...
- Android OpenGL ES(七)基本几何图形定义 .
在前面Android OpenGL ES(六):创建实例应用OpenGLDemos程序框架 我们创建了示例程序的基本框架,并提供了一个“Hello World”示例,将屏幕显示为红色. 本例介绍Ope ...
- Android OpenGL ES(六)创建实例应用OpenGLDemos程序框架 .
有了前面关于Android OpenGL ES的介绍,可以开始创建示例程序OpenGLDemos. 使用Eclipse 创建一个Android项目 Project Name: OpenGLDemos ...
- Android OpenGL ES(五)GLSurfaceView .
Android OpenGL ES 相关的包主要定义在 javax.microedition.khronos.opengles GL 绘图指令 javax.microedition.khrono ...
随机推荐
- 对Vue.js的认知
一.什么是MVVM? MVVM是Model-View-ViewModel的缩写.MVVM是一种设计思想.Model 层代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑:View 代表UI ...
- JAVA中的for循环
在Java程序中,要“逐一处理”――或者说,“遍历”――某一个数组或Collection中的元素的时候,一般会使用一个for循环来实现(当 然,用其它种类的循环也不是不可以,只是不知道是因为for这个 ...
- is 和 == 区别
== 和 is 的区别 == 比较 比较的是两个值 适用于 列表a = '[1:2]'b = '[1:2]'print(a == b) #True 字典a = '{1,2,3}'b = '{ ...
- [转]jQuery Mobile动态刷新页面样式
本文转自:http://blog.csdn.net/zht666/article/details/8560765 当我们使用Ajax或者javascript动态在页面上添加元素后,如添加select控 ...
- ubuntu下安装录屏软件
sudo add-apt-repository ppa:maarten-baert/simplescreenrecorder sudo apt-get update sudo apt-get inst ...
- stm32串口学习(二)
今天继续学习stm32的串口编程(利用库函数).上次我们说了串口的发送,这次我们说接收. 接收可以用查询的方法,也可以用中断.显然,工程中多用中断的方式,那么就来看看中断接收. 代码其实很简单,基本的 ...
- JEECMS站群管理系统-- Jeecms安装过程
Jeecms是基于java技术研发的站群管理系统,稳定.安全.高效.跨平台.无限扩展是jeecms 的优点,系统支持mysql.oracle.sqlserver.db2等主流数据库. 轻松建设大规模网 ...
- SSH密钥登录原理
Client 发送请求 login 请求 --> Server 接受请求 --> 根据 authorized_key 文件中的对应 Client 的 ip 地址的公钥对一串随机数进行加密 ...
- POJ 3660—— Cow Contest——————【Floyd传递闭包】
Cow Contest Time Limit:1000MS Memory Limit:65536KB 64bit IO Format:%I64d & %I64u Submit ...
- 纯CSS控制背景图片100%自适应填充布局
https://blog.csdn.net/wd4java/article/details/50537562 解决: html,body{height: 100%;width: 100%;marg ...