android OpenGL ES 地球仪绘制——球体绘制及纹理映射

支持:

(1) opengl es绘制三角形拼成球体

(2)2的幂次方大小的图片作为纹理映射到整个球面上

(3)双点触控缩放球体

(4) 拖动旋转球体

效果图:

android OpenGL ES 地球仪绘制——球体绘制及纹理映射_第1张图片

在 android openGL 提供了绘制三角形(面)的函数

// 绑定点的坐标
gl.glVertexPointer(3, GL10.GL_FIXED, 0, mVertexBuffer);
// 按三角形模式(vCount个点中,每三个点作为一组)绘制多个三角形
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, vCount);

绘制球体:

1、将球体横着切成N层,然后按弧线竖着切M份,就得到了N*M个长方形

2、在按对角戏切分就得到了2*N*M个三角形,当然也可以有其他的方法划分成三角形

3、然后计算每个三角形的顶点坐标

计算三角形顶点坐标:

        final int angleSpan = 9;// 将球进行单位切分的角度

        for (int rowAngle = -90; rowAngle <= 90; rowAngle += angleSpan) {
            for (int colAngleAngle = 0; colAngleAngle < 360; colAngleAngle += angleSpan) {
                double xozLength = R * Math.cos(Math.toRadians(rowAngle));
                int x = (int) (xozLength * Math.cos(Math.toRadians(colAngleAngle)));
                int z = (int) (xozLength * Math.sin(Math.toRadians(colAngleAngle)));
                int y = (int) (R * Math.sin(Math.toRadians(rowAngle)));
                alVertix.add(x);
                alVertix.add(y);
                alVertix.add(z);
            }
        }

4、准备纹理

纹理图的大小一定要是2的幂次方的,如果不是,可以长宽缩放成这个规格大小的图片。

    public int initTexture(GL10 gl, int resourceId) {
        int[] textures = new int[1];
        gl.glGenTextures(1, textures, 0);
        int currTextureId = textures[0];
        gl.glBindTexture(GL10.GL_TEXTURE_2D, currTextureId);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);

        InputStream is = this.getResources().openRawResource(resourceId);
        Bitmap bitmapTmp;
        try {
            bitmapTmp = BitmapFactory.decodeStream(is);
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmapTmp, 0);
        bitmapTmp.recycle();
        return currTextureId;
    }

bitmap和纹理绑定:

GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmapTmp, 0);

5、计算每个三角形的坐标与其对应这张二维纹理图的坐标

