美文网首页
Android 绘制 2D、3D 图形

Android 绘制 2D、3D 图形

作者: gaookey | 来源:发表于2022-02-27 22:58 被阅读0次
image.png

绘制 2D 图形

使用 OpenGL ES 需要三个步骤

  1. 创建 GLSurfaceView 组件,使用 Activity 来显示 GLSurfaceView 组件。
  2. GLSurfaceView 组件创建 GLSurfaceView.Renderer 实例,实现 GLSurfaceView.Renderer 类时需要实现该接口里的三个方法。
  • void onDrawFrame(GL10 gl) Renderer 对象调用该方法绘制 GLSurfaceView 的当前帧
  • void onSurfaceChanged(GL10 gl, int width, int height)GLSurfaceView 的大小改变时回调该方法
  • void onSurfaceCreated(GL10 gl, EGLConfig config)GLSurfaceView 被创建时回调该方法
  1. 调用 GLSurfaceView 组件的 setRenderer() 方法指定 Renderer 对象,该 Renderer 对象将会完成 GLSurfaceView 里 3D 图形的绘制

实现 Renderer 类时需要实现三个方法,这三个方法都有一个 GL10 形参,它就代表了 OpenGL ES 的“绘制画笔”

SurfaceView 被创建时,系统会回调 Renderer 对象的 onSurfaceCreated() 方法,该方法可以对 OpenGL ES 执行一些无须任何改变的初始化。

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {

        // 关闭抗抖动,可以提高性能
        gl.glDisable(GL10.GL_DITHER);
        // 设置系统对透视进行修正
        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
        // 用黑色“清屏”
        gl.glClearColor(0, 0, 0, 0);
        // 设置阴影平滑模式
        gl.glShadeModel(GL10.GL_SMOOTH);
        // 启用深度测试。所谓深度测试,就是让 OpenGL ES 负责跟踪每个物体在 Z 轴上的深度,这样就可避免后面的物体遮挡前面的物体。
        gl.glEnable(GL10.GL_DEPTH_TEST);
        // 设置深度测试的类型
        gl.glDepthFunc(GL10.GL_LEQUAL);
    }
  • glDisable() 用于禁用 OpenGL ES 某个方面的特性
  • glHint() 用于对 OpenGiL ES 某方面进行修正
  • glClearColor() 用于设置 OpenGL ES “清屏”所用的颜色,4个参数分别设置红、绿、蓝、透明度值——0 为最小值,1 为最大值
  • glShadeModel() 用于设置 OpenGL ES 的阴影模式
  • glEnable() 该方法与 glDisable() 方法相对,用于启用 OpenGL ES 某方面的特性

SurfaceView 组件的大小发生改变时,系统会回调 Renderer 对象的 onSurfaceChanged() 方法,因此该方法通常用于初始化 3D场景。

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {

        // 设置 3D 视窗的大小及位置
        gl.glViewport(0, 0, width, height);
        // 将当前矩阵模式设为投影矩阵
        gl.glMatrixMode(GL10.GL_PROJECTION);
        // 初始化单位矩阵
        gl.glLoadIdentity();
        // 计算透视视窗的宽度、高度比
        float ratio = (float) width / height;
        // 调用此方法设置透视视窗的空间大小
        gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
    }
  • glViewport() 用于设置 3D 视窗的位置与大小。其中前两介参数指定该视窗的位置;后两个参数指定该视窗的宽、高
  • glMatrixMode() 用于设置视图的短阵模型。通常可接受 GL10.GL_PROJECTION(指定将屏幕设为透视图(要想看到逼真的三维物体,这是必要的),这意味着越远的东西看起来越小)、GL10.GL_MODELVIEW (将当前矩阵模式设为模型视图矩阵,这意味着任何新的变换都会影响该短阵中的所有物体)两个常量值
  • glLoadIdentity() 相当于 reset() 方法,用于初始化单位矩阵
  • glFrustumf() 用于设置透视投影的空间大小。前两个参数用干设置 X 轴上的最小坐标值、最大坐标值。中间两个参数用于设置 Y 轴上的最小坐标值、最大坐标值。后两个参数用于设置 Z 轴上所能绘制的场景深度的最小值、最大值。

gl.glFrustumf(-0.8f, 0.8f, -1f, 1f, 1f, 10f);
这意味着如果有一个二维矩形,它的4个顶点的坐标分别为 (-0.8, 1)、(0.8, 1)、(0.8. -1)、(-0.8, -1),这个矩形将会占满整个视窗

    @Override
    public void onDrawFrame(GL10 gl) {
        // 清除屏幕缓存和深度缓存
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
    }

绘制平面上的多边形

Renderer

