美文网首页
Android-openGL ES绘制点.线段.三角形

Android-openGL ES绘制点.线段.三角形

作者: 犀利的小眼神 | 来源:发表于2018-03-21 09:51 被阅读367次

    openGL ES绘制的基本流程

    1.创建一个类,继承自GLSurfaceView
    2.创建渲染器Render,实现三个方法
    3.创建绘制对象,初始化顶点数据
    4.开始绘制

    准备工作

    首先创建一个类,继承自GLSurfaceView,重写两个参数构造方法.跟自定义View一样,一个参数构造是为在代码中new的形式创建对象,两个参数构造是为在XML中创建对象.
    在构造方法中建立init方法,准备初始化工作.

    1. 创建view对象
    public class EGLView extends GLSurfaceView {
    
        public EGLView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        private void init() {
            EGLRenderer renderer = new EGLRenderer();//创建渲染器
            setRenderer(renderer);//设置渲染器
            setRenderMode(RENDERMODE_CONTINUOUSLY);//设置渲染模式为主动渲染
        }
    
        //创建渲染器Rander对象
        private class EGLRenderer implements Renderer {
           private SanJiaoXing sanJiaoXing;
           @Override
            public void onSurfaceCreated(GL10 gl, EGLConfig config) {
                gl.glClearColor(0, 1, 1, 1);//设置背景色
                //也在这里对绘制对象进行初始化
                //此处以三角形为例,绘制其他,替换掉即可
                sanJiaoXing = new SanJiaoXing();
            }
    
            @Override
            public void onSurfaceChanged(GL10 gl, int width, int height) {
                gl.glViewport(0, 0, width, height);//设置视口为整个屏幕,前两参数是屏幕原点,后两个参数为屏幕宽高
                gl.glMatrixMode(GL10.GL_PROJECTION);//设置投影矩阵
                gl.glLoadIdentity();//使矩阵生效
                float r = (float) width / height;//获得宽高比
                gl.glFrustumf(-r, r, -1, 1, 1, 20);//设置视角大小
            }
    
            @Override
            public void onDrawFrame(GL10 gl) {
    
                gl.glClear(GL10.GL_DEPTH_BUFFER_BIT | GL10.GL_COLOR_BUFFER_BIT);//清理深度缓存和颜色缓存
                gl.glMatrixMode(GL10.GL_MODELVIEW);//设置模型矩阵
                gl.glLoadIdentity();//使矩阵生效
                //为防止渲染图像渲染在近平面之前,我们对图像进行Z轴平移
                gl.glTranslatef(0, 0, -1.5f);//z轴平移1.5
    
                //在这里进行绘制
                //此处以三角形为例,绘制其他,替换掉即可
                sanJiaoXing.drawSelf(gl);
            }
      }
    }
    
    
    宽高比.png

    视口大小中,我们获得的宽高比:

    为了计算方便,我们将高度H设置为单位长度1,则宽度应为r = W/H.
    设中心点为0,那么,左右上下分别为-r,r,1,-1.

    投影分类:正交投影/透视投影

    正交投影:没有近大远小效果


    正交投影.png

    透视投影:有近大远小效果,视野更宽广


    透视投影.png
    Render类

    我们继承Render,实现了三个方法,分别为

    onSurfaceCreated
    onSurfaceChanged
    onDrawFrame

    onSurfaceCreated只在第一次初始化的时候调用,初始化结束后会调用onSurfaceChanged.每次渲染时会调用onDrawFrame.

    渲染分为主动渲染(RENDERMODE_CONTINOUSLY)和等待渲染(RENDERMODE_WHEN_DIRTY).
    主动渲染就是一直不停的在渲染,会持续调用onDrawFrame进行绘制.
    等待渲染只有在调用requestRender()方法时才会唤醒onDrawFrame进行绘制.

    我们在init()方法中对Render进行初始化.

    1. 在Activity的xml里引用刚才建立的view
    <?xml version="1.0" encoding="utf-8"?>
    <com.otitan.opengltest.view.EGLView
        android:id="@+id/igl_view"
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.otitan.opengltest.MainActivity">
    
    </com.otitan.opengltest.view.EGLView>
    
    
    1. 准备的工具类(青坏坏版),将数组放入缓冲区
    public abstract class OpenGLUtils {
    
        public FloatBuffer getFloatbuffer(float[] ver) {
            ByteBuffer vbb = ByteBuffer.allocateDirect(ver.length * 4);
            vbb.order(ByteOrder.nativeOrder());
            FloatBuffer buffer = vbb.asFloatBuffer();
            buffer.put(ver);
            buffer.position(0);
            return buffer;
        }
    
        public ByteBuffer getByteBuffer(byte[] indices) {
            //创建三角形构造索引数据缓冲
            ByteBuffer indexBuffer = ByteBuffer.allocateDirect(indices.length);
            indexBuffer.put(indices);
            indexBuffer.position(0);
            return indexBuffer;
        }
    
        public IntBuffer getIntBuffer(int[] ver) {
            //创建顶点坐标数据缓存,由于不同平台字节顺序不同,数据单元不是字节的
            // 一定要经过ByteBuffer转换,关键是通过ByteOrder设置nativeOrder()
            //一个整数四个字节,根据最新分配的内存块来创建一个有向的字节缓冲
            ByteBuffer vbb = ByteBuffer.allocateDirect(ver.length * 4);
            vbb.order(ByteOrder.nativeOrder());//设置这个字节缓冲的字节顺序为本地平台的字节顺序
            IntBuffer intBuffer = vbb.asIntBuffer();//转换为int型缓冲
            intBuffer.put(ver);//向缓冲区中放入顶点坐标数据
            intBuffer.position(0);//设置缓冲区的起始位置
            return intBuffer;
        }
    }
    
    1. 创建绘制对象
    public class SanJiaoXing extends OpenGLUtils {
    
      public SanJiaoXing() {
            init();
        }
    
      private void init() {
    
      }
    
      public void drawSelf(GL10 gl) {
      }
    
    }
    

    开始绘制

    绘制点

    import com.otitan.opengltest.utils.OpenGLUtils;
    
    import java.nio.ByteBuffer;
    import java.nio.IntBuffer;
    
    import javax.microedition.khronos.opengles.GL10;
    
    public class Points extends OpenGLUtils {
    
        private IntBuffer verBuffer;
        private IntBuffer colorBuffer;
        private ByteBuffer indexBuffer;
    
        public Points () {
            init();
        }
    
        private void init() {
            //顶点数据
    
            int UNIT_SIZE = 10000;//比例
            int var[] = new int[]{//初始化6个点,每三个一组(x,y,z坐标)
                    -3 * UNIT_SIZE, -2 * UNIT_SIZE, 0,
                    0, -2 * UNIT_SIZE, 0,
                    -1 * UNIT_SIZE, 1 * UNIT_SIZE, 0,
                    2 * UNIT_SIZE, -3 * UNIT_SIZE, 0,
                    1 * UNIT_SIZE, 2 * UNIT_SIZE, 0,
    
                    2 * UNIT_SIZE, 1 * UNIT_SIZE, 0
            };
    
            verBuffer = getIntBuffer(var);//创建数据缓冲
    
            //颜色数据
            int one = 65536;//支持65536色彩通道
            //顶点个数=颜色个数
            //颜色数据(RGB A)
            int color[] = new int[]{
                    one, 0, 0, 0,
                    one, 0, 0, 0,
                    one, 0, 0, 0,
                    one, 0, 0, 0,
                    one, 0, 0, 0,
                    one, 0, 0, 0
            };
            colorBuffer = getIntBuffer(color);
    
            //创建索引数组
            byte index[] = new byte[]{
                    0, 1, 2, 3, 4, 5
            };
    
            indexBuffer = getByteBuffer(index);//注意此处为byte类型
        }
    
        //绘制自身
        public void drawSelf(GL10 gl) {
            //启用顶点数组坐标
            gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
            //启用顶点颜色数组
            gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
    
            //设置画笔,给画笔设置顶点数据
            gl.glVertexPointer(3,//verBuffer中多少个数据是一个顶点
              GL10.GL_FIXED,//数据类型-这个代表整形
              0,//顶点间隔,默认0
              verBuffer );//顶点数据
    
            //给画笔指定顶点颜色数据
            gl.glColorPointer(4,GL10.GL_FIXED,0,colorBuffer);//同顶点数据
    
            gl.glPointSize(20);//设置点大小,太小的话看不见
            gl.glDrawElements(GL10.GL_POINTS,//绘制类型
                   6, //绘制顶点个数,即索引数组中点个数,可小于数组长度
                   GL10.GL_UNSIGNED_BYTE, indexBuffer);
        }
    }
    
    

    点绘制效果如下:


    点.jpg

    绘制线段

    import com.otitan.opengltest.utils.OpenGLUtils;
    
    import java.nio.ByteBuffer;
    import java.nio.IntBuffer;
    
    import javax.microedition.khronos.opengles.GL10;
    
    public class Line extends OpenGLUtils {
    
        private IntBuffer verBuffer;
        private IntBuffer colorBuffer;
        private ByteBuffer indexBuffer;
    
        public Line() {
            init();
        }
    
        private void init() {
            //顶点数据
    
            int UNIT_SIZE = 10000;
            int var[] = new int[]{
                    -3 * UNIT_SIZE, -2 * UNIT_SIZE, 0,
                    0, -2 * UNIT_SIZE, 0,
                    -1 * UNIT_SIZE, 1 * UNIT_SIZE, 0,
                    2 * UNIT_SIZE, -3 * UNIT_SIZE, 0,
                    1 * UNIT_SIZE, 2 * UNIT_SIZE, 0,
    
                    2 * UNIT_SIZE, 1 * UNIT_SIZE, 0
            };
    
            verBuffer = getIntBuffer(var);
    
            //颜色数据
            int one = 65536;
            int color[] = new int[]{
                    one, 0, 0, 0,
                    one, 0, 0, 0,
                    one, 0, 0, 0,
                    one, 0, 0, 0,
                    one, 0, 0, 0,
                    one, 0, 0, 0
            };
            colorBuffer = getIntBuffer(color);
    
            //创建索引数组
            byte index[] = new byte[]{
                    0, 1, 2, 3, 4, 5
            };
    
            indexBuffer = getByteBuffer(index);
        }
    
        public void drawSelf(GL10 gl) {
            gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
            gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
    
            gl.glVertexPointer(3, GL10.GL_FIXED, 0, verBuffer);
    
            gl.glColorPointer(4, GL10.GL_FIXED, 0, colorBuffer);
    
            gl.glLineWidth(30);//设置线宽,线太窄时看不见
            gl.glDrawElements(GL10.GL_LINES, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);
        }
    }
    
    

    正常绘制:两个点一组进行点的绘制,如果只有一个点就会舍弃这个点

    正常绘制效果如下:
    gl.glDrawElements(GL10.GL_LINES, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);

    正常绘制.png

    条带线:按照顶点顺序连接顶点

    gl.glDrawElements(GL10.GL_LINE_STRIP, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);
    条带线绘制效果如下:

    line_strip.jpg

    循环线:按照顶点顺序连接顶点,最后一个点连接第一点
    gl.glDrawElements(GL10.GL_LINE_LOOP, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);
    循环线效果如下:

    循环线.jpg

    绘制三角形

    import com.otitan.opengltest.utils.OpenGLUtils;
    
    import java.nio.ByteBuffer;
    import java.nio.IntBuffer;
    
    import javax.microedition.khronos.opengles.GL10;
    
    
    public class SanJiaoXing extends OpenGLUtils {
    
        private IntBuffer verBuffer;
        private IntBuffer colorBuffer;
        private ByteBuffer indexBuffer;
    
        public SanJiaoXing() {
            init();
        }
    
        private void init() {
            //顶点数据
    
            int UNIT_SIZE = 10000;
            int var[] = new int[]{
                    -3 * UNIT_SIZE, -2 * UNIT_SIZE, 0,
                    0, -2 * UNIT_SIZE, 0,
                    -1 * UNIT_SIZE, 1 * UNIT_SIZE, 0,
                    2 * UNIT_SIZE, -3 * UNIT_SIZE, 0,
                    1 * UNIT_SIZE, 2 * UNIT_SIZE, 0,
                    2 * UNIT_SIZE, 1 * UNIT_SIZE, 0
            };
    
            verBuffer = getIntBuffer(var);
    
            //颜色数据
            int one = 65536;
            int color[] = new int[]{
                    one, 0, 0, 0,
                    one, 0, 0, 0,
                    one, 0, 0, 0,
                    one, 0, 0, 0,
                    one, 0, 0, 0,
                    one, 0, 0, 0
            };
            colorBuffer = getIntBuffer(color);
    
            //创建索引数组,即绘制点位的顺序,此处为顺序绘制,亦可乱序绘制
            //如1,0,3,2,5,4
            //顺序不同,绘制效果亦不同
            byte index[] = new byte[]{
                    0, 1, 2, 3, 4, 5
            };
    
            indexBuffer = getByteBuffer(index);
        }
    
        public void drawSelf(GL10 gl) {
            //启用顶点数组坐标
            gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
            //启用顶点颜色数组
            gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
    
            //设置画笔,给画笔设置顶点数据
            gl.glVertexPointer(3,//verBuffer中多少个数据是一个顶点
              GL10.GL_FIXED,//数据类型-这个代表整形
              0,//顶点间隔,默认0
              verBuffer );//顶点数据
    
            //给画笔指定顶点颜色数据
            gl.glColorPointer(4,GL10.GL_FIXED,0,colorBuffer);//同顶点数据
    
            //索引法绘制 
            gl.glDrawElements(GL10.GL_TRIANGLES,//绘制模型(点1,线段3,三角形3)
              6,//绘制顶点个数,即索引数组中点个数,可小于数组长度
              GL10.GL_UNSIGNED_BYTE,indexBuffer);
        }
    }
    
    

    三角形绘制效果:

    正常效果,三个点一组,如果不够三个点就会舍弃多余的点.
    如下面两图,上面是当绘制5个点时的效果,下面为绘制6个点时效果.

    gl.glDrawElements(GL10.GL_TRIANGLES, 5, GL10.GL_UNSIGNED_BYTE, indexBuffer);
    5个点效果如下:

    GL_TRIANGLES.png
    gl.glDrawElements(GL10.GL_TRIANGLES, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);
    6个点效果如下:
    GL_TRIANGLES 2.png

    三角形带:顶点按照顺序依次 组成三角形绘制,最后实际形成的是一个三角型带!

    gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);
    三角形带效果如下:

    _STRIP.png

    三角形扇面:将第一个点作为中心点,其他点作为边缘点,绘制一系列的组成扇形的三角形

    gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);
    三角形扇面效果如下:

    _FAN.png

    以上均为索引法绘制,下面展示下数组法绘制:

    package com.otitan.opengltest.draw;
    
    import com.otitan.opengltest.utils.OpenGLUtils;
    
    import java.nio.ByteBuffer;
    import java.nio.IntBuffer;
    
    import javax.microedition.khronos.opengles.GL10;
    
    public class SanJiaoXing extends OpenGLUtils {
    
        private IntBuffer verBuffer;
        private IntBuffer colorBuffer;
        //private ByteBuffer indexBuffer;
    
        public SanJiaoXing() {
            init();
        }
    
        private void init() {
            //顶点数据
    
            int UNIT_SIZE = 10000;
            int var[] = new int[]{
                    -3 * UNIT_SIZE, -2 * UNIT_SIZE, 0,
                    0, -2 * UNIT_SIZE, 0,
                    -1 * UNIT_SIZE, 1 * UNIT_SIZE, 0,
                    2 * UNIT_SIZE, -3 * UNIT_SIZE, 0,
                    1 * UNIT_SIZE, 2 * UNIT_SIZE, 0,
    
                    2 * UNIT_SIZE, 1 * UNIT_SIZE, 0
            };
    
            verBuffer = getIntBuffer(var);
    
            //颜色数据
            int one = 65536;
            int color[] = new int[]{
                    one, 0, 0, 0,
                    one, 0, 0, 0,
                    one, 0, 0, 0,
                    one, 0, 0, 0,
                    one, 0, 0, 0,
                    one, 0, 0, 0
            };
            colorBuffer = getIntBuffer(color);
    
            //使用数组法,索引值就不需要了
            //创建索引数组
            //byte index[] = new byte[]{
            //       0, 1, 2, 3, 4, 5
            //};
    
            //indexBuffer = getByteBuffer(index);
        }
    
        public void drawSelf(GL10 gl) {
            gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
            gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
    
            gl.glVertexPointer(3, GL10.GL_FIXED, 0, verBuffer);
    
            gl.glColorPointer(4, GL10.GL_FIXED, 0, colorBuffer);
    
            //此为索引法绘制
            //gl.glDrawElements(GL10.GL_TRIANGLE_FAN, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);
    
    
            //数组法绘制
            gl.glDrawArrays(
                 GL10.GL_TRIANGLE_FAN,//绘制类型
                 0, //起始值,即顶点数据三个数据为一组,从哪个组开始绘制
                 6);//绘制个数
        }
    }
    
    

    效果和之前的扇面三角形是一样的,不再重复展示.上面7种绘制,均可使用数组法.有兴趣可自己尝试.

    相关文章

      网友评论

          本文标题:Android-openGL ES绘制点.线段.三角形

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