openGL中的图元一共有三种:点、线、三角形。

针对线,openGL为我们提供了三种画线的方式:

①画线段(lines):所谓线段就是不相连的一些些线集。

iOS OpenGL ES 线条绘制 opengl画线段_数组

②画线带(line_strip):首尾相连但不闭合的线。

iOS OpenGL ES 线条绘制 opengl画线段_Math_02

③画线环(line_loop):首尾相连且闭合的线。

iOS OpenGL ES 线条绘制 opengl画线段_Math_03

首先来实现画线段,这里我们来画一个发散的射线形状,效果图如下:

iOS OpenGL ES 线条绘制 opengl画线段_Math_04

render的实现类重写的三个方法,前两个一模一样,关键看一下onDrawFrame()方法:

同样的,准备工作是,清除颜色缓冲区,设置绘图颜色,设置矩阵模式,加载单位矩阵,放置眼球位置,如有需要的话还可以设置旋转角度。

那么做好了准备工作就可以正式画射线了,首先要给线段一个长度r,再定义圆面上点的坐标,用list存放各个点坐标,发现点太多不能手动指定,所以用for循环出圆面上各个点的坐标并放进list,在将这个点放进list之前要记得把中心点(0,0,0)先放进list,这样就由两点(中心点及循环出来的圆上的点)确定了一条线,最后,再设置顶点指针和画数组,画数组时候要注意,type是GL10.GL_LINES,first还是0,count为总数除以三。


接下来实现画线带,这次用画线带的方式画一段螺旋线,效果图如下:

iOS OpenGL ES 线条绘制 opengl画线段_List_05

实现方式与线段基本类似,只不过不需要原点,再将glDrawArrays()方法中的type参数设置为GL_LINE_STRIP就OK了。

最后附代码

画线段:


public class MyLineRenderer extends AbstractRenderer {


    public void onDrawFrame(GL10 gl) {


        //清除颜色缓冲区
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
        //设置绘图颜色
        gl.glColor4f(1f,1f,1f,1f);
        //设置模型视图矩阵
        gl.glMatrixMode(GL10.GL_MODELVIEW);
        //载入单位矩阵
        gl.glLoadIdentity();
        //放置眼球位置
        GLU.gluLookAt(gl, 0f, 0f, 5f, 0f, 0f, 0f, 0f, 1f, 0f);
        //旋转角度,以便更直观查看
//        gl.glRotatef(-90,1,0,0);// 绕x轴旋转 (openGL规定,顺时针旋转为负值)
//        gl.glRotatef(yRotate,1,0,0);// 绕y轴旋转

        /**
         * 计算点的坐标
         * @param r 半径
         * @param coordsList 坐标集合
         * @param x,y,z 每个点的坐标
         * @param alpha 角度
         *
         */

        float r = 0.5f;//半径
        float x = 0f,y = 0f,z = 0f;//点的坐标
        List<Float> coordsList = new ArrayList<Float>();

        //循环绘制点
        for( float alpha = 0f; alpha < Math.PI * 6;alpha = (float) (alpha+Math.PI / 16 )){
            x = (float) (Math.cos(alpha) * r);
            y = (float) (Math.sin(alpha) * r);
            //添加原点
            coordsList.add(0f);
            coordsList.add(0f);
            coordsList.add(0f);
            //添加当前点
            coordsList.add(x);
            coordsList.add(y);
            coordsList.add(z);
        }
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, BufferUtils.list2ByteBuffer(coordsList));
        gl.glDrawArrays( GL10.GL_LINES,0,coordsList.size() / 3 );
    }
}

画线带:


public class MyLineStripRenderer extends AbstractRenderer{

