aliang study opengles之opengles开篇

Android Opengles开篇

前言:opengles感觉算是一个很"古老"的技术了,从1.0-2.0-3.0经历 了三个版本了
本篇及以后讲述的都是建立在opengles2.0基础上进行demo演示的。可能有些人
对opengl比较陌生,但在Android手机当中和图像相关的东西,都会涉及到Opengl。

一.什么是Opengles?

Opengl这东西是有的,为什么要在后面加上一个ES呢?它主要是针对于嵌入式设备,对原本Opengl
进行了裁剪的缩减库.Opengles的功能比Opengl要少一些。说白了就是Opengles中你要写的代码就更多了,
因为在Opengl中有的一些基本图形被缩减到只有点,线,三角形了。


二.废话就不. 多说了,开篇就用opengles画个三角形,对其中的基本概念进行叙述。

开发工具就用是Android studio2.1
在AndroidStudio中有个GLSL的插件,可以识别着色器语言的.
GLSL是opengles中一个关键的文件,它主要是分为Vertex(顶点)Shader的glsl以及Fragment(片段)Shader的glsl.对它们的编写就决定最终物体相关属性是怎么设置的了。
对于GLES的说明会放到后面进行。这里画三角形,对GLSL编写也是很简单的,就直接在代码里加注释说明。


以建造房子为例子来阐述一个三角形的绘制

三.首先是GLES的简单编写

1.画笔准备:

Vertex和Frament在opengles中是以字符串的形式被载入,所以在assets里面创建两个资源文件
vertex.glsl和frag.glsl

vertex.glsl

uniform mat4 uMVPMatrix;  //顶点最终变换矩阵
attribute vec3 aPostion;  //顶点坐标值(x,y,z)
attribute vec4 aColor;  //顶点的颜色(R,G,B,A)
varying vec4 vColor;   //传递给片段着色器的颜色值,varying声明的变量都是要传递给fragment的

void main(){
    //gl_Position是glsl的内置变量,记录顶点最终的位置 。
    //vec4(aPostion,1)为是矩阵想成匹配
    gl_Position = uMVPMatrix * vec4(aPostion,1);
    vColor = aColor;//将顶点颜色值传递给fragment
}

frag.glsl

precision mediump float;  //声明float的精度,一般情况下都是用mediump的
varying vec4 vColor; //接收从顶点glsl传过来的颜色参数
//对片段颜色的具体处理
void main(){
    //直接将顶点传过来的颜色参数赋值给了内置变量gl_fragColor,也就给fragment上色了
    gl_FragColor = vColor;
}

2.将shader(glsl)载入到系统中做画画准备:

帮助类的编写,这个是固定的模板,以后都可以直接进行复用的.
这个帮主类的简单流程如下:

  1. 将asset中的vertext.glsl和frag.glsl
    1. 方法名为:public static String loadFromAssetsFile(String fname, Resources r);
  2. 获取vertex的ID和fragment的ID
    1. 方法名为:public static int loadShader(int shaderType, String source )
  3. 通过上面获得的ID,将vertext和fragment组合成programe
    1. public static int createProgram(String vertexSource, String fragmentSource)
帮助类ShaderUtil的具体代码
package com.bn.Sample3_1;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;

import android.content.res.Resources;
import android.opengl.GLES20;
import android.util.Log;

//加载顶点Shader与片元Shader的工具类
public class ShaderUtil {
    // 加载制定shader的方法
    public static int loadShader(int shaderType, // shader的类型
                                                    // GLES20.GL_VERTEX_SHADER(顶点)
                                                    // GLES20.GL_FRAGMENT_SHADER(片元)
            String source // shader的脚本字符串
    ) {
        // 创建一个新shader
        int shader = GLES20.glCreateShader(shaderType);
        // 若创建成功则加载shader
        if (shader != 0) {
            // 加载shader的源代码
            GLES20.glShaderSource(shader, source);
            // 编译shader
            GLES20.glCompileShader(shader);
            // 存放编译成功shader数量的数组
            int[] compiled = new int[1];
            // 获取Shader的编译情况
            GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
            if (compiled[0] == 0) {// 若编译失败则显示错误日志并删除此shader
                Log.e("ES20_ERROR", "Could not compile shader " + shaderType
                        + ":");
                Log.e("ES20_ERROR", GLES20.glGetShaderInfoLog(shader));
                GLES20.glDeleteShader(shader);
                shader = 0;
            }
        }
        return shader;
    }

