前面介绍了Android OpenGL的开发基础,绘制了一个3D的物体,在立体空间控制一个3D对象,但如何来构建一个3D的场景呢?接下来就讲讲怎样去完成一个3D世界的场景吧。

首先,我们应该明白的是,任何一个复杂的对象都是由一些简单的三角形构成的,所以在创建一个复杂的3D场景之前,要先定义一个场景的数据结构。三角形本质上是由一些(两个以上)顶点组成的多边形,顶点是最基本的分类单位,它包含了OpenGL真正有用的数据,我们用3D空间中的坐标值(x,y,z)以及它们的纹理坐标(u,v)来定义三角形的每个顶点。当然啦,每个对象都不只是由一个三角形构成的,因此可以通过一个List来存储这些三角形。

数据结构代码如下:

/*ScData.java*/
import java.util.ArrayList;import java.util.List;//VERTEX顶点结构class VERTEX{    float x, y, z;// 3D 坐标    float u, v;// 纹理坐标    public VERTEX(float x,float y,float z,float u,float v)    {        this.x = x;        this.y = y;        this.z = z;        this.u = u;        this.v = v;    }}//TRIANGLE三角形结构class TRIANGLE{    // VERTEX矢量数组,大小为3    VERTEX[]    vertex    = new VERTEX[3];}//SECTOR区段结构class SECTOR{    // Sector中的三角形个数    int numtriangles;    // 三角行的list    List<TRIANGLE>    triangle    = new ArrayList<TRIANGLE>();}

一个场景必然由很多个顶点组成,由于这些顶点的数据量过大,所以这里将这些顶点存放到一个和游戏一起打包的文件中,然后在程序中通过装载这个文件来取得数据。在这里将顶点数据存放到“assets/data/world.txt”文件中,其读取文件的代码如下:

/*GLFile.java*/
import java.io.IOException;import java.io.InputStream;import android.content.res.AssetManager;import android.content.res.Resources;public class GLFile{    public static Resources resources;    public GLFile(Resources resources)    {        GLFile.resources = resources;    }    public static InputStream getFile(String name){        AssetManager am = GLFile.resources.getAssets();        try {            return am.open(name);        } catch (IOException e) {            e.printStackTrace();            return null;        }    }}

装载图片的代码前面也已经说过,在此就不详细说了。。代码如下:

/*GLImage.java*/
import android.content.res.Resources;import android.graphics.Bitmap;import android.graphics.BitmapFactory;public class GLImage{    public static Bitmap mBitmap;    public static void load(Resources resources)    {        mBitmap = BitmapFactory.decodeResource(resources, R.drawable.img);    }}

然后创建一个MainActivity.java,代码跟以前的差不多,也不详细讲解了。。不懂的可以去我前两篇帖子。。代码如下:

/*MainActivity.java*/
public class MainActivity  extends Activity {        GLRender renderer = new GLRender();        @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        GLImage.load(this.getResources());        new GLFile(this.getResources());        GLSurfaceView glView = new GLSurfaceView(this);        glView.setRenderer(render);        setContentView(glView);    }}

接下来就是整个应用的重头戏了。。先创建一个GLRender.java文件,根据http://www.apkbus.com/android-121526-1-1.html ,当然也会少不了下面的3个抽象方法的:
public void onDrawFrame(GL10 gl){}
public void onSurfaceChanged(GL10 gl, int width, int height){}
public void onSurfaceCreated(GL10 gl, EGLConfig config){}

onSurfaceCreated先实现下载纹理,然后就是一些设置,这些在前两篇中说到过了。。最后用读取资源数据,这里用了一个SetupWorld()函数。代码如下:

    @Override    public void onSurfaceCreated(GL10 gl, EGLConfig config)    {        LoadGLTextures(gl);        gl.glEnable(GL10.GL_TEXTURE_2D);                                    gl.glBlendFunc(GL10.GL_SRC_ALPHA,GL10.GL_ONE);                            gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);                        gl.glClearDepthf(1.0f);                                            gl.glDepthFunc(GL10.GL_LESS);                                        gl.glEnable(GL10.GL_DEPTH_TEST);                                    gl.glShadeModel(GL10.GL_SMOOTH);                                    gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);        SetupWorld();    }