按比例计算:

        vCount = alVertix.size() / 3;// 顶点的数量为坐标值数量的1/3,因为一个顶点有3个坐标

        // 将alVertix中的坐标值转存到一个int数组中
        int vertices[] = new int[vCount * 3];
        for (int i = 0; i < alVertix.size(); i++) {
            vertices[i] = alVertix.get(i);
        }
        alVertix.clear();
        ArrayList alTexture = new ArrayList();// 纹理

        int row = (180 / angleSpan) + 1;// 球面切分的行数
        int col = 360 / angleSpan;// 球面切分的列数

        float splitRow = row;
        float splitCol = col;

        for (int i = 0; i < row; i++)// 对每一行循环
        {
            if (i > 0 && i < row - 1) {// 中间行
                for (int j = 0; j < col; j++) {// 中间行的两个相邻点与下一行的对应点构成三角形
                    int k = i * col + j;
                    // 第1个三角形顶点
                    alVertix.add(vertices[(k + col) * 3]);
                    alVertix.add(vertices[(k + col) * 3 + 1]);
                    alVertix.add(vertices[(k + col) * 3 + 2]);

                    // 纹理坐标
                    alTexture.add(j / splitCol);
                    alTexture.add((i + 1) / splitRow);

                    // 第2个三角形顶点
                    int tmp = k + 1;
                    if (j == col - 1) {
                        tmp = (i) * col;
                    }
                    alVertix.add(vertices[(tmp) * 3]);
                    alVertix.add(vertices[(tmp) * 3 + 1]);
                    alVertix.add(vertices[(tmp) * 3 + 2]);

                    // 纹理坐标
                    alTexture.add((j + 1) / splitCol);
                    alTexture.add(i / splitRow);

                    // 第3个三角形顶点
                    alVertix.add(vertices[k * 3]);
                    alVertix.add(vertices[k * 3 + 1]);
                    alVertix.add(vertices[k * 3 + 2]);

                    // 纹理坐标
                    alTexture.add(j / splitCol);
                    alTexture.add(i / splitRow);
                }
                for (int j = 0; j < col; j++) {// 中间行的两个相邻点与上一行的对应点构成三角形
                    int k = i * col + j;

                    // 第1个三角形顶点
                    alVertix.add(vertices[(k - col) * 3]);
                    alVertix.add(vertices[(k - col) * 3 + 1]);
                    alVertix.add(vertices[(k - col) * 3 + 2]);
                    alTexture.add(j / 40f);
                    alTexture.add((i - 1) / splitRow);

                    int tmp = k - 1;
                    if (j == 0) {
                        tmp = i * col + col - 1;
                    }
                    // 第2个三角形顶点
                    alVertix.add(vertices[(tmp) * 3]);
                    alVertix.add(vertices[(tmp) * 3 + 1]);
                    alVertix.add(vertices[(tmp) * 3 + 2]);
                    alTexture.add((j - 1) / splitCol);
                    alTexture.add(i / splitRow);

                    // 第3个三角形顶点
                    alVertix.add(vertices[k * 3]);
                    alVertix.add(vertices[k * 3 + 1]);
                    alVertix.add(vertices[k * 3 + 2]);
                    alTexture.add(j / splitCol);
                    alTexture.add(i / splitRow);
                }
            }
        }

6、画笔绑定纹理
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
7、绘制

gl.glDrawArrays(GL10.GL_TRIANGLES, 0, vCount);

8、控制旋转和缩放:

给ball实体设置三个旋转变量 : 

    public float mAngleX = 0;// 沿x轴旋转角度
    public float mAngleY = 0;// 沿y轴旋转角度
    public float mAngleZ = 0;// 沿z轴旋转角度
给ball实体设置缩放变量 :

    public float zoom = -3f;

    public final float maxZoom = -2f;
    public final float minZoom = -4f;

9、ball每次draw之前变换坐标系:
gl.glTranslatef(0f, 0f, zoom);
gl.glRotatef(mAngleX, y_axis[0], y_axis[1], y_axis[2]);

10、重写OnTouch函数

控制手指拖动和多点缩放

    private OnTouchListener onTouchListener = new OnTouchListener() {
        float lastX, lastY;

        private int mode = 0; // 触控点的个数

        float oldDist = 0;

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                mode = 1;
                lastX = event.getRawX();
                lastY = event.getRawY();
                break;

            case MotionEvent.ACTION_POINTER_DOWN:
                mode += 1;

                oldDist = caluDist(event);

                break;

            case MotionEvent.ACTION_POINTER_UP:
                mode -= 1;
                break;

            case MotionEvent.ACTION_UP:
                mode = 0;
                break;

            case MotionEvent.ACTION_MOVE:
                if (mode >= 2) {
                    float newDist = caluDist(event);
                    if (Math.abs(newDist - oldDist) > 2f) {
                        zoom(newDist, oldDist);
                    }
                } else {
                    float dx = event.getRawX() - lastX;
                    float dy = event.getRawY() - lastY;

                    float a = 180.0f / 320;
                    ball.mAngleX += dx * a;
                    ball.mAngleY += dy * a;
                }
                break;
            }

            lastX = (int) event.getRawX();
            lastY = (int) event.getRawY();
            return true;
        }
    };

mode是用来记录当前屏幕上按下了几个点。


11、源码下载:

点击打开链接   : http://download.csdn.net/detail/newfar1991/4657846


你可能感兴趣的:(android)