public class MyRenderer implements GLSurfaceView.Renderer {
    float[] triangleData = new float[]{
            0.1f, 0.6f, 0.0f, // 上顶点
            -0.3f, 0.0f, 0.0f, // 左顶点
            0.3f, 0.1f, 0.0f  // 右顶点
    };
    int[] triangleColor = new int[]{
            65535, 0, 0, 0, // 上顶点红色
            0, 65535, 0, 0, // 左顶点绿色
            0, 0, 65535, 0 // 右顶点蓝色
    };
    float[] rectData = new float[]{
            0.4f, 0.4f, 0.0f, // 右上顶点
            0.4f, -0.4f, 0.0f, // 右下顶点
            -0.4f, 0.4f, 0.0f, // 左上顶点
            -0.4f, -0.4f, 0.0f // 左下顶点
    };
    int[] rectColor = new int[]{
            0, 65535, 0, 0, // 右上顶点绿色
            0, 0, 65535, 0, // 右下顶点蓝色
            65535, 0, 0, 0, // 左上顶点红色
            65535, 65535, 0, 0 // 左下顶点黄色
    };
    // 依然是正方形的4个顶点,只是顺序交换一下
    float[] rectData2 = new float[]{
            -0.4f, 0.4f, 0.0f, // 左上顶点
            0.4f, 0.4f, 0.0f, // 右上顶点
            0.4f, -0.4f, 0.0f, // 右下顶点
            -0.4f, -0.4f, 0.0f // 左下顶点
    };
    float[] pentacle = new float[]{
            0.4f, 0.4f, 0.0f,
            -0.2f, 0.3f, 0.0f,
            0.5f, 0.0f, 0f,
            -0.4f, 0.0f, 0f,
            -0.1f, -0.3f, 0f
    };
    FloatBuffer triangleDataBuffer;
    IntBuffer triangleColorBuffer;
    FloatBuffer rectDataBuffer;
    IntBuffer rectColorBuffer;
    FloatBuffer rectDataBuffer2;
    FloatBuffer pentacleBuffer;

    public MyRenderer() {
        // 将顶点位置数据数组转换成FloatBuffer
        triangleDataBuffer = floatBufferUtil(triangleData);
        rectDataBuffer = floatBufferUtil(rectData);
        rectDataBuffer2 = floatBufferUtil(rectData2);
        pentacleBuffer = floatBufferUtil(pentacle);
        // 将顶点颜色数据数组转换成IntBuffer
        triangleColorBuffer = intBufferUtil(triangleColor);
        rectColorBuffer = intBufferUtil(rectColor);
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // 关闭抗抖动
        gl.glDisable(GL10.GL_DITHER);
        // 设置系统对透视进行修正
        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT
                , GL10.GL_FASTEST);
        gl.glClearColor(0, 0, 0, 0);
        // 设置阴影平滑模式
        gl.glShadeModel(GL10.GL_SMOOTH);
        // 启用深度测试
        gl.glEnable(GL10.GL_DEPTH_TEST);
        // 设置深度测试的类型
        gl.glDepthFunc(GL10.GL_LEQUAL);
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        // 设置3D视窗的大小及位置
        gl.glViewport(0, 0, width, height);
        // 将当前矩阵模式设为投影矩阵
        gl.glMatrixMode(GL10.GL_PROJECTION);
        // 初始化单位矩阵
        gl.glLoadIdentity();
        // 计算透视视窗的宽度、高度比
        float ratio = (float) width / height;
        // 调用此方法设置透视视窗的空间大小
        gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
    }

    // 绘制图形的方法
    @Override
    public void onDrawFrame(GL10 gl) {
        // 清除屏幕缓存和深度缓存
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
        // 启用顶点坐标数据
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        // 启用顶点颜色数据
        gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
        // 设置当前矩阵堆栈为模型堆栈
        gl.glMatrixMode(GL10.GL_MODELVIEW);
        // --------------------绘制第一个图形---------------------
        // 重置当前的模型视图矩阵
        gl.glLoadIdentity();
        gl.glTranslatef(-0.32f, 0.35f, -1.2f);  // ①
        // 设置顶点的位置数据
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, triangleDataBuffer);
        // 设置顶点的颜色数据
        gl.glColorPointer(4, GL10.GL_FIXED, 0, triangleColorBuffer);
        // 根据顶点数据绘制平面图形。GL_TRIANGLES:绘制三角形
        gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);
        // --------------------绘制第二个图形---------------------
        // 重置当前的模型视图矩阵
        gl.glLoadIdentity();
        gl.glTranslatef(0.6f, 0.8f, -1.5f);

        /*
         * 设置顶点的位置数据
         * 这个方法中 pointer 参数用于指定顶点坐标值,但这里并未使用三维数组来指定每个顶点 X、Y、Z 坐标的值,pointer 依然是一个一维数组,其格式为 (x1, y1, z1, x2, y2, z2, x3, y3, z3, ... xN, yN,zN);也就是该数组里将会包含 3N 个数值,每 3 个值指定一个顶点的 X、Y、Z 坐标值。第一个参数 size 指定多少个元素指定一个顶点位置,该 size 参数通常总是 3;type 参数指定顶点坐标值的类型,如果顶点坐标值为 float 类型,则指定为GL10.GL_FLOAT;如果顶点坐标值为整数,则指定为 GL10.GL_FIXED
         * */
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, rectDataBuffer);
        /*
         * 设置顶点的颜色数据
         * 这个方法中 pointer 参数用于指定顶点的颜色值,pointer 依然是一个一维数组,其格式为 (r1, g1, b1, a1, r2, g2, b2, a2, r3, g3, b3, a3, ... rN, gN, bN, aN);也就是该数组里将会包含 4N 个数值,每 4 个值指定一个顶点颜色的红、绿、蓝、透明度的值。第一个参数 size 指定多少个元素指定一个顶点位置,该 size 参数通常总是 4;type 参数指定顶点坐标值的类型,如果顶点坐标值为 foat 类型,则指定为 GL10.GL_FLOAT;如果顶点坐标值为整数,则指定为 GLI0.GL_FIXED
         * */
        gl.glColorPointer(4, GL10.GL_FIXED, 0, rectColorBuffer);
        /*
         * 根据顶点数据绘制平面图形
         * 该方法的第一个参数指定绘制图形类型,第二个参数指定从哪个顶点开始绘制,第三个参数指定总共绘制的顶点数量。
         * GL_TRIANGLE_STRIP:用多个三角形来绘制多边形
         * */
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
        // --------------------绘制第三个图形---------------------
        // 重置当前的模型视图矩阵
        gl.glLoadIdentity();
        gl.glTranslatef(-0.4f, -0.5f, -1.5f);
        // 设置顶点的位置数据(依然使用之前的顶点颜色)
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, rectDataBuffer2);
        // 根据顶点数据绘制平面图形
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
        // --------------------绘制第四个图形---------------------
        // 重置当前的模型视图矩阵
        gl.glLoadIdentity();
        gl.glTranslatef(0.4f, -0.5f, -1.5f);
        // 设置使用纯色填充
        gl.glColor4f(1.0f, 0.2f, 0.2f, 0.0f);  // ②
        gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
        // 设置顶点的位置数据
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, pentacleBuffer);
        // 根据顶点数据绘制平面图形
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 5);
        // 绘制结束
        gl.glFinish();
        // 停用顶点坐标数据、顶点颜色数据
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
    }

    // 定义一个工具方法,将int[]数组转换为OpenGL ES所需的IntBuffer
    private IntBuffer intBufferUtil(int[] arr) {
        IntBuffer mBuffer;
        // 初始化ByteBuffer,长度为arr数组的长度*4,因为一个int占4字节
        ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
        // 数组排列用nativeOrder
        qbb.order(ByteOrder.nativeOrder());
        mBuffer = qbb.asIntBuffer();
        mBuffer.put(arr);
        mBuffer.position(0);
        return mBuffer;
    }

    // 定义一个工具方法,将float[]数组转换为OpenGL ES所需的FloatBuffer
    private FloatBuffer floatBufferUtil(float[] arr) {
        FloatBuffer mBuffer;
        // 初始化ByteBuffer,长度为arr数组的长度*4,因为一个int占4字节
        ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
        // 数组排列用nativeOrder
        qbb.order(ByteOrder.nativeOrder());
        mBuffer = qbb.asFloatBuffer();
        mBuffer.put(arr);
        mBuffer.position(0);
        return mBuffer;
    }
}

