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

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种绘制,均可使用数组法.有兴趣可自己尝试.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350