上图:(转自安卓巴士,个人觉得不错)

色子是可以触摸转动的,不要见怪,更多玩法还有待开发。
进入正题,先看一下类结构:

DiceActivity.java是主Activity,主要代码:

mGLView = new DiceSurfaceView(this);                setContentView(mGLView);

  就是将DiceSurfaceView的实例设置为Activity的内容视图,菜单操作只是为了使这个小项目看起来还像个东西才加上的,可以忽略。
DiceSurfaceView.java继承了android.opengl.GLSurfaceView,在DiceSurfaceView的构造方法里为他设置一个DiceRenderer渲染器实例,负责视图的渲染。这里解释一下:在Android平台中提供了一个android.opengl包,类GLSurfaceView提供了对Display(实际显示设备的抽象),Suface(存储图像的内存区域FrameBuffer的抽象),Context(存储OpenGL ES绘图的一些状态信息)的管理,大大简化了OpenGL ES的程序框架,开发OpenGL ES应用时只需为GLSurfaceView 设置渲染器实例(调用setRenderer(mRenderer))即可。关于Display,Suface,Context,附件有份AndroidOpenGL小结(不同地方拼一起的,还请原作者见谅),里面有介绍。看DiceSurfaceView.java源码:

class DiceSurfaceView extends GLSurfaceView {        private DiceRenderer mRenderer = null;        private float mPreviousX = 0;        private float mPreviousY = 0;        public DiceSurfaceView(Context context) {                super(context);                // 设置渲染器,                mRenderer = new DiceRenderer(this);                setRenderer(mRenderer);                // 设置描绘方式,                setAutoRender(false);                this.requestRender();        }        @Override        public boolean onTouchEvent(MotionEvent e) {                float x = e.getX();                float y = e.getY();                //转换坐标方向;                y = -y;                switch (e.getAction()) {                case MotionEvent.ACTION_MOVE:                        float dx = x - mPreviousX;                        float dy = y - mPreviousY;                        mRenderer.onTouchMove(dx, dy);                case MotionEvent.ACTION_DOWN://                        Log.i("tg","touch down/" + x + "/" + y);                        this.mPreviousX = x;                        this.mPreviousY = y;                        break;                case MotionEvent.ACTION_UP://                        Log.i("tg","touch up/" + x + "/" + y);                        this.mPreviousX = 0;                        this.mPreviousY = 0;                        setAutoRender(true);                        this.mRenderer.startRotate();                        break;                }                this.requestRender();                return true;        }        /**         * 设置是否自动连续渲染         * @param auto         */        public void setAutoRender(boolean auto){                // RENDERMODE_WHEN_DIRTY-有改变时重绘-需调用requestRender()                // RENDERMODE_CONTINUOUSLY -自动连续重绘(默认)                if(auto){                        setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);                }else{                        setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);                }        }        //重置背景画        public void resetBackground(int optionalBg){                TextureManager.bgIndex = optionalBg;                this.requestRender();        }}