MainActivity

public class MainActivity extends AppCompatActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 创建一个GLSurfaceView,用于显示OpenGL绘制的图形
        GLSurfaceView glView = new GLSurfaceView(this);
        // 创建GLSurfaceView的内容绘制器
        MyRenderer myRender = new MyRenderer();
        // 为GLSurfaceView设置绘制器
        glView.setRenderer(myRender);
        setContentView(glView);
    }
}
image.png

旋转

glRotatef() 控制旋转

public class MyRenderer implements GLSurfaceView.Renderer {
    // 省略定义顶点坐标的代码
    float[] triangleData = new float[]{
            0.1f, 0.6f, 0.0f, // 上顶点
            -0.3f, 0.0f, 0.0f, // 左顶点
            0.3f, 0.1f, 0.0f  // 右顶点
    };
    int[] triangleColor = new int[]{
            65535, 0, 0, 0, // 上顶点红色
            0, 65535, 0, 0, // 左顶点绿色
            0, 0, 65535, 0 // 右顶点蓝色
    };
    float[] rectData = new float[]{
            0.4f, 0.4f, 0.0f, // 右上顶点
            0.4f, -0.4f, 0.0f, // 右下顶点
            -0.4f, 0.4f, 0.0f, // 左上顶点
            -0.4f, -0.4f, 0.0f // 左下顶点
    };
    int[] rectColor = new int[]{
            0, 65535, 0, 0, // 右上顶点绿色
            0, 0, 65535, 0, // 右下顶点蓝色
            65535, 0, 0, 0, // 左上顶点红色
            65535, 65535, 0, 0 // 左下顶点黄色
    };
    // 依然是正方形的4个顶点,只是顺序交换了一下
    float[] rectData2 = new float[]{
            -0.4f, 0.4f, 0.0f, // 左上顶点
            0.4f, 0.4f, 0.0f, // 右上顶点
            0.4f, -0.4f, 0.0f, // 右下顶点
            -0.4f, -0.4f, 0.0f // 左下顶点
    };
    float[] pentacle = new float[]{
            0.4f, 0.4f, 0.0f,
            -0.2f, 0.3f, 0.0f,
            0.5f, 0.0f, 0f,
            -0.4f, 0.0f, 0f,
            -0.1f, -0.3f, 0f
    };

