OpenGL ES简介: 简述管线绘制流程

OpenGLES的术语很多,我想通过自己的语言讲的简单一些:(不知道效果怎样,有意见的同学欢迎在评论区指教)

1、确定顶点,纹理坐标:

任何的图形: 点、线、几何图形、空间几何图形,都可以用几个三角形来构成。
三个顶点可以确定一个三角形,所以通过一系列顶点坐标的组合,我们可以绘制出任何图形。


image

纹理简单来说就是一张图片。
纹理坐标就是图片上的点的坐标。
确定纹理坐标是指:顶点对应图片资源上的点的坐标。

2、顶点处理:

例如某个空间图形旋转90°,那旋转前后该空间图形的顶点的空间坐标发生了变化,懒惰的程序员是不会重新列举一遍顶点坐标的,我们会通过一个矩阵和原来的顶点坐标来计算新的顶点坐标。这一步Opengl es 通过顶点着色器来实现。

3、绘制纹理:

你可以把图片想象成块布,纹理坐标就是上面的特定位置加上图钉📌,最后把图钉按到对应的顶点坐标上。
平面上的每一个点都会对应图片上某个点,把对应点的颜色绘制到平面上,整个空间图形就有了色彩。
这一步Opengl es 通过片元着作色器实现。

记住流程,我们一步一步来调用具体api:

OpenGLES api

1、确定顶点坐标,纹理坐标:

2018080414041192.png

Opengl es 坐标系中(0,0)指的是画布的中心点,对于GLSufaceView就是View的中心点。x=1指的是View最右的位置,y=1指的是view最上的位置。(1,1)就是右上角的点。
以绘制一个平面图形为例,一个正方形由两个三角形组成,所以坐标为:

{
  -1, 1//三角形1号
  -1,-1
   1, 1

   1, 1//三角形2号
  -1,-1
   1,-1
}

OpenGL有个更简便的写法:

{
  -1, 1//三角形1号,1号点
  -1,-1//2号点
   1, 1 //3号点
   1,-1//三角形2号 ,4号点
}

方式1:太直观了,6个顶点。
方式2:前三个顶点组成一个三角形,后面每一个顶点代表一个三角形。实际上方式2的顶点数组会由OpenGL转换成方式1。
偶数点三角形的顶点为 [n-1 ,n-2 ,n],奇数点三角形的顶点为 [n-2 ,n-1 ,n]
方式2最后一个顶点是第四个,偶数点,三角形坐标:(1,1),( -1,-1),(1,-1)和方式1一样。

这里必须要注意的是:顶点数组的书写顺序是由要求的
先提个问:一个三角形有几个面?1个?再想想。
两个,正反面,OpenGL中使用顶点顺序来标识正反面。在没有通过api进行设置的情况下,OpenGL认为逆时针的顶点排序为正面,顺时针的顶点排序为反面。对于平面图形来说,渲染到反面的图像是看不见的。
所以注意,顶点的书写顺序为逆时针。

上图可以明显的看到顶点坐标对应的纹理坐标,以方法二为例:

{//顶点坐标
  -1, 1//三角形1号,1号点
  -1,-1//2号点
   1, 1 //3号点
   1,-1//三角形2号 ,4号点
}

{//纹理坐标
 0, 0//三角形1号,1号点
 0,1//2号点
 1, 0 //3号点
 1,1//三角形2号 ,4号点
} 

2、顶点处理:

上面说到OpenGL中这一步通过顶点着色器实现。那么什么是顶点着色器呢?
着色器是运行在OpenGL里通过GLSL(OpenGL Shading Language)语言编写的程序。
顶点着色器 Vertex Shader :则是一个用来操作顶点数据的着色器程序。

//这是一个最普通的顶点着色器
 String vStr =      "attribute vec4 vPosition;" +
                    "attribute vec2 vCoordinate;" +
                    "varying vec2 aCoordinate;" +
                    "uniform mat4 vMatrix;" +
                    "void main() { " +
                    "   gl_Position = vMatrix*vPosition;" +
                    "   aCoordinate=vCoordinate;" +
                    "}";

attribute vec4:attribute声明的变量需要开发者从外部程序输入一个坐标数组,这个坐标数组构成一个区域,而OpenGL会把区域中的一个点用一个多维向量(vec4就是四维(x,y,z,w),vec2就是二维(x,y))赋值给该变量。OpenGL会反复调用程序,对区域内的每一个点进行计算
uniform:声名一个值从程序外传入的变量。
mat4:表示变量是个4x4浮点矩阵,同理mat3——3x3
varying:varying变量是顶点着色器和片元着色器之间做数据传递用的。
gl_Position :顶点着色器的输出,是个vec4变量。其值决定了画笔的位置。

既然着色器是另外一种语言,想在android中运行另一种语言编写的程序,其中必有一番曲折:
1、创建一个空的OpenGLES程序

int mProgram = GLES20.glCreateProgram();

返回值是个句柄,大部分OpenGL方法的返回值都是句柄。

2、编译着色器
变成语言语言变成可运行程序必然需要编译啦。

//vStr是上面的顶点着色器程序代码,以String形式输入
int  vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vStr); 
------------------------------------------------------------------------------------
private int loadShader(int type, String str) {
      //1:创建Shader glCreateShader
     int id = GLES20.glCreateShader(type);
     //2:指定Shader源代码 glShaderSource
     GLES20.glShaderSource(id, str);
     // 3:编译Shader glCompileShader
     GLES20.glCompileShader(id);
     // 4:获取shader状态 glGetShaderiv
     private int[] compile = new int[1];//入参
     GLES20.glGetShaderiv(id, GLES20.GL_COMPILE_STATUS, compile, 0);
     // 5:如果出错 就 获取shader日志信息 glGetShaderInfoLog
    if (compile[0] == GLES20.GL_FALSE) {
         Log.e("ES20_ERROR", GLES20.glGetShaderInfoLog(id));
         GLES20.glDeleteShader(id);
         return -1;
     } else {
         return id;
     }
}