  接受一个渲染器DiceRenderer实例,并对触摸事件作出处理。
DiceRenderer.java 继承自android.opengl.GLSurfaceView.Renderer,做具体的渲染操作,源码:

public class DiceRenderer implements Renderer {        //90度角的正余弦        private static final float NORMALS_COS = (float) Math.cos(Math.PI/2);        private static final float NORMALS_SIN = (float)Math.sin(Math.PI/2);        private static final int MSG_ROTATE_STOP = 1;                private DiceSurfaceView surface = null;        private Handler handler = null;        private Dice dice = null;        private BackWall back = null;        //转动时速度矢量        private float rotateV = 0;        //已旋转角度        private float rotated = 0;        //当前旋转轴        private float axisX = 0;        private float axisY = 0;        private RotateTask rTask = null;                /**渲染器*/        public DiceRenderer(DiceSurfaceView surface){//                Log.i("tg","Renderer 构造。");                this.surface = surface;                // 初始化数据                dice = new Dice();                back = new BackWall();                handler = new Handler(){                        @Override                        public void handleMessage(Message msg){                                super.handleMessage(msg);                                if(msg.what == MSG_ROTATE_STOP){                                        DiceRenderer.this.surface.setAutoRender(false);//设置非自动连续渲染                                }                        }                };        }        @Override        public void onSurfaceCreated(GL10 gl, EGLConfig config) {//                Log.i("tg","Surface created.config/" + config);                                // Set the background frame color                gl.glClearColor(0.3f, 0.3f, 0.4f, 0.7f);                // 启用深度测试, 不启用时,不管远近,后画的会覆盖之前画的,                gl.glEnable(GL10.GL_DEPTH_TEST);                gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);// 启用顶点坐标数组                gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);// 打开法线数组                //初始化纹理                TextureManager.initTexture(gl, this.surface.getResources());                initLight(gl);                initMaterial(gl);        }        @Override        public void onSurfaceChanged(GL10 gl, int width, int height) {//                Log.i("tg","Surface changed.。");                //设置视窗                gl.glViewport(0, 0, width, height);        // 适应屏幕比例        float ratio = (float) width / height;        //设置矩阵为投射模式        gl.glMatrixMode(GL10.GL_PROJECTION);        // set matrix to projection mode        //重置矩阵        gl.glLoadIdentity();                        // reset the matrix to its default state        //设置投射椎体 // apply the projection matrix        if(ratio < 1 ){                gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7);         }else{                gl.glFrustumf(-ratio, ratio, -1, 1, 4, 8); //                gl.glFrustumf(-ratio*1.5f, ratio*1.5f, -1*1.5f, 1*1.5f, 4, 8);         }                }        @Override        public void onDrawFrame(GL10 gl) {//                Log.i("tg","draw a frame..");                // 重画背景,  刷屏                gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);                // 设置 GL_MODELVIEW(模型观察) 转换模式                gl.glMatrixMode(GL10.GL_MODELVIEW);                // 重置矩阵,设置当前矩阵为单位矩阵,相当于渲染之前清屏                gl.glLoadIdentity();                // 使用GL_MODELVIEW 模式时, 必须设置视点//                GLU.gluLookAt(gl, 3,3,3, 1f, 1f, 1f, 0f, 1.0f, 0f);                GLU.gluLookAt(gl, 0, 0, 5, 0f, 0f, -1f, 0f, 1.0f, 0.0f);                                // 绘制背景墙                gl.glPushMatrix();                back.drawSelf(gl);                gl.glPopMatrix();                // 绘制色子                gl.glPushMatrix();                if(rotated != 0){                        RotateOnTouch(gl);                }                gl.glRotatef(45, 1, 1, 0);                dice.drawSelf(gl);                gl.glPopMatrix();        }        /**触摸后转动*/        private void RotateOnTouch(GL10 gl){                this.rotated += rotateV;                gl.glRotatef(rotated, axisX, axisY, 0);                if(rotateV>0){//                        Log.i("tg","GL rotateV/" + rotateV);//                        Log.i("tg","GL rotated/" + rotated + "/" + rotateV);                }        }        /**         * 响应触摸移动         * @param dx         * @param dy         */        public void onTouchMove(float dx,float dy){                rotateV = Math.abs(dx) + Math.abs(dy);//                Log.i("tg","GL rotateV/" + rotateV);                rotated += rotateV;                setAxisLine(dx,dy);        }        /**设置转轴线*/        public void setAxisLine(float dx ,float dy){                //x1 = x0 * cosB - y0 * sinB                y1 = x0 * sinB + y0 * cosB                this.axisX = dx*NORMALS_COS - dy*NORMALS_SIN;                this.axisY= dx*NORMALS_SIN + dy*NORMALS_COS;        }        /**启动旋转线程*/        public void startRotate(){                if(rTask != null){                        rTask.running = false;                }                rTask = new RotateTask();                rTask.start();        }        /**         * 旋转线程类         *         */        class RotateTask extends Thread{                boolean running = true;                @Override                public void run() {                        while(running && rotateV > 0){                                if(rotateV>50){                                        rotateV -= 7;                                }else if(rotateV>20){                                        rotateV -= 3;                                }else{                                        rotateV --;                                }                                try {                                        Thread.sleep(200);                                } catch (InterruptedException e) {                                        e.printStackTrace();                                }                        }                        if(rotateV<=0){                                handler.sendEmptyMessage(MSG_ROTATE_STOP);                        }                }        }        /** 初始化灯光         * 定义各种类型光的光谱         * */        private void initLight(GL10 gl) {                gl.glEnable(GL10.GL_LIGHTING);                //打开照明总开关                gl.glEnable(GL10.GL_LIGHT1);                // 打开1号灯                // 环境光设置                float[] ambientParams = { 0.7f, 0.7f, 0.7f, 1.0f };// 光参数 RGBA                gl.glLightfv(GL10.GL_LIGHT1,                //光源序号                                GL10.GL_AMBIENT,                         //光照参数名-环境光                                ambientParams,                                 //参数值                                0                                                        //偏移                                );                // 散射光设置                float[] diffuseParams = { 0.7f, 0.7f, 0.7f, 1.0f };// 光参数 RGBA                gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_DIFFUSE, diffuseParams, 0);                // 反射光设置                float[] specularParams = { 1f, 1f, 1f, 1.0f };// 光参数 RGBA                gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_SPECULAR, specularParams, 0);                //光源位置                float[] positionParams = { 0,0,9,1 };                gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_POSITION, positionParams, 0);                //聚光灯方向                float[] directionParams = {0,0,-1};                gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_SPOT_DIRECTION , directionParams, 0);                //聚光角度(0-90)度                gl.glLightf(GL10.GL_LIGHT1, GL10.GL_SPOT_CUTOFF , 30);                //聚光程度(0-128)实现聚焦                gl.glLightf(GL10.GL_LIGHT1, GL10.GL_SPOT_EXPONENT  , 10);        }        /** 初始化材质          * 定义平面对各种类型光的反射光谱         * */        private void initMaterial(GL10 gl) {                //控制环境光在平面上的反射光光谱                                                        float ambientMaterial[] = { 0.4f, 0.5f, 0.6f, 0.3f };                gl.glMaterialfv(                                GL10.GL_FRONT_AND_BACK, //反射面,正面,反面,或两面(android)只支持两面                                GL10.GL_AMBIENT,                //反射光类型,环境光                                ambientMaterial,                 //反射参数值                                0                                                //偏移                                );                //控制反射散射光                float diffuseMaterial[] = { 0.7f, 0.6f, 0.7f, 0.8f };                gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE,                                diffuseMaterial, 0);                //控制反射光                float specularMaterial[] = { 0.9f, 0.9f, 0.9f, 0.8f };                gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR,                                specularMaterial, 0);                //对高光的反射指数(0-128)值越大光的散射越小                gl.glMaterialf(GL10.GL_FRONT_AND_BACK, GL10.GL_SHININESS, 120f);        }        }