SetupWorld()函数就是从”assets/data/world.txt”中读取数据,每取出3个点就构成一个三角形,代码如下:

    public void SetupWorld()    {        BufferedReader br = new BufferedReader(new InputStreamReader(GLFile.getFile("data/world.txt")));        TRIANGLE triangle = new TRIANGLE();        int vertexIndex = 0;            try {            String line = null;            while((line = br.readLine()) != null){                if(line.trim().length() <= 0 || line.startsWith("/")){                    continue;                }                String part[] = line.trim().split("\\s+");                float x = Float.valueOf(part[0]);                float y = Float.valueOf(part[1]);                float z = Float.valueOf(part[2]);                float u = Float.valueOf(part[3]);                float v = Float.valueOf(part[4]);                VERTEX vertex = new VERTEX(x, y, z, u, v);                triangle.vertex[vertexIndex] = vertex;                           vertexIndex ++;                if(vertexIndex == 3){                    vertexIndex = 0;                    sector1.triangle.add(triangle);                    triangle = new TRIANGLE();                }            }        } catch (IOException e) {            e.printStackTrace();        }    }

LoadGLTextures(GL10 gl) 实现的是下载纹理,这部分在http://www.apkbus.com/android-121768-1-1.html 有较详细的讲到。。也不细说了哈。。代码如下:

    public void LoadGLTextures(GL10 gl)     {        IntBuffer textureBuffer = IntBuffer.allocate(3);        gl.glGenTextures(3, textureBuffer);        texture[0] = textureBuffer.get();        gl.glBindTexture(GL10.GL_TEXTURE_2D, texture[0]);        gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);        gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST);        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, GLImage.mBitmap, 0);        texture[1] = textureBuffer.get(2);        gl.glBindTexture(GL10.GL_TEXTURE_2D, texture[1]);        gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);        gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, GLImage.mBitmap, 0);    }

接下来就来实现onDrawFrame(GL10 gl)函数吧。首先要去掉每个三角形的顶点数据,然后就是装在纹理贴图,将这些数据绘制到屏幕上就构建了所指定的场景样式了。代码如下:

    @Override    public void onDrawFrame(GL10 gl)    {        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);    // Clear The Screen And The Depth Buffer        gl.glLoadIdentity();                                        // Reset The View        float xtrans = -xpos;        float ztrans = -zpos;        float ytrans = -walkbias-0.25f;        float sceneroty = 360.0f - yrot;        FloatBuffer vertexPointer = FloatBuffer.wrap(new float[9]);        FloatBuffer texCoordPointer = FloatBuffer.wrap(new float[6]);        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexPointer);        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, texCoordPointer);                gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);                gl.glLoadIdentity();        gl.glRotatef(lookupdown, 1.0f, 0.0f, 0.0f);        gl.glRotatef(sceneroty, 0.0f, 1.0f, 0.0f);        gl.glTranslatef(xtrans, ytrans, ztrans);                gl.glBindTexture(GL10.GL_TEXTURE_2D, texture[1]);        for(TRIANGLE triangle : sector1.triangle)        {            vertexPointer.clear();            texCoordPointer.clear();            gl.glNormal3f(0.0f, 0.0f, 1.0f);            for(int i=0; i<3; i++)            {                VERTEX vt = triangle.vertex[i];                vertexPointer.put(vt.x);                vertexPointer.put(vt.y);                vertexPointer.put(vt.z);                texCoordPointer.put(vt.u);                texCoordPointer.put(vt.v);            }            gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 4);        }        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);        }

