关键字: android OpenGL 移动开发 教程

到目前为止,我们已经学会了如何创建3D物体,给物体着色,在空间中打开灯光,给物体贴上纹理等基本功能。并有能力创建一个旋转的立方体或者一群闪烁的星星了,对3D编程也有了一定的了解。

这一课,我们将演示如何加载3D世界,并在3D世界中遨游。为此我们采用一个文本文件来定义我们的3D世界。这样可以方便地加载不同的3D世界定义文件,来得到不同的3D世界。

数据文件中每个三角形都以如下形式声明:
        X1 Y1 Z1 U1 V1
        X2 Y2 Z2 U2 V2
        X3 Y3 Z3 U3 V3

保存在world .txt文件中。

类MyWorld方法loadWorld加载这个文件。

MyWorld.java

package wintop.gllesson10;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnKeyListener;
import android.view.View.OnTouchListener;
import android.content.Context;
import android.util.Log;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLUtils;
public class MyWorld implements OnKeyListener, OnTouchListener {
// 纹理指针
private int[] textures = new int[3];
// 上下文句柄
private Context context;
// 3D 世界
private Sector sector1;
// 下面这些变量用于在3D世界中导航和头像的导航
private final float piover180 = 0.0174532925f;
private float heading;
private float xpos;
private float zpos;
private float yrot;                 //Y 旋转
private float walkbias = 0;
private float walkbiasangle = 0;
private float lookupdown = 0.0f;
// 用于输入控制的变量和因子
private float oldX;
private float oldY;
private final float TOUCH_SCALE = 0.2f;    //
// 顶点缓冲区
private FloatBuffer vertexBuffer;
// 纹理坐标缓冲区
private FloatBuffer textureBuffer;
// 最初的顶点定义
private float[] vertices;
// 纹理坐标(u, v)
private float[] texture;
// 构造函数
public MyWorld(Context context) {
this.context = context;
}
// 加载我们的3D世界
// 参数fileName 从assets目录中加载的文件名
public void loadWorld(String fileName) {
try {
// 一些临时变量
int numtriangles = 0;
int counter = 0;
sector1 = new Sector();
List<String> lines = null;
StringTokenizer tokenizer;
//用于输入文件的快速阅读器
InputStream in_stream = this.context.getAssets().open(fileName);
BufferedReader reader = new BufferedReader(new InputStreamReader(in_stream));
//迭代读取所有的行
String line = null;
while((line = reader.readLine()) != null) {
//跳过注释和空行
if(line.startsWith("//") || line.trim().equals("")) {
continue;
}
//读取这个文件包含多少个三角形
if(line.startsWith("NUMPOLLIES")) {
numtriangles = Integer.valueOf(line.split(" ")[1]);
sector1.num_triangles = numtriangles;
sector1.triangle = new Triangle[sector1.num_triangles];
//读所有的其他行
} else {
if(lines == null) {
lines = new ArrayList<String>();
}
lines.add(line);
}
}
//清理
reader.close();
//现在迭代分析所有的行
for(int loop = 0; loop < numtriangles; loop++) {
//...定义三角形...
Triangle triangle = new Triangle();
//...然后yoga读取的五个点值赋给三角形的顶点
for(int vert = 0; vert < 3; vert++) {
//
line = lines.get(loop * 3 + vert);
tokenizer = new StringTokenizer(line);
//
triangle.vertex[vert] = new Vertex();
//
triangle.vertex[vert].x = Float.valueOf(tokenizer.nextToken());
triangle.vertex[vert].y = Float.valueOf(tokenizer.nextToken());
triangle.vertex[vert].z = Float.valueOf(tokenizer.nextToken());
triangle.vertex[vert].u = Float.valueOf(tokenizer.nextToken());
triangle.vertex[vert].v = Float.valueOf(tokenizer.nextToken());
}
//最后,添加这些三角形到sector
sector1.triangle[counter++] = triangle;
}
//如果什么都没发生, 则写一个日志然后返回
} catch(Exception e) {
Log.e("World", "Could not load the World file!", e);
return;
}
// 现在将顶点和纹理坐标分离出来
vertices = new float[sector1.num_triangles * 3 * 3];
texture = new float[sector1.num_triangles * 3 * 2];
int vertCounter = 0;
int texCounter = 0;
for(Triangle triangle : sector1.triangle) {
//
for(Vertex vertex : triangle.vertex) {
//
vertices[vertCounter++] = vertex.x;
vertices[vertCounter++] = vertex.y;
vertices[vertCounter++] = vertex.z;
//
texture[texCounter++] = vertex.u;
texture[texCounter++] = vertex.v;
}
}
// 建立缓冲区
ByteBuffer byteBuf = ByteBuffer.allocateDirect(vertices.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
vertexBuffer = byteBuf.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
byteBuf = ByteBuffer.allocateDirect(texture.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
textureBuffer = byteBuf.asFloatBuffer();
textureBuffer.put(texture);
textureBuffer.position(0);
}
// 绘图
public void draw(GL10 gl, int filter) {
// 根据给定的滤波方式绑定纹理
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[filter]);
float xtrans = -xpos;                      //用于游戏者沿X轴平移时的大小
float ztrans = -zpos;                      //用于游戏者沿Z轴平移时的大小
float ytrans = -walkbias - 0.25f;          //用于头部的上下摆动
float sceneroty = 360.0f - yrot;           //位于游戏者方向的360度角
//视图
gl.glRotatef(lookupdown, 1.0f, 0, 0);       //上下旋转
gl.glRotatef(sceneroty, 0, 1.0f, 0);        //根据游戏者正面所对方向所作的旋转
gl.glTranslatef(xtrans, ytrans, ztrans);    //以游戏者为中心的平移场景
//使能顶点,纹理和法向状态
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
// 设置缓冲区数据指针
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
// 绘制所有的三角形
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, vertices.length / 3);
// 返回前恢复原来的状态
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}
// 加载纹理
public void loadGLTexture(GL10 gl, Context context) {
// 从Android的资源目录获取纹理
InputStream is = context.getResources().openRawResource(R.drawable.mud);
Bitmap bitmap = null;
try {
//BitmapFactory 是 Android 图形库中处理图像的工具
bitmap = BitmapFactory.decodeStream(is);
} finally {
//总是要清理和关闭
try {
is.close();
is = null;
} catch (IOException e) {
}
}
// 生成纹理指针
gl.glGenTextures(3, textures, 0);
// 生成Nearest方式的滤波纹理并绑定到纹理0
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
// 生成Linear方式的滤波纹理并绑定到纹理1
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[1]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
// 生成mipmapped方式的滤波纹理并绑定到纹理2
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[2]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR_MIPMAP_NEAREST);
// 在Android系统SDK中,1.0版本没有buildMipMap类似的函数,因此这里
// 检查OpenGL实例的版本,如果为1.1。则使用系统提供的方法,否在使用自定义的方法
if(gl instanceof GL11) {
gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_GENERATE_MIPMAP, GL11.GL_TRUE);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
//
} else {
buildMipmap(gl, bitmap);
}
//清理
bitmap.recycle();
}
// 建立自己的mipmap工具,总是按1/2的比例来缩小原图作为我们新的mipmap纹理层次。
private void buildMipmap(GL10 gl, Bitmap bitmap) {
//
int level = 0;
//
int height = bitmap.getHeight();
int width = bitmap.getWidth();
//
while(height >= 1 || width >= 1) {
//首先, 根据位图生成纹理并设置器对应的层次
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, bitmap, 0);
if(height == 1 || width == 1) {
break;
}
//增加 mipmap 层次
level++;
height /= 2;
width /= 2;
Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true);
//清理
bitmap.recycle();
bitmap = bitmap2;
}
}
/ 用于3D世界的一些结构 //
// 一个包含纹理坐标的经典的Vertex类
public class Vertex {
public float x,y,z;
public float u,v;
}
// Triangle类,包含三角形的所有顶点
public class Triangle {
public Vertex[] vertex = new Vertex[3];
}
// Sector类,保存我们的3D世界的所有三角形的个数和所有三角形
public class Sector {
public int num_triangles;
public Triangle[] triangle;
}
// ===== 处理监听事件 ===== //
// 重载键盘监听器以监听键盘事件
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// 处理键盘按下的事件
if(event.getAction() == KeyEvent.ACTION_DOWN)
{
return onKeyDown(keyCode, event);
}
return false;
}
/**
* 检查 DPad 按下 左, 右, 上  和  下按钮.
* 然后按相应的方向行走或者旋转头部.
*
* @参数 keyCode - 键值
* @参数 event - 键事件
* @返回值 事件处理了则返回true
*/
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
heading += 1.0f;
yrot = heading;                    // 向左旋转场景
} else if(keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
heading -= 1.0f;
yrot = heading;                    //向右侧旋转场景
} else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
xpos -= (float)Math.sin(heading * piover180) * 0.05f;  //沿游戏者所在的X平面移动
zpos -= (float)Math.cos(heading * piover180) * 0.05f;  //沿游戏者所在的Z平面移动
if(walkbiasangle >= 359.0f) {                           //如果walkbiasangle大于359度
walkbiasangle = 0.0f;                              //将 walkbiasangle 设为0
} else {
walkbiasangle += 10;                              //如果 walkbiasangle < 359 ,则增加 10
}
walkbias = (float)Math.sin(walkbiasangle * piover180) / 20.0f; //使游戏者产生跳跃感
} else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
xpos += (float)Math.sin(heading * piover180) * 0.05f; //沿游戏者所在的X平面移动
zpos += (float)Math.cos(heading * piover180) * 0.05f; //沿游戏者所在的Z平面移动
if(walkbiasangle <= 1.0f) {                             //如果walkbiasangle小于1度
walkbiasangle = 359.0f;                                //使 walkbiasangle 等于 359
} else {
walkbiasangle -= 10;                               //如果 walkbiasangle > 1 减去 10
}
walkbias = (float)Math.sin(walkbiasangle * piover180) / 20.0f; //使游戏者产生跳跃感
}
//事件处理完成后
return true;
}
/**
* 触屏方式处理.
*/
@Override
public boolean onTouch(View v, MotionEvent event) {
boolean handled = false;
float x = event.getX();
float y = event.getY();
//如果在屏幕上移动
if(event.getAction() == MotionEvent.ACTION_MOVE) {
//计算改变的值
float dx = x - oldX;
float dy = y - oldY;
//通过触摸上下移动
lookupdown += dy * TOUCH_SCALE;
//通过触摸左右观看
heading += dx * TOUCH_SCALE;
yrot = heading;
//处理完事件后
handled = true;
}
//保存当前的值
oldX = x;
oldY = y;
return handled;
}
}