    // 创建shader程序的方法
    public static int createProgram(String vertexSource, String fragmentSource) {
        // 加载顶点着色器
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
        if (vertexShader == 0) {
            return 0;
        }

        // 加载片元着色器
        int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
        if (pixelShader == 0) {
            return 0;
        }

        // 创建程序
        int program = GLES20.glCreateProgram();
        // 若程序创建成功则向程序中加入顶点着色器与片元着色器
        if (program != 0) {
            // 向程序中加入顶点着色器
            GLES20.glAttachShader(program, vertexShader);
            checkGlError("glAttachShader");
            // 向程序中加入片元着色器
            GLES20.glAttachShader(program, pixelShader);
            checkGlError("glAttachShader");
            // 链接程序
            GLES20.glLinkProgram(program);
            // 存放链接成功program数量的数组
            int[] linkStatus = new int[1];
            // 获取program的链接情况
            GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
            // 若链接失败则报错并删除程序
            if (linkStatus[0] != GLES20.GL_TRUE) {
                Log.e("ES20_ERROR", "Could not link program: ");
                Log.e("ES20_ERROR", GLES20.glGetProgramInfoLog(program));
                GLES20.glDeleteProgram(program);
                program = 0;
            }
        }
        return program;
    }

    // 检查每一步操作是否有错误的方法
    public static void checkGlError(String op) {
        int error;
        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
            Log.e("ES20_ERROR", op + ": glError " + error);
            throw new RuntimeException(op + ": glError " + error);
        }
    }

    // 从sh脚本中加载shader内容的方法
    public static String loadFromAssetsFile(String fname, Resources r) {
        String result = null;
        try {
            InputStream in = r.getAssets().open(fname);
            int ch = 0;
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            while ((ch = in.read()) != -1) {
                baos.write(ch);
            }
            byte[] buff = baos.toByteArray();
            baos.close();
            in.close();
            result = new String(buff, "UTF-8");
            result = result.replaceAll("\\r\\n", "\n");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}

准备工作做完,就需要开始绘制三角形了。

3.Trangle 三角形类编写

注意事项:1.opengles采用的是左手坐标系x->大拇指,y->中指,z->食指
2.其中涉及到的一些矩阵,后续会讲到。

//三角形
public class Triangle
{
    public static float[] mProjMatrix = new float[16];//4x4矩阵 投影用
    public static float[] mVMatrix = new float[16];//摄像机位置朝向9参数矩阵
    public static float[] mMVPMatrix;//最后起作用的总变换矩阵
    
    int mProgram;//自定义渲染管线程序id
    int muMVPMatrixHandle;//总变换矩阵引用id
    int maPositionHandle; //顶点位置属性引用id  
    int maColorHandle; //顶点颜色属性引用id  
    String mVertexShader;//顶点着色器         
    String mFragmentShader;//片元着色器
    static float[] mMMatrix = new float[16];//具体物体的移动旋转矩阵,旋转、平移
    
    FloatBuffer   mVertexBuffer;//顶点坐标数据缓冲
    FloatBuffer   mColorBuffer;//顶点着色数据缓冲
    int vCount=0;   
    float xAngle=0;//绕x轴旋转的角度
    public Triangle(MyTDView mv)
    {       
        //初始化顶点坐标与着色数据
        initVertexData();
        //初始化shader
        initShader(mv);
    }
   
    public void initVertexData()
    {
        //顶点坐标数据的初始化
        vCount=3;  
        final float UNIT_SIZE=0.2f;
        float vertices[]=new float[]
        {
            -4*UNIT_SIZE,0,
            0,0,-4*UNIT_SIZE,
            0,4*UNIT_SIZE,0,0,
            -4*UNIT_SIZE,0,0
        };
        
        ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
        vbb.order(ByteOrder.nativeOrder());
        mVertexBuffer = vbb.asFloatBuffer();
        mVertexBuffer.put(vertices);
        mVertexBuffer.position(0);
        
        float colors[]=new float[]
        {
                1,0,0,1,
                0,0,1,0,
                0,1,0,0
        };
        
        ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4);
        cbb.order(ByteOrder.nativeOrder());
        mColorBuffer = cbb.asFloatBuffer();
        mColorBuffer.put(colors);
        mColorBuffer.position(0);
    }

    //初始化shader
    public void initShader(MyTDView mv)
    {
        //加载顶点着色器的脚本内容
        mVertexShader=ShaderUtil.loadFromAssetsFile("vertex.sh", mv.getResources());
        //加载片元着色器的脚本内容
        mFragmentShader=ShaderUtil.loadFromAssetsFile("frag.sh", mv.getResources());  
        //基于顶点着色器与片元着色器创建程序
        mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader);
        //获取程序中顶点位置属性引用id  
        maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
        //获取程序中顶点颜色属性引用id  
        maColorHandle= GLES20.glGetAttribLocation(mProgram, "aColor");
        //获取程序中总变换矩阵引用id
        muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");  
    }
    
    int i=0;
    public void drawSelf()
    {        
         //制定使用某套shader程序
         GLES20.glUseProgram(mProgram);        
         //初始化变换矩阵
         Matrix.setRotateM(mMMatrix,0,0,0,1,0);
         i=1;
         if(i == 1){
             for(int j=0;j<mMMatrix.length;j++){
                 Log.i("liang.chen","item"+j+" is:"+mMMatrix[j]);
             }
             
         }
         i++;
         
         //设置沿Z轴正向位移1
         Matrix.translateM(mMMatrix,0,0,0,-1);
         //设置绕x轴旋转
         Matrix.rotateM(mMMatrix,0,xAngle,0,0,1);
         //
         GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, Triangle.getFianlMatrix(mMMatrix), 0); 
         //为画笔指定顶点位置数据
         GLES20.glVertexAttribPointer(
                maPositionHandle,   
                3, 
                GLES20.GL_FLOAT, 
                false,
                3*4,   
                mVertexBuffer
         );
         GLES20.glVertexAttribPointer  
         (
                maColorHandle,
                4,
                GLES20.GL_FLOAT,
                false,
                4*4,
                mColorBuffer
         );
         //允许顶点位置数据数组
         GLES20.glEnableVertexAttribArray(maPositionHandle);  
         GLES20.glEnableVertexAttribArray(maColorHandle);  
         //绘制三角形
         GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount); 
    }
    public static float[] getFianlMatrix(float[] spec)
    {
        mMVPMatrix=new float[16];
        Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, spec, 0);
        Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);        
        return mMVPMatrix;
    }
}