  这里包括了灯光,材质的设置,旋转逻辑,最终的渲染画屏,可参看注释理解。
以上是主要类,他们之间的联系看参考Android开发文档Resources>Tutorials>OpenGL ES 1.0 或巴士里相关帖,这里不罗嗦了。
TextureManager.java是上线后重构出来整个结构更清晰,是管理纹理的类,由它生成纹理ID,绑定图片资源,供渲染所用,看源码:

public class TextureManager {        //纹理索引号        public static final int TEXTURE_INDEX_DICE = 0;        public static final int TEXTURE_INDEX_BG00 = 1;        public static final int TEXTURE_INDEX_BG01 = 2;        public static final int TEXTURE_INDEX_BG02 = 3;        //纹理资源id        private static int[] textureSrcs = {R.drawable.dice_map,R.drawable.bg00,R.drawable.bg01,R.drawable.bg02};        //纹理id存储        private static int[] textureIds = new int[textureSrcs.length];                private static GL10 gl = null;        private static Resources res = null;        //背景画索引 0-2;        public static int bgIndex = 0;                /**         * 取得指定索引的纹理id         * @param index         * @return         */        public static int getTextureId(int index){//                Log.i("tg","TextureManager/getTextureId/" + textureIds[index]);                if(textureIds[index] <= 0){                        Log.i("tg","TextureManager/getTextureId/" + textureIds[index]);                        gl.glGenTextures(1, textureIds, index);                        bindTexture(gl,res,index);                }                return textureIds[index];        }        /**初始化纹理*/        public static void initTexture( GL10 gl, Resources res) {                TextureManager.gl = gl;                TextureManager.res = res;                //获取未使用的纹理对象ID                gl.glGenTextures(1, textureIds, TEXTURE_INDEX_DICE);                bindTexture(gl,res,TEXTURE_INDEX_DICE);                //获取未使用的纹理对象ID                gl.glGenTextures(1, textureIds, bgIndex + 1);                bindTexture(gl,res,bgIndex + 1);                //                for(int i=0;i<textureIds.length;i++){//                        bindTexture(gl,res,i);//                }        }        /**         * 为纹理id绑定纹理。         * @param gl         * @param res         * @param index         */        private static void bindTexture(GL10 gl,Resources res,int index){//                Log.i("tg","TextureManager/initTexture/" + textureIds[i]);                //绑定纹理对象                gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIds[index]);                //设置纹理控制,指定使用纹理时的处理方式                //缩小过滤:一个像素代表多个纹素。                gl.glTexParameterf(GL10.GL_TEXTURE_2D,         //纹理目标                                GL10.GL_TEXTURE_MIN_FILTER,                        //纹理缩小过滤                                GL10.GL_NEAREST                                                                //使用距离当前渲染像素中心最近的纹素                                );                //放大过滤:一个像素是一个纹素的一部分。                //放大过滤时,使用距离当前渲染像素中心,最近的4个纹素加权平均值,也叫双线性过滤。                gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,                                GL10.GL_LINEAR);                //                //设置纹理贴图方式,指定对超出【0,1】的纹理坐标的处理方式                //左下角是【0,0】,右上角是【1,1】,横向是S维,纵向是T维。android以左上角为原点                //S维贴图方式:重复平铺                gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,                                GL10.GL_REPEAT);                //T维贴图方式:重复平铺                gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,                                GL10.GL_REPEAT);                bindBitmap(index,res);        }        /**         * 为纹理绑定位图         * @param index         * @param res         */        private static void bindBitmap(int index,Resources res){                Bitmap bitmap = null;                InputStream is = res.openRawResource(textureSrcs[index]);                try {                        bitmap = BitmapFactory.decodeStream(is);                } finally {                        if(is != null){                                try {                                        is.close();                                        is = null;                                } catch (IOException e) {                                        e.printStackTrace();                                }                        }                }                //为纹理对象指定位图                GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);                //释放bitmap对象内存,像素数据仍存在,不影响使用。                bitmap.recycle();        }}