最终运行结果:

代码下载地址:http://download.csdn.net/detail/seniorwizard/4470542

Andriod OpenGL 教程 10 - 3D世界相关推荐

  1. OpenGL(十八)——Qt OpenGL绘制一个3D世界

    OpenGL(十八)--Qt OpenGL绘制一个3D世界 一.说明 本篇介绍构建一个3D的世界. 二.简介 加载3D世界,并在其中漫游: 在这一课中,你将学会如何加载3D世界,并在3D世界中漫游. ...

  2. Qt OpenGL 加载3D世界,并在其中漫游

    这次教程中,我将教大家如何加载一个3D世界,并在3D世界中漫游.这相较于我们只能创造一个旋转的立方体或一群星星时有很大的进步了,当然这节课代码难度不低,但也不会很难,只要你跟着我慢慢一步一步来. 一个 ...

  3. NeHe OpenGL教程 第十课:3D世界

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  4. NeHe OpenGL第十课:3D世界

    NeHe OpenGL第十课:3D世界 加载3D世界,并在其中漫游: 在这一课中,你将学会如何加载3D世界,并在3D世界中漫游.这一课使用第一课的代码,当然在课程说明中我只介绍改变了代码. 这一课是由 ...

  5. NeHe OpenGL教程 第四十四课:3D光晕

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  6. Android OpenGL ES 2.0 屏幕坐标和3D世界坐标转换

    Android OpenGL ES 2.0 屏幕坐标和3D世界坐标转换 查看全文 http://www.taodudu.cc/news/show-6705596.html 相关文章: word中如何加 ...

  7. C++ Opengl 3D世界源码

    C++ Opengl 3D世界源码 项目开发环境 项目功能 项目演示 项目源码传送门 项目开发环境 开发语言:C++和IDE:VS2017,操作系统Windows版本windows SDK8.1,三方 ...

  8. OpenGL基础12:进入3D世界

    前置:OpenGL基础11:空间 一.坐标系 我们都学过坐标系,根据z轴的方向,一般有两种不同的坐标系表示方法: 其中拇指是x轴正方向,食指是y轴正方向,中指是z轴正方形 OpenGL约定是右手坐标系 ...

  9. WhyGL:一套学习OpenGL的框架,及翻写Nehe的OpenGL教程

    最近在重学OpenGL,之所以说重学是因为上次接触OpenGL还是在学校里,工作之后就一直在搞D3D,一转眼已经毕业6年了.OpenGL这门手艺早就完全荒废了,现在只能是重学.学习程序最有效的办法是动 ...

  10. 三维投影总结:数学原理、投影几何、OpenGL教程、我的方法

    如果要得到pose视图,除非有精密的测量方法,否则进行大量的样本采集时很耗时耗力的.可以采取一些取巧的方法,正如A Survey on Partial of 3d shapes,描述的,可以利用已得到 ...