    FloatBuffer triangleDataBuffer;
    IntBuffer triangleColorBuffer;
    FloatBuffer rectDataBuffer;
    IntBuffer rectColorBuffer;
    FloatBuffer rectDataBuffer2;
    FloatBuffer pentacleBuffer;
    // 控制旋转的角度
    private float rotate;

    public MyRenderer() {
        // 将顶点位置数据数组包装成FloatBuffer
        triangleDataBuffer = floatBufferUtil(triangleData);
        rectDataBuffer = floatBufferUtil(rectData);
        rectDataBuffer2 = floatBufferUtil(rectData2);
        pentacleBuffer = floatBufferUtil(pentacle);
        // 将顶点颜色数据数组包装成IntBuffer
        triangleColorBuffer = intBufferUtil(triangleColor);
        rectColorBuffer = intBufferUtil(rectColor);
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // 关闭抗抖动
        gl.glDisable(GL10.GL_DITHER);
        // 设置系统对透视进行修正
        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT
                , GL10.GL_FASTEST);
        gl.glClearColor(0, 0, 0, 0);
        // 设置阴影平滑模式
        gl.glShadeModel(GL10.GL_SMOOTH);
        // 启用深度测试
        gl.glEnable(GL10.GL_DEPTH_TEST);
        // 设置深度测试的类型
        gl.glDepthFunc(GL10.GL_LEQUAL);
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        // 设置3D视窗的大小及位置
        gl.glViewport(0, 0, width, height);
        // 将当前矩阵模式设为投影矩阵
        gl.glMatrixMode(GL10.GL_PROJECTION);
        // 初始化单位矩阵
        gl.glLoadIdentity();
        // 计算透视视窗的宽度、高度比
        float ratio = (float) width / height;
        // 调用此方法设置透视视窗的空间大小
        gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
    }

    // 绘制图形的方法
    @Override
    public void onDrawFrame(GL10 gl) {
        // 清除屏幕缓存和深度缓存
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
        // 启用顶点坐标数据
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        // 启用顶点颜色数据
        gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
        // 设置当前矩阵堆栈为模型堆栈
        gl.glMatrixMode(GL10.GL_MODELVIEW);
        // --------------------绘制第一个图形---------------------
        // 重置当前的模型视图矩阵
        gl.glLoadIdentity();
        gl.glTranslatef(-0.32f, 0.35f, -1.2f);
        // 设置顶点的位置数据
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, triangleDataBuffer);
        // 设置顶点的颜色数据
        gl.glColorPointer(4, GL10.GL_FIXED, 0, triangleColorBuffer);

        // 根据顶点数据绘制平面图形
        gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);
        // --------------------绘制第二个图形---------------------
        // 重置当前的模型视图矩阵
        gl.glLoadIdentity();
        gl.glTranslatef(0.6f, 0.8f, -1.5f);
        gl.glRotatef(rotate, 0f, 0f, 0.1f);
        // 设置顶点的位置数据
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, rectDataBuffer);
        // 设置顶点的颜色数据
        gl.glColorPointer(4, GL10.GL_FIXED, 0, rectColorBuffer);
        // 根据顶点数据绘制平面图形
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
        // --------------------绘制第三个图形---------------------
        // 重置当前的模型视图矩阵
        gl.glLoadIdentity();
        gl.glTranslatef(-0.4f, -0.5f, -1.5f);
        gl.glRotatef(rotate, 0f, 0.2f, 0f);
        // 设置顶点的位置数据(依然使用之前的顶点颜色)
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, rectDataBuffer2);
        // 根据顶点数据绘制平面图形
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
        // --------------------绘制第四个图形---------------------
        // 重置当前的模型视图矩阵
        gl.glLoadIdentity();
        gl.glTranslatef(0.4f, -0.5f, -1.5f);
        // 设置使用纯色填充
        gl.glColor4f(1.0f, 0.2f, 0.2f, 0.0f);
        gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
        // 设置顶点的位置数据
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, pentacleBuffer);
        // 根据顶点数据绘制平面图形
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 5);
        // 绘制结束
        gl.glFinish();
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        // 旋转角度增加1
        rotate += 1;
    }
    // 省略intBufferUtil和floatBufferUtil两个工具方法的代码

    // 定义一个工具方法,将int[]数组转换为OpenGL ES所需的IntBuffer
    private IntBuffer intBufferUtil(int[] arr) {
        IntBuffer mBuffer;
        // 初始化ByteBuffer,长度为arr数组的长度*4,因为一个int占4个字节
        ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
        // 数组排列用nativeOrder
        qbb.order(ByteOrder.nativeOrder());
        mBuffer = qbb.asIntBuffer();
        mBuffer.put(arr);
        mBuffer.position(0);
        return mBuffer;
    }

    // 定义一个工具方法,将float[]数组转换为OpenGL ES所需的FloatBuffer
    private FloatBuffer floatBufferUtil(float[] arr) {
        FloatBuffer mBuffer;
        // 初始化ByteBuffer,长度为arr数组的长度*4,因为一个int占4个字节
        ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
        // 数组排列用nativeOrder
        qbb.order(ByteOrder.nativeOrder());
        mBuffer = qbb.asFloatBuffer();
        mBuffer.put(arr);
        mBuffer.position(0);
        return mBuffer;
    }
}