  还有Dice.java和BackWall.java是渲染物件类色子和背景,各自保存自己的顶点,法向量,贴图坐标数据,并提供一个自我渲染的方法,看Dice.java源码:

public class Dice {        private int vertexCount = 36;        /** 顶点坐标数据缓冲 */        private FloatBuffer mVertexBuffer;        /** 顶点法向量数据缓冲 */        private FloatBuffer mNormalBuffer;        /** 顶点纹理数据缓冲,存储每个顶点在位图中的坐标 */        private FloatBuffer mTextureBuffer;        /**色子类*/        public Dice() {                initDataBuffer();        }        /**初始化定点数据缓冲区*/        private void initDataBuffer(){                float[] vertices = Constant.VERTEX_COORD;                float[] normals = Constant.NORMALS_COORD;                float[] texST = Constant.TEXTURE_COORD;                //new float[cpTexST.length];        //常量数组的内容可变,这里要拷贝                // vertices.length*4是因为一个Float四个字节                ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);                vbb.order(ByteOrder.nativeOrder());// 设置字节顺序                mVertexBuffer = vbb.asFloatBuffer();// 转换为float型缓冲                mVertexBuffer.put(vertices);// 向缓冲区中放入顶点坐标数据                mVertexBuffer.position(0);// 设置缓冲区起始位置                                ByteBuffer nbb = ByteBuffer.allocateDirect(normals.length * 4);                nbb.order(ByteOrder.nativeOrder());// 设置字节顺序                mNormalBuffer = nbb.asFloatBuffer();// 转换为int型缓冲                mNormalBuffer.put(normals);// 向缓冲区中放入顶点着色数据                mNormalBuffer.position(0);// 设置缓冲区起始位置                                ByteBuffer tbb = ByteBuffer.allocateDirect(texST.length * 4);                tbb.order(ByteOrder.nativeOrder());// 设置字节顺序                mTextureBuffer = tbb.asFloatBuffer();// 转换为int型缓冲                mTextureBuffer.put(texST);// 向缓冲区中放入顶点着色数据                mTextureBuffer.position(0);// 设置缓冲区起始位置        }        /**绘制色子*/        public void drawSelf(GL10 gl) {//                Log.i("tg","to draw dice..");                // 为画笔指定顶点坐标数据                gl.glVertexPointer(3,                                 // 每个顶点的坐标数量为3 xyz                                GL10.GL_FLOAT,                                 // 顶点坐标值的类型为 GL_FIXED                                0,                                                                                 // 连续顶点坐标数据之间的间隔                                mVertexBuffer                                         // 顶点坐标数据                );                // 为画笔指定顶点法向量数据                gl.glNormalPointer(GL10.GL_FLOAT, 0, mNormalBuffer);                // 开启纹理贴图                gl.glEnable(GL10.GL_TEXTURE_2D);                // 允许使用纹理ST坐标缓冲                gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);                // 指定纹理ST坐标缓冲                gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTextureBuffer);                // 绑定当前纹理                gl.glBindTexture(GL10.GL_TEXTURE_2D, TextureManager.getTextureId(TextureManager.TEXTURE_INDEX_DICE));                // 绘制图形 , 以三角形方式填充                gl.glDrawArrays(GL10.GL_TRIANGLES,         0,         vertexCount );        }}