最新文章

  1. 【转】Alert Log Messages: Private Strand Flush Not Complete [ID 372557.1]
  2. 2017-07-18日看登录日志,显示十条:最早的是:2014-04-07 17:09,应该就是注册日期吧
  3. Finding Similar Items 文本相似度计算的算法——机器学习、词向量空间cosine、NLTK、diff、Levenshtein距离...
  4. kubernetes集群搭建以及遇到的问题
  5. 剑网3 最新服务器,《剑网3》各服务器IP及所在地
  6. MyBatis 接口绑定方案及多参数传递、动态 SQL、ThreadLocal、缓存
  7. 智能门禁(6)---调用face++实现人脸检测
  8. php动态网页设计制作作业成品
  9. Mybatis 处理日期格式自动转换
  10. 分表用到的一些函数/php mysql 前面补0
  11. 2021年BATJ30套大厂Android经典高频面试题,附答案
  12. Arduino相关函数
  13. 计算机文本有哪些类型,文本类型有哪些
  14. 谈谈成功,你离成功有多远?施瓦辛格励志演讲分享(配中文翻译)
  15. 举个栗子!Tableau技巧(25):学做漂亮的桑基图 Sankeydiagram
  16. 【微信机器人】实现保存微信表情包到相册
  17. IT部门绩效考核管理思考
  18. 真正的Java学习从入门到精通
  19. 学生动漫网页设计模板下载 海贼王大学生HTML网页制作作品 简单漫画网页设计成品 dreamweaver学生网站模板
  20. 在word 页眉插入章编号+标题

热门文章

  1. wegame开dnf正在连接服务器,DNF安装wegame后显示无网络连接状态解决办法
  2. 17岁韩寒在CCTV《对话》舌战群吊的视频
  3. 韩寒式的幽默-屌丝回忆录
  4. 如何在win10上显示隐藏文件
  5. 英语基础-英语的动词变化
  6. 热门好用的IP归属地API
  7. win远程桌面连接服务器,远程桌面连接windows服务器
  8. UESTC 男神的礼物
  9. 线性回归(线性拟合)与非线性回归(非线性拟合)原理、推导与算法实现(一)
  10. java 支付宝 验证签名失败,关于支付宝签名校验失败的问题排解