绘制 3D 图形

使用 OpenGL ES 绘制 3D 图形的步骤与绘制 2D 图形的步骤大致相同,只是绘制 3D 图形需要定义更多的顶点数据,而且 3D 图形需要绘制更多的三角形。

void glDrawElements(int mode, int count, int type, java.nio.Buffer indices)

根据 indices 指定的索引点来绘制三角形。该方法的第一个参数指定绘制的图形类型,可设为 GL10.GL_TRIANGLESGL10.GL_TRIANGLE_STRIP;第二个参数指定一共包含多少个顶点。indices 参数最重要,它包装了一个长度为 3N 的数组,比如让该参数包装 {0, 2, 3, 1, 4, 5} 数组,这意味着告诉 OpenGL ES 要绘制两个三角形,第一个三角形的三个顶点为 0、2、3 顶点,第二个三角形的三个顶点为 1、4、5 顶点。

public class MyRenderer implements GLSurfaceView.Renderer {
    // 定义三棱椎的4个顶点
    float[] taperVertices = new float[]{
            0.0f, 0.5f, 0.0f,
            -0.5f, -0.5f, -0.2f,
            0.5f, -0.5f, -0.2f,
            0.0f, -0.2f, 0.2f
    };
    // 定义三棱椎的4个顶点的颜色
    int[] taperColors = new int[]{
            65535, 0, 0, 0,  // 红色
            0, 65535, 0, 0,     // 绿色
            0, 0, 65535, 0,  // 蓝色
            65535, 65535, 0, 0 //黄色
    };
    // 定义三棱椎的4个三角面
    private byte[] taperFacets = new byte[]{
            0, 1, 2, // 0、1、2三个顶点组成一个面
            0, 1, 3, // 0、1、3三个顶点组成一个面
            1, 2, 3, // 1、2、3三个顶点组成一个面
            0, 2, 3 // 0、2、3三个顶点组成一个面
    };
    // 定义立方体的8个顶点
    float[] cubeVertices = new float[]{
            // 上顶面正方形的4个顶点
            0.5f, 0.5f, 0.5f,
            0.5f, -0.5f, 0.5f,
            -0.5f, -0.5f, 0.5f,
            -0.5f, 0.5f, 0.5f,
            // 下底面正方形的4个顶点
            0.5f, 0.5f, -0.5f,
            0.5f, -0.5f, -0.5f,
            -0.5f, -0.5f, -0.5f,
            -0.5f, 0.5f, -0.5f
    };
    // 定义立方体所需要的6个面(一共是12个三角形所需的顶点)
    private byte[] cubeFacets = new byte[]{
            0, 1, 2,
            0, 2, 3,
            2, 3, 7,
            2, 6, 7,
            0, 3, 7,
            0, 4, 7,
            4, 5, 6,
            4, 6, 7,
            0, 1, 4,
            1, 4, 5,
            1, 2, 6,
            1, 5, 6
    };
    // 定义Open GL ES绘制所需要的Buffer对象
    FloatBuffer taperVerticesBuffer;
    IntBuffer taperColorsBuffer;
    ByteBuffer taperFacetsBuffer;
    FloatBuffer cubeVerticesBuffer;
    ByteBuffer cubeFacetsBuffer;
    // 控制旋转的角度
    private float rotate;

    public MyRenderer() {
        // 将三棱椎的顶点位置数据数组包装成FloatBuffer
        taperVerticesBuffer = floatBufferUtil(taperVertices);
        // 将三棱椎的4个面的数组包装成ByteBuffer
        taperFacetsBuffer = ByteBuffer.wrap(taperFacets);
        // 将三棱椎的4个定点的颜色数组包装成IntBuffer
        taperColorsBuffer = intBufferUtil(taperColors);
        // 将立方体的顶点位置数据数组包装成FloatBuffer
        cubeVerticesBuffer = floatBufferUtil(cubeVertices);
        // 将立方体的6个面(12个三角形)的数组包装成ByteBuffer
        cubeFacetsBuffer = ByteBuffer.wrap(cubeFacets);
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // 关闭抗抖动
        gl.glDisable(GL10.GL_DITHER);
        // 设置系统对透视进行修正
        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
        gl.glClearColor(0, 0, 0, 0);
        // 设置阴影平滑模式
        gl.glShadeModel(GL10.GL_SMOOTH);
        // 启用深度测试
        gl.glEnable(GL10.GL_DEPTH_TEST);
        // 设置深度测试的类型
        gl.glDepthFunc(GL10.GL_LEQUAL);
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        // 设置3D视窗的大小及位置
        gl.glViewport(0, 0, width, height);
        // 将当前矩阵模式设为投影矩阵
        gl.glMatrixMode(GL10.GL_PROJECTION);
        // 初始化单位矩阵
        gl.glLoadIdentity();
        // 计算透视视窗的宽度、高度比
        float ratio = (float) width / height;
        // 调用此方法设置透视视窗的空间大小。
        gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
    }