现在对代码的==关键点==进行说明:

  • 四个重要的矩阵mMMatrix,mVMatrix,mProjMatrix,mMVPMatrix

    1. mMMatrix指的是物体本身位置,角度的状态矩阵
    2. mVMatrix指的是人眼看物体时候的位置,角度的状态矩阵
    3. mProjMatrix指的是物体成像所处的透视环境状态
    4. mMVPMatrix指的是mMMatrix,mVMatrix,mProjMatrix三者结合的矩阵
  • 其它参数说明:

    1. mVertexBuffer指的是vertex buffer ,将顶点数据灿在vertex buffer里面后后续的管线操作就可以直接从buffer取的数据,这样加快了数据的读取
    2. mColorBuffer 类似
  • 局部代码说明:

    float vertices[]=new float[]
      {
          -4*UNIT_SIZE,0,
          0,0,-4*UNIT_SIZE,
          0,4*UNIT_SIZE,0,0,
      };
    

    顶点的坐标的,前面说过了opengl采用的是左手坐标系

       float colors[]=new float[]
        {
                1,0,0,1,
                0,0,1,0,
                0,1,0,0
        };
    

    三个顶点的颜色值采用的是RGBA

    1. Matrix.setRotateM(mMMatrix,0,0,1,0,0),是将mMMatrix初始化为单位矩阵

     GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, Triangle.getFianlMatrix(mMMatrix), 0); 
         //为画笔指定顶点位置数据
         GLES20.glVertexAttribPointer(
                maPositionHandle,   
                3, 
                GLES20.GL_FLOAT, 
                false,
                3*4,   
                mVertexBuffer
         );
         GLES20.glVertexAttribPointer  
         (
                maColorHandle,
                4,
                GLES20.GL_FLOAT,
                false,
                4*4,
                mColorBuffer
         );
    

    这两个的作用是对顶点属性值进行赋值的,以第一个glVertexAttribPointer为例
    ==maPositionHandle ==是vertex shader中对应的位置属性值,==3==指的是坐标点是以顶点数组中三个值为一个顶点,==GLES20.GL_FLOAT==是顶点数组中数据类型,==34==值的是Buffer中一个顶点的大小因为三个float组成一个顶点所以是34,mVertexBuffer就是顶点坐标数组转成的buffer了

    5.GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount);
    Android的openlgles的图元有点,线,三角形,所有的图形就靠它们来组成,因为这里
    是直接画三角形,所以就用GLES20.GL_TRAINGLES

    这篇就直观上讲解了opengles绘制一个简单图形是怎么绘制的。下篇开始讲知识点。
    代码地址:http://pan.baidu.com/s/1dFcjdZj

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

推荐阅读更多精彩内容