本文为稀土技能社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究!

经过阅览本文,你将获得以下收获:

1.进一步了解图元安装
2.开端学习片段着色器的特色和运用

上篇回顾

上一篇一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(二) 首要讲解了着色器Shader,要点讲了极点着色器,文末讲到极点着色器处理完数据之后,载着数据的小马车将从头出发,依据之前一看就懂的OpenGL ES——图形烘托管线的那些事讲的内容,数据的下一站,就到了图元安装的阶段。

一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(三)

再探图元安装

此刻故事的小马车现已到了第2个阶段:

一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(三)

之前在一看就懂的OpenGL ES——图形烘托管线的那些事一文中介绍过图元安装阶段首要作业便是依据开发者的需求将极点衔接成为一个图形,比方如上图所示将三个点衔接为一个三角形。

首先要解说清楚的一个概念便是图元(Primitive),什么是图元呢?

图元这个东西容易误解为图形,图元的确和图形休戚相关,但是图元强调的是,用什么办法去将一系列点衔接成为一个图形。

比方关于只需1个点的状况,那么图元只需一种,那便是无法做衔接操作,终究“衔接”构成的图形便是一个点。

关于2个点的情形,能够挑选为不衔接,即构成2个点,也能够挑选将2个点进行衔接,构成一条线段。

关于3个点的情形,那状况就丰富了很多,能够挑选不衔接构成3个点,也能够只衔接其中2个点构成一条线段和一个点。也能够将3个点衔接起来,构成一个三角形。

那么关于3个以上的,那能供给的衔接办法就多种多样了,OpenGL首要供给了以下的图元类型

一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(三)

假设现在传入极点着色器的极点数组的元素别离为:v0,v1,v2,v3,v4,v5,v6,v7,那么对应的图元效果图如下所示:

一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(三)

那么怎么确认图元安装的类型呢?

假设看过前面2篇文章的童鞋,可能会注意到一个办法:

glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);

这是OpenGL的制作指令,在它前面的一系列指令仅仅装备,而glDrawArrays就像一个流水线开关,一踩下去,整个流水线才真正动起来,传入OpenGL的数据才像一条河流相同在一个个阶段活动起来,直到构成的图画数据被制作到对应的帧缓冲~

该办法榜首个参数便是指定上面提到的图元类型,这儿传的是三角形条带GL_TRIANGLE_STRIP,第二个参数表明从传入的极点特色数组的第几个元素开端制作,第三个参数表明制作多少个极点特色数组元素

关于图元具体信息,能够看下官网Primitive

假设关于图元还是有点模糊,不要紧,后边会用实例一一讲解。让咱们先进入另一个主角——片段着色器(Fragment Shader)。

一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(三)

片段着色器

其实看过之前的文章的老哥应该知道,图元安装之后,还会进行光栅化的操作,这个操作非常重要,它将安装好的图元切成一块块片段(fragment),而这一块块的片段,才为后边的着色供给了根底

一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(三)

介于光栅化的内容首要是图形学算法相关的,本系列定位为入门,所以在这儿就只需理解这个阶段的效果即可。而接下来的片段着色器,才是咱们的主角,也是一个相当有意思的家伙。

效果特色

官网关于片段着色器的定义是:

AFragment Shaderis theShaderstage that will process aFragmentgenerated by theRasterizationinto a set of colors and a single depth value.

The fragment shader is the OpenGL pipeline stage after a primitive is rasterized. For each sample of the pixels covered by a primitive, a “fragment” is generated. Each fragment has aWindow Spaceposition, a few other values, and it contains all of the interpolated per-vertex output values from the lastVertex Processingstage.

The output of a fragment shader is a depth value, a possible stencil value (unmodified by the fragment shader), and zero or more color values to be potentially written to the buffers in the current framebuffers.

Fragment shaders take a single fragment as input and produce a single fragment as output.

提取核心内容便是

1.片段着色器是经过对光栅化产生的片段数据的处理,产生一个色彩集合和一个深度信息

2.每个光栅化产生的片段,会带着方位信息,以及极点着色器产生的数据的插值信息。(什么意思呢,后边会具体讲到。)

3.片段着色器的输入是一个片段,然后将该片段带着一个深度值,若干个色彩值以及可能会有的模板值,然输出后传入对应的帧缓冲。(关于深度和模板,在 一看就懂的OpenGL ES——图形烘托管线的那些事一文的测试和混合章节已有提及)

一句话便是:片段着色器便是指定当时片段色彩的,假设需求的话,还能够指定深度、模板等信息供后边阶段处理。更通俗来说便是担任染色的。