 转自安卓巴士,个人觉得不错 

部分代码已加上注释,就不多说了,上附件:

http://files.cnblogs.com/feifei1010/Dice-1.0.zip

深圳群 260134856;成都群 252743807;西安群252746034;武汉群121592153;杭州群253603803;大连群253672904;青岛群 257925319

更多相关文章

  1. Android中文API (60) ―― DatePicker.OnDateChangedListener
  2. 关于Android(安卓)渐变动画 淡入效果的实现
  3. android 设置全屏方法1
  4. Android(安卓)背景虚化实现
  5. sysclktz 0
  6. android 图片放大缩小 多点缩放
  7. ConstraintLayout子View设置match_parent后约束失效
  8. Android中自制通讯录中显示出数据库中的姓名和电话号码进行打电
  9. Android(安卓)Chronometer(计时器)

随机推荐

  1. Android中添加Admob广告(转
  2. Android开发小技巧集合(不断更新中)
  3. Android插件化开发之DexClassLoader动态
  4. Android解决在onCreate()中获取View的wid
  5. Android 传感器的 数据流和框架
  6. Android(安卓)Lua 相互调用
  7. Android 自己动手写ListView学习其原理 1
  8. Flutter 项目打包发布
  9. Android(安卓)ADB 找不到设备的解决方法
  10. Android-Module:ImageView常用XML属性