    // 绘制图形的方法
    @Override
    public void onDrawFrame(GL10 gl) {
        // 清除屏幕缓存和深度缓存
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
        // 启用顶点坐标数据
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        // 启用顶点颜色数据
        gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
        // 设置当前矩阵模式为模型视图。
        gl.glMatrixMode(GL10.GL_MODELVIEW);
        // --------------------绘制第一个图形---------------------
        // 重置当前的模型视图矩阵
        gl.glLoadIdentity();
        gl.glTranslatef(-0.6f, 0.0f, -1.5f);
        // 沿着Y轴旋转
        gl.glRotatef(rotate, 0f, 0.2f, 0f);
        // 设置顶点的位置数据
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, taperVerticesBuffer);
        // 设置顶点的颜色数据
        gl.glColorPointer(4, GL10.GL_FIXED, 0, taperColorsBuffer);
        // 按taperFacetsBuffer指定的面绘制三角形
        gl.glDrawElements(GL10.GL_TRIANGLE_STRIP
                , taperFacetsBuffer.remaining(),
                GL10.GL_UNSIGNED_BYTE, taperFacetsBuffer);
        // --------------------绘制第二个图形---------------------
        // 重置当前的模型视图矩阵
        gl.glLoadIdentity();
        gl.glTranslatef(0.7f, 0.0f, -2.2f);
        // 沿着Y轴旋转
        gl.glRotatef(rotate, 0f, 0.2f, 0f);
        // 沿着X轴旋转
        gl.glRotatef(rotate, 1f, 0f, 0f);
        // 设置顶点的位置数据
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, cubeVerticesBuffer);
        // 不设置顶点的颜色数据,还用以前的颜色数据
        // 按cubeFacetsBuffer指定的面绘制三角形
        gl.glDrawElements(GL10.GL_TRIANGLE_STRIP
                , cubeFacetsBuffer.remaining(),
                GL10.GL_UNSIGNED_BYTE, cubeFacetsBuffer);
        // 绘制结束
        gl.glFinish();
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        // 旋转角度增加1
        rotate += 1;
    }

    // 定义一个工具方法,将int[]数组转换为OpenGL ES所需的IntBuffer
    private IntBuffer intBufferUtil(int[] arr) {
        IntBuffer mBuffer;
        // 初始化ByteBuffer,长度为arr数组的长度*4,因为一个int占4个字节
        ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
        // 数组排列用nativeOrder
        qbb.order(ByteOrder.nativeOrder());
        mBuffer = qbb.asIntBuffer();
        mBuffer.put(arr);
        mBuffer.position(0);
        return mBuffer;
    }

    // 定义一个工具方法,将float[]数组转换为OpenGL ES所需的FloatBuffer
    private FloatBuffer floatBufferUtil(float[] arr) {
        FloatBuffer mBuffer;
        // 初始化ByteBuffer,长度为arr数组的长度*4,因为一个int占4个字节
        ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
        // 数组排列用nativeOrder
        qbb.order(ByteOrder.nativeOrder());
        mBuffer = qbb.asFloatBuffer();
        mBuffer.put(arr);
        mBuffer.position(0);
        return mBuffer;
    }
}
public class MainActivity extends AppCompatActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 创建一个GLSurfaceView,用于显示OpenGL绘制的图形
        GLSurfaceView glView = new GLSurfaceView(this);
        // 创建GLSurfaceView的内容绘制器
        MyRenderer myRender = new MyRenderer();
        // 为GLSurfaceView设置绘制器
        glView.setRenderer(myRender);
        setContentView(glView);
    }
}
image.gif

应用纹理贴图