参数str指的是我们用GLSL语言书写的着色器程序文本字符串。
GLES20.GL_VERTEX_SHADER:指的是当前编译的着色器类型——为顶点着色器。

3、将顶点着色器加入到程序

GLES20.glAttachShader(mProgram, vertexShader);

4、指定当前使用的Program

GLES20.glLinkProgram(mProgram);

5、为着色器中的变量赋值

glPosition = GLES20.glGetAttribLocation(mProgram, "vPosition");//获取句柄,获取一次即可
glHCoordinate = GLES20.glGetAttribLocation(mProgram, "vCoordinate");
—————————————————————————————————————— 
GLES20.glEnableVertexAttribArray(glPosition);//激活,绘制开始时调用
GLES20.glVertexAttribPointer(glPosition, 2, GLES20.GL_FLOAT, false, 0, vertexBuffer); //输入顶点坐标数据
GLES20.glEnableVertexAttribArray(glHCoordinate);
GLES20.glVertexAttribPointer(glHCoordinate, 2, GLES20.GL_FLOAT, false, 0, textureBuffer);//输入纹理坐标数据
——————————————————————————————
GLES20.glDisableVertexAttribArray(glPosition);//绘制完成后调用
GLES20.glDisableVertexAttribArray(glHCoordinate);//绘制完成后调用

说一下 glVertexAttribPointer:
第一个参数为目标参数句柄。
第二个参数2表示的是:坐标的维数,2就是2维(x,y)。
第三个参数GLES20.GL_FLOAT表示输入数据的精度。
第四个参数定义是否希望数据被标准化(归一化)。
第五个参数数是步长(Stride),指定在连续的顶点属性之间的间隔。如果传1取值方式为0123、1234、2345……
最后一个即顶点坐标数组数据,以 java.nio.Buffer形式输入。还有另外一种输入方式这里暂不讨论

vertices={
  -1, 1//三角形1号,1号点
  -1,-1//2号点
   1, 1 //3号点
   1,-1//三角形2号 ,4号点
}


ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
vbb.order(ByteOrder.nativeOrder());
FloatBuffer  vertexBuffer = vbb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);

3、绘制纹理:

绘制纹理OpenGL 通过片元着色器实现,下面是一个简单的片元着色器:

String fStr =
            " precision mediump float; " +
                    "uniform sampler2D vTexture; " +
                    "varying vec2 aCoordinate;  " +
                    "void main() {" +
                    "   gl_FragColor=texture2D(vTexture,aCoordinate);" +
                    "}";

sampler2D:一个纹理引用
aCoordinate:由顶点作色器传过来的纹理坐标
texture2D()函数:从纹理上取纹理坐标对应点的颜色。
gl_FragColor:纹理着色器的输出,也就是画笔要画的颜色
同样片元着色器也需要经过编译绑定到OpenGL的program中

int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fStr);
GLES20.glAttachShader(mProgram, fragmentShader);

需要赋值的参数只有一个uniform sampler2D vTexture

glTexture = GLES20.glGetUniformLocation(mProgram, "vTexture");//获取句柄
---------------------------------------------------------------------------------------------------
int[] texture = new int[1];
if (bitmap != null && !bitmap.isRecycled()) {
     //生成纹理
    GLES20.glGenTextures(1, texture, 0);
    //激活0号纹理单元 
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    //绑定纹理
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture[0]);
    //设置缩小过滤为使用纹理中坐标最接近的一个像素的颜色作为需要绘制的像素颜色
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
    //设置放大过滤为使用纹理中坐标最接近的若干个颜色,通过加权平均算法得到需要绘制的像素颜色
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
    //设置环绕方向S,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
    //设置环绕方向T,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
    //根据以上指定的参数,生成一个2D纹理
    GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
    return texture[0];
}
-----------------------------------------------------------------------------------------------------
GLES20.glUniform1i(glTexture, 0);//输入纹理,使用0号纹理单元
-----------------------------------------------------------------------------------------------------

这里说一下纹理单元,有点像一个类,它里面包含很多类型的纹理成员变量,比如说常用的GL_TEXTURE_2D,GL_TEXTURE_EXTERNAL_OES。

//打个比方,不是实际代码
TextureUnit{
GL_TEXTURE_2D textureA;
GL_TEXTURE_EXTERNAL_OES textureB;
}

我们只有通过glActiveTexture激活了纹理单元后才能通过glBindTexture方法对其中的成员变量进行赋值。

以上GLSL的相关流程就结束了,完成了OpenGL管线的配置,但是要开始实际的绘制,我们还要调用绘制的api

GLES20.glDrawElements(GLES20.GL_TRIANGLES, indices.length, GLES20.GL_UNSIGNED_SHORT, indexBuffer);//绘制调用方式1
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);//绘制调用方式2,参数4代表输入顶点数组的长度

glDrawElements 需要额外输入索引数组 (上面的indexBuffer),其内容指的是顶点的绘制顺序。

{//顶点坐标
  -1, 1//三角形1号,1号点
  -1,-1//2号点
   1, 1 //3号点
   1,-1//三角形2号 ,4号点
}

{
  0,1,2,  
  1,3,2
 }

以上接口都需要在GLThread中调用,通过GLSufaceVIew可以简单创建GLThread环境:
OpenGL简介:简单的使用GLSufaceView

我在学习android 的OpenGL时写的小栗子
https://github.com/UniqueKenzhang/AllAboutVideo

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