剩下一个就是onSurfaceChanged(GL10 gl, int width, int height)啦,这个跟前面的说的区别不大,直接看代码吧。。

    @Override    public void onSurfaceChanged(GL10 gl, int width, int height)    {        float ratio = (float) width / height;        //设置OpenGL场景的大小        gl.glViewport(0, 0, width, height);        //设置投影矩阵        gl.glMatrixMode(GL10.GL_PROJECTION);        //重置投影矩阵        gl.glLoadIdentity();        // 设置视口的大小        gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);        // 选择模型观察矩阵        gl.glMatrixMode(GL10.GL_MODELVIEW);            // 重置模型观察矩阵        gl.glLoadIdentity();        }

其实,到这里就可以说已经搞定了3D的场景效果啦。。不过这个静态的。。

下面就将通过按键事件处理镜头的移动和旋转,具体代码如下:

    public boolean onKeyUp(int keyCode, KeyEvent event)    {        switch ( keyCode )        {            case KeyEvent.KEYCODE_DPAD_LEFT:                yrot -= 1.5f;                break;            case KeyEvent.KEYCODE_DPAD_RIGHT:                yrot += 1.5f;                break;            case KeyEvent.KEYCODE_DPAD_UP:                // 沿游戏者所在的X平面移动                xpos -= (float)Math.sin(heading*piover180) * 0.05f;                    // 沿游戏者所在的Z平面移动                zpos -= (float)Math.cos(heading*piover180) * 0.05f;                            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;                        break;            case KeyEvent.KEYCODE_DPAD_DOWN:                // 沿游戏者所在的X平面移动                xpos += (float)Math.sin(heading*piover180) * 0.05f;                        // 沿游戏者所在的Z平面移动                zpos += (float)Math.cos(heading*piover180) * 0.05f;                    // 如果walkbiasangle小于1度                if (walkbiasangle <= 1.0f)                                    {                    walkbiasangle = 359.0f;// 使 walkbiasangle 等于 359                                    }                else                                            {                    walkbiasangle-= 10;// 如果 walkbiasangle > 1 减去 10                }                // 使游戏者产生跳跃感                walkbias = (float)Math.sin(walkbiasangle * piover180)/20.0f;                        break;        }        return false;    }

因为有按键事件,所以要在MAinActivity.java 中加上如下代码:

@Override public boolean onKeyUp(int keyCode, KeyEvent event){     renderer.onKeyUp(keyCode, event);     return super.onKeyUp(keyCode, event); }

额。。结束了哦。。期待效果图了吧。。见下图吧:

其实,这个还有许多的地方可以加以改进的,比如可以用鼠标或者触笔来实现对场景的缩放,毕竟现在很多的手机和平板都没有按键的了。。(PS:LZ最近很忙,就不弄这个了哈。。等改天有空再来改进吧)

还有就是考虑到程序运行的效率,可以减少处理镜头背面的三角形的绘制,也即是观察者视线不能看到的地方,这样会使程序运行得更加流畅。。(PS:还是那就是,LZ最近很忙。。)

代码下载链接:http://download.csdn.net/detail/klcf0220/5526237

参考链接:http://developer.android.com/guide/topics/graphics/opengl.html

http://www.cnblogs.com/android100/archive/2012/06/27/2565438.html

喜欢开源,乐意分享的大神们,欢迎加入QQ群:176507146,你值的拥有哦!

更多相关文章

  1. 搭建云服务器
  2. 原 美团外卖Android(安卓)Crash治理之路
  3. Android的16进制颜色值
  4. Android(安卓)Studio Intent向上下活动传递数据 第一行代码
  5. Android(安卓)编译文件使其支持wml
  6. Android截图代码实现(DDMS使用部分)
  7. android App集成支付宝
  8. Android获取双卡双待手机的SIM卡信息示例代码
  9. Android通过WebView实现原生Java与JS交互

随机推荐

  1. Android(安卓)图片倒影和setXfermode
  2. Android布局管理器总结
  3. Android存在“后门”?收集用户信息以推广
  4. ANDROID 单元测试
  5. android listview 相关
  6. Android(安卓)之不要滥用 SharedPreferen
  7. Android(安卓)抖动效果
  8. android监听软键盘退格(删除)事件
  9. launcher学习
  10. EditText光标居上