public class MainActivity extends AppCompatActivity implements GestureDetector.OnGestureListener {
    // 定义旋转角度
    private float anglex = 0f;
    private float angley = 0f;
    static final float ROTATE_FACTOR = 60;
    // 定义手势检测器实例
    GestureDetector detector;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 创建一个GLSurfaceView,用于显示OpenGL绘制的图形
        GLSurfaceView glView = new GLSurfaceView(this);
        // 创建GLSurfaceView的内容绘制器
        MyRenderer myRender = new MyRenderer(this);
        // 为GLSurfaceView设置绘制器
        glView.setRenderer(myRender);
        setContentView(glView);
        // 创建手势检测器
        detector = new GestureDetector(this, this);
    }

    @Override
    public boolean onTouchEvent(MotionEvent me) {
        // 将该Activity上的触碰事件交给GestureDetector处理
        return detector.onTouchEvent(me);
    }

    @Override
    public boolean onFling(MotionEvent event1, MotionEvent event2,
                           float velocityX, float velocityY) {
        velocityX = velocityX > 2000 ? 2000 : velocityX;
        velocityX = velocityX < -2000 ? -2000 : velocityX;
        velocityY = velocityY > 2000 ? 2000 : velocityY;
        velocityY = velocityY < -2000 ? -2000 : velocityY;
        // 根据横向上的速度计算沿Y轴旋转的角度
        angley += velocityX * ROTATE_FACTOR / 4000;
        // 根据纵向上的速度计算沿X轴旋转的角度
        anglex += velocityY * ROTATE_FACTOR / 4000;
        return true;
    }

    @Override
    public boolean onDown(MotionEvent arg0) {
        return false;
    }

    @Override
    public void onLongPress(MotionEvent event) {
    }

    @Override
    public boolean onScroll(MotionEvent event1, MotionEvent event2,
                            float distanceX, float distanceY) {
        return false;
    }

    @Override
    public void onShowPress(MotionEvent event) {
    }

    @Override
    public boolean onSingleTapUp(MotionEvent event) {
        return false;
    }

    public class MyRenderer implements GLSurfaceView.Renderer {
        // 立方体的顶点坐标(一共是36个顶点,组成12个三角形)
        private float[] cubeVertices = {-0.6f, -0.6f, -0.6f, -0.6f, 0.6f,
                -0.6f, 0.6f, 0.6f, -0.6f, 0.6f, 0.6f, -0.6f, 0.6f, -0.6f, -0.6f,
                -0.6f, -0.6f, -0.6f, -0.6f, -0.6f, 0.6f, 0.6f, -0.6f, 0.6f, 0.6f,
                0.6f, 0.6f, 0.6f, 0.6f, 0.6f, -0.6f, 0.6f, 0.6f, -0.6f, -0.6f,
                0.6f, -0.6f, -0.6f, -0.6f, 0.6f, -0.6f, -0.6f, 0.6f, -0.6f, 0.6f,
                0.6f, -0.6f, 0.6f, -0.6f, -0.6f, 0.6f, -0.6f, -0.6f, -0.6f, 0.6f,
                -0.6f, -0.6f, 0.6f, 0.6f, -0.6f, 0.6f, 0.6f, 0.6f, 0.6f, 0.6f,
                0.6f, 0.6f, -0.6f, 0.6f, 0.6f, -0.6f, -0.6f, 0.6f, 0.6f, -0.6f,
                -0.6f, 0.6f, -0.6f, -0.6f, 0.6f, 0.6f, -0.6f, 0.6f, 0.6f, 0.6f,
                0.6f, 0.6f, 0.6f, 0.6f, -0.6f, -0.6f, 0.6f, -0.6f, -0.6f, -0.6f,
                -0.6f, -0.6f, -0.6f, 0.6f, -0.6f, -0.6f, 0.6f, -0.6f, 0.6f, 0.6f,
                -0.6f, 0.6f, -0.6f,};
        // 定义立方体所需要的6个面(一共是12个三角形所需的顶点)
        private byte[] cubeFacets = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
                13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
                30, 31, 32, 33, 34, 35,};
        // 定义纹理贴图的坐标数据
        private float[] cubeTextures = {1.0000f, 1.0000f, 1.0000f, 0.0000f,
                0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f, 1.0000f,
                1.0000f, 0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f,
                1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f, 0.0000f,
                1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f, 1.0000f, 0.0000f,
                0.0000f, 0.0000f, 0.0000f, 1.0000f, 0.0000f, 1.0000f, 1.0000f,
                1.0000f, 1.0000f, 0.0000f, 1.0000f, 0.0000f, 0.0000f, 0.0000f,
                0.0000f, 1.0000f, 0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f,
                0.0000f, 1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f,
                0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f, 1.0000f,
                0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f};
        private Context context;
        private FloatBuffer cubeVerticesBuffer;
        private ByteBuffer cubeFacetsBuffer;
        private FloatBuffer cubeTexturesBuffer;
        // 定义本程序所使用的纹理
        private int texture;

        public MyRenderer(Context main) {
            this.context = main;
            // 将立方体的顶点位置数据数组包装成FloatBuffer;
            cubeVerticesBuffer = floatBufferUtil(cubeVertices);
            // 将立方体的6个面(12个三角形)的数组包装成ByteBuffer
            cubeFacetsBuffer = ByteBuffer.wrap(cubeFacets);
            // 将立方体的纹理贴图的坐标数据包装成FloatBuffer
            cubeTexturesBuffer = floatBufferUtil(cubeTextures);
        }

        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            // 关闭抗抖动
            gl.glDisable(GL10.GL_DITHER);
            // 设置系统对透视进行修正
            gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
            gl.glClearColor(0, 0, 0, 0);
            // 设置阴影平滑模式
            gl.glShadeModel(GL10.GL_SMOOTH);
            // 启用深度测试
            gl.glEnable(GL10.GL_DEPTH_TEST);
            // 设置深度测试的类型
            gl.glDepthFunc(GL10.GL_LEQUAL);
            // 启用2D纹理贴图
            gl.glEnable(GL10.GL_TEXTURE_2D);
            // 装载纹理
            loadTexture(gl);
        }

        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            // 设置3D视窗的大小及位置
            gl.glViewport(0, 0, width, height);
            // 将当前矩阵模式设为投影矩阵
            gl.glMatrixMode(GL10.GL_PROJECTION);
            // 初始化单位矩阵
            gl.glLoadIdentity();
            // 计算透视视窗的宽度、高度比
            float ratio = (float) width / height;
            // 调用此方法设置透视视窗的空间大小。
            gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
        }

        public void onDrawFrame(GL10 gl) {
            // 清除屏幕缓存和深度缓存
            gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
            // 启用顶点坐标数据
            gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
            // 启用贴图坐标数组数据
            gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);   // ①
            // 设置当前矩阵模式为模型视图。
            gl.glMatrixMode(GL10.GL_MODELVIEW);
            gl.glLoadIdentity();
            // 把绘图中心移入屏幕2个单位
            gl.glTranslatef(0f, 0.0f, -2.0f);
            // 旋转图形
            gl.glRotatef(angley, 0, 1, 0);
            gl.glRotatef(anglex, 1, 0, 0);
            // 设置顶点的位置数据
            gl.glVertexPointer(3, GL10.GL_FLOAT, 0, cubeVerticesBuffer);
            // 设置贴图的坐标数据
            gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, cubeTexturesBuffer);  // ②
            // 执行纹理贴图
            gl.glBindTexture(GL10.GL_TEXTURE_2D, texture);  // ③
            // 按cubeFacetsBuffer指定的面绘制三角形
            gl.glDrawElements(GL10.GL_TRIANGLES, cubeFacetsBuffer.remaining(),
                    GL10.GL_UNSIGNED_BYTE, cubeFacetsBuffer);
            // 绘制结束
            gl.glFinish();
            // 禁用顶点、纹理坐标数组
            gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
            gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
            // 递增角度值以便每次以不同角度绘制
        }

        private void loadTexture(GL10 gl) {
            Bitmap bitmap = null;
            try {
                // 加载位图
                bitmap = BitmapFactory.decodeResource(context.getResources(),
                        R.drawable.sand);
                int[] textures = new int[1];
                // 指定生成N个纹理(第一个参数指定生成一个纹理)
                // textures数组将负责存储所有纹理的代号
                // offset指定从第几个数组元素开始存放纹理代号
                gl.glGenTextures(1, textures, 0);
                // 获取textures纹理数组中的第一个纹理
                texture = textures[0];
                // 通知OpenGL将texture纹理绑定到GL10.GL_TEXTURE_2D目标中
                gl.glBindTexture(GL10.GL_TEXTURE_2D, texture);
                // 设置纹理被缩小(距离视点很远时被缩小)时的滤波方式
                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);
                // 加载位图生成纹理
                GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
            } finally {
                // 生成纹理之后,回收位图
                if (bitmap != null)
                    bitmap.recycle();
            }
        }
    }

    // 定义一个工具方法,将float[]数组转换为OpenGL ES所需的FloatBuffer
    private FloatBuffer floatBufferUtil(float[] arr) {
        FloatBuffer mBuffer;
        // 初始化ByteBuffer,长度为arr数组的长度*4,因为一个int占4个字节
        ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
        // 数组排列用nativeOrder
        qbb.order(ByteOrder.nativeOrder());
        mBuffer = qbb.asFloatBuffer();
        mBuffer.put(arr);
        mBuffer.position(0);
        return mBuffer;
    }
}