一个片段着色器代码是什么姿态的呢?(根据GLSL3.0

       #version 300 es
       precision mediump float;
       out vec4 FragColor;
        void main() {
           //给当时片段赋色彩值
           FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
        }

这是一个最根底的片段着色器,只做了一件事,便是接纳上一个阶段传过来的vec4变量,然后赋给out变量FragColor表明该片段的终究色彩。这儿在GLSL2.0中有个内置变量gl_FragColor表明该片段的终究色彩,但是在GLSL3.0现已被弃用。

榜首行 #version 300 es不必解说了,还不清楚的请出门左转至 一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(二)

第二行precision mediump float;表明精度修饰符,表明数据的精度,一般状况下,精读越高,烘托质量越高,不过性能开销就越大。这是专门为OpenGL es量身定制的修饰符,这彻底表现了Khronos安排关于嵌入式设备性能的关心和照顾。

精度修饰符的语法定义如下:

precision precision-qualifier​ type​;

关键字precision,加上具体指定精度的修饰符precision-qualifier,加上具体的修饰类型type,便是它的悉数。

precision-qualifier取值能够是highp,mediump, andlowp,精度从高到低。type取值暂时只需floatint

因为这儿着色器运用的是vec4类型,所以要指定为float,精度依据设备状况指定,这儿我就风行中庸之道,指定为mediump

下面一行是定义输出变量: out vec4 FragColor;

输出变量将作为当时片段的色彩值传递到后续阶段(当然这不是片段的终究色彩值),它的类型是vec4,每个分量别离表明RGBA,而且色彩每个分量的强度设置在0.0到1.0之间。

接下来:

void main() {
           //给当时片段赋色彩值RGBA
           FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
        }

很好了解了,指定当时片段的色彩,之后这个片段烘托的时候大概率是这儿指定的色彩值(假设不在后续阶段“搞事”的话)。

一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(三)

着色器的编译链接

假设看过上一篇博文一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(二) ,这儿就很好了解着色器的编译链接进程了。

没错,和极点着色器的编译相同,而且它们能够用同一个着色器目标加载,从经过同一个着色器目标而被OpenGL运用。

着色器的编译链接仍旧如下:

graph TD
创立着色器目标 --> 着色器目标加载着色器代码 --> 编译着色器目标 --> 创立着色器程序 --> 相关着色器目标和着色器程序 --> 链接着色器程序 --> 运用着色器程序

所以完整的着色器加载代码如下:

//初始化极点着色器目标
GLint vsh = initShader(vertexSimpleShape, GL_VERTEX_SHADER);
//初始化片段着色器目标
GLint fsh = initShader(fragSimpleShape, GL_FRAGMENT_SHADER);
//创立着色器程序目标
GLint program = glCreateProgram();
if (program == 0) {
    LOGD("glCreateProgram failed");
    return;
}
//向着色器程序目标中相关2个着色器目标
glAttachShader(program, vsh);
glAttachShader(program, fsh);
//链接程序
glLinkProgram(program);
GLint status = 0;
glGetProgramiv(program, GL_LINK_STATUS, &status);
if (status == 0) {
    LOGD("glLinkProgram failed");
    return;
}
LOGD("glLinkProgram success");
//激活着色器程序目标
glUseProgram(program);

制作

接下来就和 一看就懂的OpenGL ES教程——再谈OpenGL作业机制 中所讲的状况机相关了,此刻OpenGL es程序处于现已处于激活着色器程序的状况,后边的操作都能够针对该着色器程序进行操作

//清空屏幕和色彩缓冲
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//给极点着色器传递极点特色数组
GLuint apos = static_cast<GLuint>(glGetAttribLocation(program, "aPosition"));
glEnableVertexAttribArray(apos);
glVertexAttribPointer(apos, 3, GL_FLOAT, GL_FALSE, 0, triangleVer);
//制作三角形
glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
//窗口显现,交流双缓冲区
eglSwapBuffers(display, winSurface);

首先是清空屏幕和色彩缓冲,这儿比较烦琐,需求2行代码(感觉是不是一行代码就能搞定?):

glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);

1.榜首行是指定将色彩缓冲清空为什么色彩,参数为对应的RGBA值。第二行为真正将色彩缓冲设置为glClearColor指定的值。能够说glClearColor函数是一个状况设置函数,而glClear函数则是一个状况运用的函数

glClear办法的参数为指定要设置的缓冲区,能够传入的数值为:GL_COLOR_BUFFER_BIT、GL_DEPTH_BUFFER_BIT 和 GL_STENCIL_BUFFER_BIT,别离表明色彩缓冲、深度缓冲、模板缓冲

关于这三个缓冲:

前面说过光栅化后构成的片段包括制作一个像素的一切信息,包括色彩、深度、模板等,一切这些信息会缓存在3个缓冲区中,别离便是色彩缓冲(color buffer)、深度缓冲(depth buffer)、模板缓冲(stencil buffer)。

2.接下来给极点着色器传递极点特色数组这部分在上一篇文章 一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(二) 中现已有具体论述,在这儿就不赘述。

3.真正的制作办法: glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);

前面讲图元的时候现已讲过,它是真正启动整个图形烘托管线作业的按钮

4.最终是交流缓冲区: eglSwapBuffers(display, winSurface);

在OpenGL程序中,会默许运用一个帧缓冲和应用程序窗口相关(咱们也能够创立新的帧缓冲进行离屏烘托,后边章节会讲到),默许帧缓冲总会包括一个双重缓冲机制的色彩缓冲,所谓双重缓冲,便是一个直接在窗口显现的前置缓冲,以及一个用来烘托新图画的后备缓冲

为什么要用双重缓冲呢?因为烘托图画是一个依照某个规律(一般是从左到右、从上到下)烘托每个像素,不是像孙悟空编程超级赛亚人那样一会儿就把色彩贴上去的。幻想下,假设一个缓冲即要显现同时又在烘托,会产生什么呢?闪耀感是难以幸免了,假设屏幕刷新率和烘托频率不一致的话,可能还会出现画面撕裂感,所以双缓冲的引进是非常必要的。

在这儿eglSwapBuffers办法是在制作指令处理完结,即图画现已烘托到后备缓冲之后,交流前后两个缓冲,将最新图画显现在屏幕上。

至于eglSwapBuffers传入的2个参数,在之前一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(一) EGL装备现已有描绘,这儿就不再赘述。

一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(三)

片段着色器的履行特色

已然片段着色器的首要效果是给当时片段赋值色彩,那么阐明有几个片段就会履行几个片段着色器。假设当时烘托区域为800×600,则一共有480,000片段,假设帧率为30帧每秒,那么一秒钟要履行1,400,000个片段着色器代码

一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(三)

因为着色器程序是履行在GPU中的,这就要从GPU和CPU的差异说起了。

对比下CPU和GPU的结构图(绿色的是核算单元,橙红色的是存储单元,橙黄色的是操控单元):

CPU:

一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(三)

能够看出CPU首要由强大的逻辑操控单元和存储单元以及核算单元,从图中能够看出核算单元仅仅占了CPU的一部分

GPU:

一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(三)

然而GPU就很不相同了,逻辑操控单元和存储单元仅仅占了很小的一部分,核算单元占了绝大部分

所以CPU拿手逻辑操控,串行的运算。GPU拿手的是大规模并发核算,核算量大,但没什么技能含量

一个很经典的比喻,CPU像一个教授,积分微分都会算。而GPU像很多个小学生。假设当时有一个任务,需求履行成千上万次没有依赖关系的一百以内加减乘运算除,那么派很多个小学生去完结比派出一个教授完结要适宜的多。

同理,因为每个片段着色器的履行都是独立的,所以片段着色器在GPU中是大规模地并发履行,即如下图所示:

一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(三)
(the bookof shaders)

所以之前文章说过,OpenGL是一个高效的烘托引擎的首要原因也就在于此~

一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(三)

总结

本文首要具体论述了图元安装以及片段着色器的效果和履行特色,下一篇博文一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(四) 讲要点去制作图形,逐渐接近完结咱们的三角形制作大业。

原创不易,假设觉得本文对自己有协助,别忘了顺手点赞和关注,这也是我创造的最大动力~

代码地址

(项目代码将不断更新)
github.com/yishuinanfe…

参阅

Shader官方文档
Fragment Shader
Core Language (GLSL)
你好,三角形
《OpenGL编程指南(第8版)》

系列文章目录

更多博文,请看音视频体系学习的浪漫马车之总目录

实践项目: 介绍一个自己刚出炉的安卓音视频播放录制开源项目

视频理论根底:
视频根底知识扫盲
音视频开发根底知识之YUV色彩编码
解析H264视频编码原理——从孙艺珍的电影说起(一)
解析H264视频编码原理——从孙艺珍的电影说起(二)
H264码流结构一探究竟

Android渠道MediaCodec系列:
Android硬编解码利器MediaCodec解析——从猪肉饭馆的故事讲起(一)
Android硬编解码东西MediaCodec解析——从猪肉饭馆的故事讲起(二)
Android硬编解码东西MediaCodec解析——从猪肉饭馆的故事讲起(三)

轻松入门OpenGL系列
一看就懂的OpenGL ES教程——图形烘托管线的那些事
一看就懂的OpenGL ES教程——再谈OpenGL作业机制
一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(一)
一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(二)
一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(三)
一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(四)