    public void onDrawFrame(GL10 gl) {



        //清除颜色缓冲区
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
        //设置绘图颜色
        gl.glColor4f(1f, 1f, 1f, 1f);
        //设置模型视图矩阵
        gl.glMatrixMode(GL10.GL_MODELVIEW);
        //载入单位矩阵
        gl.glLoadIdentity();
        //放置眼球位置
        GLU.gluLookAt(gl, 0f, 0f, 5f, 0f, 0f, 0f, 0f, 1f, 0f);
        //旋转角度,以便更直观查看
        gl.glRotatef(-90, 1, 0, 0);// 绕x轴旋转 (openGL规定,顺时针旋转为负值)
        gl.glRotatef(yRotate,1,0,0);// 绕y轴旋转

        /**
         * 计算点的坐标
         * @param r 半径
         * @param coordsList 坐标集合
         * @param x,y,z 每个点的坐标
         * @param alpha 角度
         *
         */

        List<Float> coordsList = new ArrayList<Float>(); //用list存放各个点
        float r = 0.5f;//半径
        float x = 0f,y = 0f,z = 1.5f;//点的坐标
        float zStep = 0.005f;//z轴的步长
        float lsize = 1.0f;
        float lStep = 0.5f;
        //用for循环出各个点的坐标
        for( float alpha = 0f; alpha < Math.PI * 12;alpha = (float) (alpha+Math.PI / 32 )){
            x = (float) (Math.cos(alpha) * r);
            y = (float) (Math.sin(alpha) * r);
            z = z - zStep;
            gl.glLineWidth( lsize = lStep+lsize );
            coordsList.add(x);
            coordsList.add(y);
            coordsList.add(z);
        }

        gl.glVertexPointer(3,GL10.GL_FLOAT,0, BufferUtils.list2ByteBuffer(coordsList));//指定顶点指针

        /**
         * 画数组
         * 因为coordsList中用3个点存一个坐标,
         * 所以点的数量要除以3
         */
        gl.glDrawArrays( GL10.GL_LINE_STRIP,0,coordsList.size() / 3 );


    }
}

需用到的工具类

AbstactRenderer:


public abstract class AbstractRenderer implements GLSurfaceView.Renderer{
    private float ratio;//视口比例
    public float xRotate = 0f;//绕X轴旋转的角度
    public float yRotate = 0f;//绕Y轴旋转的角度

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

        /**
         * 步骤:
         * 第一步设置清屏色,第二步启用顶点缓冲数组。
         */

        //设置清屏色
        gl.glClearColor(0f, 0f, 0f, 1f);
        //启用顶点缓冲数组
        gl.glEnableClientState( GL10.GL_VERTEX_ARRAY );

    }

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

        /**步骤:
         * 第一步设置视口,再设置平截头体,而要设置平截头体就要先指定矩阵类型为投影矩阵
         * 设置过矩阵类型后,就马上要加载单位矩阵。
         *
         * 设置平截头体:
         * 为了视口所展示的画面不失真,一般设置平截头体的比例与视口比例相同。
         * 所以就要先计算视口比例,再应用到平截头体中。
         */

        //设置视口
        gl.glViewport(0,0,width,height);
        //计算视口比例
        ratio = (float)width / (float)height;
        //设置矩阵模式
        gl.glMatrixMode(GL10.GL_PROJECTION);
        //加载单位矩阵
        gl.glLoadIdentity();
        //设置平截头体
        gl.glFrustumf(ratio,-ratio,-1f,1f,3f,7f);//ratio为with/height,所以高度为,宽度为ratio

    }

    @Override
    public abstract void onDrawFrame(GL10 gl);
}

BufferUtils:


public class BufferUtils {

    /**
     *
     *
     * 将浮点数组转换成字节缓冲区
     * @param arr
     * @return
     */

    public static ByteBuffer array2ByteBuffer( float [] arr ){


        ByteBuffer ibb = ByteBuffer.allocateDirect(arr.length * 4);//一个浮点数等于四个字节
        ibb.order(ByteOrder.nativeOrder());//设置排列顺序,本地顺序
        FloatBuffer fbb = ibb.asFloatBuffer();//将字节缓冲转换成浮点缓冲
        //用增强for循环把每个坐标put进去
        fbb.put(arr);
        ibb.position(0);//把缓冲区指针位置定位到0位置

        return ibb;
    }
    /**
     *
     *
     * 将list集合转换成字节缓冲区
     * @param list
     * @return
     */
    public static ByteBuffer list2ByteBuffer( List<Float> list){


        ByteBuffer ibb = ByteBuffer.allocateDirect(list.size() * 4);//一个浮点数等于四个字节
        ibb.order(ByteOrder.nativeOrder());//设置排列顺序,本地顺序
        FloatBuffer fbb = ibb.asFloatBuffer();//将字节缓冲转换成浮点缓冲
        //用增强for循环把每个坐标put进去
        for( Float f : list ){
            fbb.put(f);
        }
        ibb.position(0);//把缓冲区指针位置定位到0位置

        return ibb;
    }

}