摘抄至《疯狂Android讲义(第4版)》

相关文章

  • OpenGL ES概述

    OpenGL ES Android包含支持高性能2D和3D图形绘制开源库(OpenGL),尤其是OpenGL ES...

  • Android动画

    Android动画 Android提供了多种强大的API,用于动画UI元素和绘制自定义2D和3D图形。 逐帧动画(...

  • 15 使用 Canvas 绘图

    本章内容 理解 元素 绘制简单的 2D 图形 使用 WebGL 绘制 3D 图形 这个元素负责在页面中设定一个区域...

  • OpenGL ES-09-案例05-GLSL索引绘图+颜色纹理混

    我们上一篇中介绍了2D图形的绘制,那么今天来看一下3D图形的绘制。为了使3D效果更加明显,我们增加了旋转功能,因此...

  • 动画和图形概述

    Android提供了各种强大的API,用于将动画应用于UI元素和绘制自定义2D和3D图形。 下面的部分提供了可用的...

  • Android Canvas绘图详解

    Android中使用图形处理引擎,2D部分是android SDK内部自己提供,3D部分是用Open GL ES ...

  • Android之Canvas绘图

    Android中使用图形处理引擎,2D部分是android SDK内部自己提供,3D部分是用Open GL ES ...

  • 数据分析(4)--Matplotlib入门

    一、概述 Matplotlib是python中的一个包,主要用于绘制2D图形(当然也可以绘制3D,但是需要额外安装...

  • Android 图形学原理之OpenGL ES

    Android包括使用Open Graphics Library(OpenGL®)支持高性能2D和3D图形,特别是...

  • Android 图形系统

    Android framework 为2D 和 3D 提供了各种各样的图形渲染 APIs 来与设备制造商的图形驱动...

网友评论

      本文标题:Android 绘制 2D、3D 图形

      本文链接:https://www.haomeiwen.com/subject/jgirrrtx.html