当前位置:网站首页 > 云服务教程 > 正文

cmakelist教程(cmakelist有什么用)



通过阅读本文,你将获得以下收获:

前面2篇博文,轻松入门OpenGL ES——图形渲染管线的那些事
轻松入门OpenGL ES——再谈OpenGL工作机制基本已经描述了OpenGL整个工作流程机制,因为都是理论的东西,可能读者会觉得显示比较枯燥也比较空,没办法,路要一步一步走,饭要一口一口吃。幸运的是,今天就可以开始实战啦,经过今天的实战,你们才能真切感受到前2篇博文的意义之大

今天的目标是完成一个三角形的绘制:

image.png

一个看似简单的三角形,却曾经是无数初学者的梦魇,也让我当年初学折腾了许久。不过不记紧张,之所以这是很多初学者的梦魇是因为很多初学者并没有理解OpenGL的工作流程就开始写代码实战,所以由于你们有幸经过前面2节课的熏陶,加上你们的聪明,相信这并不难。

1658569020665.png

因为笔者是从事Android平台开发的,所以今天就在基于Android平台来绘制这个三角形

有没有注意到,我的标题是“轻松入门OpenGL ES——XX”,但是前面2篇博文讲的都是OpenGL,那么二者之间有什么关系呢?

只要是概念,那第一时间照搬官方已经成为条件反射:

image.png

官方叙述如下:

OpenGL® ES is a royalty-free, cross-platform API for rendering advanced 2D and 3D graphics on embedded and mobile systems - including consoles, phones, appliances and vehicles. It consists of a well-defined subset of desktop OpenGL suitable for low-power devices, and provides a flexible and powerful interface between software and graphics acceleration hardware.

翻译过来就是:

OpenGL ES是一种免版税的跨平台API,用于在嵌入式和移动系统(包括控制台、电话、家电和车辆)上渲染高级2D和3D图形。 它是桌面OpenGL的子集,用于适合低功耗设备,并在软件和图形加速硬件之间提供灵活而强大的接口。

和OpenGL很相似,重点是用于嵌入式和移动系统,是OpenGL的子集,即是OpenGL的裁剪版本,裁减掉了不适合低功耗的嵌入式和移动系统的Api,那作为移动端巨头级别的Android系统,自然用的就是OpenGL ES,那么今天的三角形,自然用的就是OpenGL ES。

那么我们马不停蹄,马上开始编码吧!

4c1d65488154871eb0fb43b356c44d6e.gif

使用Android studio创建一个ndk项目:

1658633038973.png

于是项目的基本结如下图所示,添加2个Java文件如下:

1658633152722.png

CMakeList文件内容如下:

1658633204132.png

 

主要是引用了2个库,分别是GLESv3和EGL前者是为了使用OpenGL ES 3.0,后者等会会有详细的解释。

MainActivity代码:

 

啥也不干,大道至简,就只干一件事,就是加载生成的so文件。

YuvPlayer代码:

 

Android平台上,已经提供了GLSurfaceView控件专门用于渲染OpenGL es,GLSurfaceView需要通过Render类的回调来获取它的生命周期,一般是如下是三个生命周期方法:

 

在这里,只要在surface创建时调用drawTriangle去绘制三角形即可。所以重点就是进入瞧瞧drawTriangle究竟在C++层做了什么

8c91a9fdabdfe71abd543276c6d8750f.jpeg

 

好了,OpenGL画一个三角形的代码就这么少,大家自行理解,本文结束,下次见。

1658644711730.png

咳咳,开玩笑的……这才是本文的重点。那么接下来,就要用抽丝剥茧的方式,用“行”级的方式讲解这一段代码,结合之前讲的工作机制,让你们彻底明白这个三角形是这么画出来的(很多人可能是糊里糊涂画出来的,比如当年的我)。

image.png

讲解代码之前,首先要理解一个东西,叫做EGL

khronos官方文档中描述

EGL™ is an interface between Khronos rendering APIs such as OpenGL ES or OpenVG and the underlying native platform window system. It handles graphics context management, surface/buffer binding, and rendering synchronization and enables high-performance, accelerated, mixed-mode 2D and 3D rendering using other Khronos APIs. EGL also provides interop capability between Khronos to enable efficient transfer of data between APIs – for example between a video subsystem running OpenMAX AL and a GPU running OpenGL ES.

简单来说,EGL 是 类似OpenGL ES这样的 渲染 API 和本地窗口系统(native platform window system)之间的一个中间接口层,EGL作为OpenGL ES与显示设备的桥梁,让OpenGL ES绘制的内容能够在呈现当前设备上。它主要由系统制造商实现。

简单的图表示它们的关系如下:

1658658364667.png

OpenGL ES只负责处理数据,但是并不知道如何渲染到本地窗口,即屏幕,所以必须把数据交给对本地窗口熟念于心的EGL,因为EGL是系统制造商实现的,而系统又是和硬件关系最紧密的,所以系统制造商才知道怎么去实现对图像数据的渲染。通过EGL,也成全了OpenGL ES跨平台能力。当然除了OpenGL ES有EGL,桌面版本的OpenGL也需要类似EGL的东西,比如WINDOWS系统就需要WGL,基本作用也是差不多。

详细来说,EGL的作用如下:
a:与设备的原生窗口系统通信。
b:查询绘图表面的可用类型和配置。
c:创建绘图表面。
d:在OpenGL ES 和其他图形渲染API之间同步渲染。
e:管理纹理贴图等渲染资源。

a8d56f6c8e85f3435018ee4dd2461f53.jpeg

接下来就是代码解析环节了。

首先有几个关键概念要明白: 1.Display(EGLDisplay) 是对实际显示设备的抽象,即不管是手机屏幕还是电脑屏幕或者其他各种形形式式的显示屏幕,在代码里面都化身为EGLDisplay。这也是EGL帮我们隔离了具体的显示设备

2.Surface(EGLSurface)是对用来存储图像的内存区域帧缓存(FrameBuffer )的抽象是设计来存储渲染相关的输出数据额外。通俗来说就是一个存放辅助缓冲的图像数据(颜色缓冲、模板缓冲、深度缓冲)的容器。

3.Context (EGLContext) 存储 OpenGL ES绘图的一些状态信息。记得在上一篇文章轻松入门OpenGL ES——再谈OpenGL工作机制里,说过OpenGL是一个状态机么,那么它的各种状态其实就是存储在这个EGLContext中的,记录了OpenGL渲染需要的所有信息和状态

1.内部状态信息(View port, depth range, clear color, textures, VBO, FBO, …)
2.调用缓存,保存了在这个下发起的GL调用指令。(OpenGL 调用是异步的)

EGLContext是线程相关的,一个线程只有绑定了一个EGLContext之后,才可以使用OpenGL es进行绘制。当然不同的EGLContext就维护了不同组的状态机。另外同一个EGLContext也可以被不同线程共享,但是不能同时被不同线程绑定。

接下来逐行解释EGL的配置代码:

 

EGL的配置工作究竟干了啥呢?我们来回顾一下:

Untitled (2).png

1.首先通过Java层传入的Surface对象转化为Native层的ANativeWindow对象。
2.通过eglGetDisplay方法得到设备的抽象对象EGLDisplay。
3.通过eglChooseConfig方法以及传入的期望EGL帧缓冲列表得到最匹配的EGL帧缓存配置列表。
4.通过1得到的ANativeWindow对象、2得到的EGLDisplay对象、3得到的EGL帧缓存配置列表得到EGLSurface对象。
5.通过EGLDisplay对象和3得到的EGL帧缓存配置列表得到EGLContext上下文对象。
6.将EGLContext和当前线程以及draw和read的EGLSurface关联,关联之后,当前线程就成为了OpenGL es的渲染线程。所谓draw和read就是分别是写和读的缓冲区。

关于EGL配置,详细可参见EGL Reference Pages,个人觉得博文也没必要去搬太多官方文档里面的东西,所以大家有需要的话可以看下。

配置好EGL环境之后,就相当给OpenGL es和显示设备之间打通了一条管道,于是便可以开始使用OpenGL es进行绘制了,也就要开始前面2篇文章说过的图形渲染管线了:

image.png

第一个步骤,就是组织好要渲染的数据,然后交给顶点着色器,介于篇幅关系,还有为了防止各位看久了打瞌睡,本文就先到这,下一篇博文将继续这个三角形的绘制,从组织好要渲染的数据和顶点着色器讲起~

fe0014dbdf6acf1ff11c7d72c523a46a.jpeg

本文为使用OpenGL es在Andoird平台上绘制三角形的第一篇,主要是介绍了项目的基本结构相关的Java以及CmakeList文件并重点介绍了EGL以及如何配置EGL,当这一切准备就绪之后,下一篇博文一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(二)才是我们真正绘制三角形的开始。

EGL Reference Pages
EGL 作用及其使用
Let’s talk about eglMakeCurrent, eglSwapBuffers, glFlush, glFinish

作者:半岛铁盒里的猫
链接:https://juejin.cn/post/
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

【文章福利】免费领取更多音视频学习资料包、大厂面试题、技术视频和学习路线图,资料包括(C/C++,Linux,FFmpeg webRTCrtmp hlsrtsp ffplay srs 等等)有需要的可以点击加群领取哦~

目录导航

通过阅读本文,你将获得以下收获:

1.什么是着色器
2.顶点着色器代码的基本外观以及着色器语言GLSL介绍
3.OpenGL的坐标系统
4.如何使用着色器

轻松入门OpenGL ES——这或许是你遇过最难画的三角形(一)主要讲了Android平台上开发OpenGL es应用项目的搭建以及EGL配置相关,万丈高楼平地起,上一篇博文已经帮我们打好了地基,那我们的渲染程序,就可以如雨后春笋一般破土而出了。当前我们已经知道图形渲染管线的第一步是顶点着色器(不清楚的读者可以看下轻松入门OpenGL ES——图形渲染管线的那些事
轻松入门OpenGL ES——再谈OpenGL工作机制 )

image.png

,那么我们此时的问题就是:
1.顶点着色器的真面目是什么样的?
2.作为客户端的C++程序如何将数据传给着色器?

记住,阅读本文的时候,一定要时刻记得每一个步骤都是在图形渲染管线中进行,一定要明确数据的从哪里来到哪里去,这也是我前面文章所强调的大局观。

image.png

Shader官方文档的定义是:

A Shader is a user-defined program designed to run on some stage of a graphics processor. Shaders provide the code for certain programmable stages of the rendering pipeline. They can also be used in a slightly more limited form for general, on-GPU computation.

着色器就是一个运行在图形渲染管线中某个阶段的一段开发者写的程序,。由于是运行在,所以,这和大家以前写的运行在CPU的代码就必然有所不同。

关于着色器在图形渲染管线中的阶段,官方文档描述如下:

The rendering pipeline defines certain sections to be programmable. Each of these sections, or stages, represents a particular type of programmable processing. Each stage has a set of inputs and outputs, which are passed from prior stages and on to subsequent stages (whether programmable or not).

Shaders are written in the OpenGL Shading Language. The OpenGL rendering pipeline defines the following shader stages, with their enumerator name:

  • Vertex Shaders: GL_VERTEX_SHADER
  • Tessellation Control and Evaluation Shaders: GL_TESS_CONTROL_SHADER and GL_TESS_EVALUATION_SHADER. (requires GL 4.0 or ARB_tessellation_shader)
  • Geometry Shaders: GL_GEOMETRY_SHADER
  • Fragment Shaders: GL_FRAGMENT_SHADER
  • Compute Shaders: GL_COMPUTE_SHADER. (requires GL 4.3 or ARB_compute_shader)

主要就2个重点:

1.上游阶段的着色器可以传递数据给下游阶段的着色器。
2.目前图形渲染管线已有的着色器有6种,分别是……(上文列出来的那几种),目前我们只需要关注Vertex Shaders(顶点着色器)和Fragment Shaders(片段着色器)即可。

如果我们控制了着色器,就控制了图形渲染管线的半壁江山,从而就一定程度控制了整个绘制的效果。于是接下来,。

image.png

很多博文讲着色器Shader可能就直接将 Shader官方文档 叙述或者罗列下重点,这样子我觉得可能效果还,接下来,。

我们再用类似轻松入门OpenGL ES——图形渲染管线的那些事想想要画一个三角形的步骤,第一步,当然就是,所以我们要将三个顶点位置传给图形渲染管线,那么图形渲染管线的第一个阶段便义无反顾地承担起了这个任务,于是第一个阶段也就是处理顶点的阶段——顶点着色器。

简单来说,顶点着色器的核心功能就是完成将3维坐标中的点,通过变换和投影,转换为2维的屏幕上。

作为渲染管线的,顶点着色器,作为一段拥有具体逻辑的应用程序,它还承受着以便完成一些效果等任务(比如位置变换、调整形状,或者三维变换),另外还起着(比如颜色、变换矩阵、时间参数等)并的任务。

先看下它长什么模样:

 

上面是一个的顶点着色器的代码,它只做一件事:接收客户端程序传来的顶点数据,然后传给。相当于上面程序啥都没做,只是无脑的数据传送机。那它有什么价值呢?当然有,勇敢作为排头兵,接收外头丢过来的“烫手山芋”就是它最大的贡献。 (当然你可别认为顶点着色器就这么没用,毕竟这只是一个最简单的顶点着色器程序,真正“成熟”的顶点着色器程序技能可是很爆表的。因为在顶点着色器中,你已经拿到了对每个顶点的实质控制权,可以尽情发挥想象力去操纵每一个顶点)

106000e7fd34ddca83010ddbfcf61161.jpeg

接下来就是具体讲解着色器代码的时刻了。首先要知道的是,着色器用的语言是一种特殊的语言,叫做,首先看下GLSL官方定义:

The OpenGL Shading Language is a C-style language, so it covers most of the features you would expect with such a language. Control structures (for-loops, if-else statements, etc) exist in GLSL, including the statement.

GLSL是一种C语言风格的语言,它包含大部分编程语言的特性。

所以它是和C语言类似的,只要你能看懂C语言,就能看懂它。只要你写过C语言程序,那基本也能写GLSL程序。

上面的实例代码为所写,那么这里就以现在最主流的为例,讲解着色器。

我不太喜欢文档式地罗列语法点,所以关于着色器还是遵循边用边学原则去讲。

第一行:

OpenGL es和GLSL对应的版本关系如下图所示:

image.png

第二行:

 

这是定义一个变量aPosition,但是和我们熟悉的C语言又有所不同。

迎面而来的是关键字,表示这个变量是接收外面传来数值的,当然有in就会有,表示变量是传给下一个阶段。(可能有的童鞋已经接触过GLSL2.0版本的代码了,熟悉的是arrtibute、varying等关键字,但这些在3.0已经被in、out取代)

接下来看到,表示变量类型,aPosition表示变量名。这个格式大家都很熟悉,不过vec4这种类型可能有点陌生。其实就是表示。

4维向量可以当做4个数的组合,GLSL特定这里指的是,用法也非常灵活:

 

以上我相信加上注释大家都能看懂,这里就不做赘述。

当客户端需要传数据给该变量的时候,客户端就会绑定这个名字,指定数据传给着色器变量。

但是这样客户端C++程序和着色器内部的名字强行绑定,。

于是乎便应运而生,关于这个就有点意思了,layout是GLSL的几个中的一个,一般用来指定变量布局的,可以说是着色器和客户端程序或者其他阶段着色器的通信接口,详细的依旧可以看官方文档Layout Qualifier (GLSL)

 

以下是layout的通用语法格式:

 

其中qualifier表示具体的修饰符,最常见的就是。

何为变量的位置?你可以想象每一个着色器程序是一层楼,每个需要对接外部的变量住在一个个房间中,假如你现在是客户端的C++程序,你想给着色器里面某个变量传参,然后,就像一个蹑手蹑脚的间谍,偷偷打开对应location的房门,偷偷将一份机密文件交给里面的人(location对应变量)。所以。

547f3069053afb1dc8b012a014d115ef.jpeg

至于客户端C++程序怎么传数据给location对应变量,先别急,后面小节还会讲。

第三行:

 

这个各位太熟悉了,自从第一次写了hello world程序之后,对main函数应该烂熟于心了吧。

既然GLSL是类似C的语言,那么当然也是以main函数作为入口函数的。

main函数究竟做了什么:

 

直接把传入的坐标值作为输出传入渲染管线下一个阶段。

所以说到底,顶点着色器最重要的目标就是

最后有个问题:

我们想下,顶点着色器是用来处理顶点的,也就是每个顶点都要经过它的处理,那么也就是

那么传入的vec4类型aPosition具体是什么样的呢?我们知道在三维空间中表达一个点,就是用一个3维的坐标x、y、z。

408b41d13341e47a4613f01bd71a257a.jpeg

那么为什么是vec4(4维)的呢?这里vec4分别是x、y、z、w,其中w是一个新的概念,叫做,那么齐次坐标在这里是什么作用呢?

因为本文并不是用来讲解数学的,所以这里只是简单解释下其次坐标的作用,假如2维坐标系中有一个点(x,y),如果要对该点进行,我们是可以通过,即的方式来计算

1659866345074.png

如果要对该点进行,则是要能够以下方式表示:

1659866396517.png

那如果对该点,则要用如下方式表示:

1659866432806.png

这就不是线性变换了,那么如果我们坚持一定要用呢?不用担心,数学家已经帮我们实现了,不过要再增加一个维度w就可以了:

1659867293414.png

这方面要详细了解数学推导的话,可以看GAMES101-现代计算机图形学入门-闫令琪

547f3069053afb1dc8b012a014d115ef.jpeg

虽然OpenGL的坐标是一个3维空间坐标,但是本系列教程针对的是2维空间,即我们可以直接忽略z坐标,即z始终为0,而OpenGL用的是标准化设备坐标,(在轻松入门OpenGL ES——图形渲染管线的那些事一文的一章,已经提到了这个步骤),这种坐标处理方式也叫作则OpenGL的坐标会变为如下:

1660450575311.png

这种坐标下,假如有个手机屏幕是720*1080,则x方向的1个单位长度表示720个像素,而y方向的1个单位长度表示1080个像素。

为什么要这样处理呢?因为不同的屏幕分辨率相差甚多,如果要用绝对的像素点作为坐标点的话,那么针对不同的屏幕可能就需要传一套坐标点给顶点着色器,而通过标准化设备坐标的归一化,

1660451831025.png

前面说了这么多,那么到底要怎么给图形渲染管线“喂”数据呢?在“喂”数据之前,还有一个手续需要办理一下,那就是。

再看下上一篇文章(轻松入门OpenGL ES——这或许是你遇过最难画的三角形(一))中的绘制三角形的代码:

 

可以看到,在上篇文章讲的配置EGL环境之后,就进入到,而此阶段最主要的操作,就是。着色器程序是在我们整个程序运行时编译,因为我们必须在将,就像一条工厂流水线有那么2、3个工序是空的,我们在启动流水线之前,必须将空的工序一个个填补上去。

0bd81f0ab7e782cd4826d6ae200122d0.jpeg

关于加载着色器程序的基本流程如下图所示

创建着色器对象着色器对象加载着色器代码编译着色器对象创建着色器程序关联着色器对象和着色器程序链接着色器程序使用着色器程序

这里有2个概念,一个是着色器对象,表示一段具体的着色器代码的抽象,另一个是着色器程序,表示整个图形渲染管线的着色器程序集合

接着是令大家兴奋的代码演示,代码就是根据上面的流程图一步步走的。首先将编译链接着色器程序的代码抽出来,逐行分析,首先是initShader方法:

 

1.首先用glCreateShader方法根据传入的type参数创建对应的着色器对象,type的可选范围为, , , , , or 。顶点着色器为 ,片段着色器为 。

返回值sh为一个具有引用作用的整数,这也是OpenGL最常见的一个种引用对象的方式,再之后的教程中还会非常常见。。

2.此时的着色器对象还是,所以我们需要通过glShaderSource方法将。

3.代码注入进去了,就要类似C程序一样需要一个,用的是glCompileShader方法。

4.当然有编译就有可能有编译错误,由于着色器程序是爱GPU中编译运行的,所以目前并不像我们平时写在CPU运行的程序那样有详细的编译错误信息以及可以断点调试这些高端操作,但是至少还是可以通过glGetShaderiv方法看到一些报错信息的

 

1.通过glCreateProgram方法创建一个着色器程序对象。

2.此时着色器程序对象还是空空如也,所以需要glAttachShader方法,将前面创建的。

3.此时着色器程序还是没有,所以需要通过glLinkProgram方法,类似我们C语言程序链接一个动态链接库一样,去关联上。

4.当然,链接可能会发生异常,所以通过。

5.最后再用glUseProgram,名正言顺宣告,该。

接下来就和 一看就懂的OpenGL ES教程——再谈OpenGL工作机制 中所讲的相关了,此时OpenGL es程序处于已经。

前面说过顶点着色器作为图形渲染管线的开端,会接收包括顶点数据在内的,这里我们就先从讲起。

OpenGL客户端程序传数据到OpenGL(服务端)内部的操作也是,毕竟OpenGL是,所以不好意思,肯定不像Java老司机:

 

OpenGL传输数据是这样的:

 

如果你是第一次看到,可能会出现以下表情:

bc96822bb614f79d22e417e1395af227.jpeg

不过不用担心,经过我一番“感性”的解释,你一定能彻底搞懂。

image.png

首先,前面已经客户端程序要传入OpenGL的数据,,所以它不拘留于具体格式,,。比如三角形的三个顶点坐标,那就让三个点坐标的x、y、z依次排成一队:

 

这个数组再OpenGL有个专有名词,叫做。(注意,不是“顶点数组”,,即该数组包含)

回头看下顶点着色器:

 

我们的目标是要把顶点坐标传到变量“aPosition”中,所以先指定接收的变量名:

 

接下来是,就是告诉OpenGL入的数据:

 

官网对该API的解释是:

specify the location and data format of the array of generic vertex attributes at index to use when rendering. specifies the number of components per attribute and must be 1, 2, 3, 4, or . specifies the data type of each component, and specifies the byte stride from one attribute to the next, allowing vertices and attributes to be packed into a single array or stored in separate arrays.

就是说glVertexAttribPointer方法指定了中(这里的变量指的是顶点着色器中被修饰的变量),另外就是指定以及。

函数定义为:

GLuint , GLint , GLenum , GLboolean , GLsizei , const void * ;

:表示着色器中要接收数据的变量的引用。即。

:表示。比如一个3维坐标是xyz表示,那么size就是3,即3个数可以表示一个点坐标。

:每一个数组元素的格式是什么,比如, , 等。

:是否需要归一化,即是否需要将数据范围内。

:步长,一个重要概念,表示。如果传0,则说明顶点属性数据是的。

这么一说恐怕懵逼二字写在每个人脸上,下面用一张图来说明(图来源于(learnopengl-cn.github.io/01%20Gettin…)

以数组起始位置为地址0来看,第一个顶点属性VERTEX1的开端是地址0,因为数据类型为Float,每个Float大小为4个字节,而一个Vertex需要3个元素,则第二个顶点属性VERTEX2开端是地址12,那么此时stride就是12。

image.png

这种情况属于元素之间紧密挨着的,所以stride传0,OpenGL也能自己处理。但是如果顶点属性不止顶点坐标数据,还有颜色属性,如下图:

image.png

每个顶点属性Vertex由一个坐标点和一个RGB颜色数据组成,这样子就必须告诉OpenGL,,这个它才知道怎么取对应的数据。

所以这行代码

 

可以解释为:
将triangleVer数组的每个元素用Float类型来看待,每3个元素为一个属性,每间隔12个字节(stride传0告诉OpenGL顶点属性都是紧密挨着)属性依次传给着色器中apos引用的变量。

所以,你看OpenGL挺傻的,我们需要苦口婆心地详细告诉它怎么解析数据它才知道如何解析,但是这样的好处是我们自由度高,可以非常灵活地传递数据,后面就会展示需要传递颜色数据的情况

da30f2dd74a82606185c70491a10c343.jpeg

那么结合实例,我们传入的数组此时就应该这样看待:

 

然后OpenGL就会一个一个顶点坐标传入顶点着色器执行,顶点着色器执行完之后,数据就会输出给下一个阶段——(千万别以为是直接到啊,不然前面的文章就白写了= =)

最后的:

 

这就是一个开关方法,就是打开着色器中这个变量,想象下你还是那个间谍,你需要。

23ab0c50c89a60394110138ecf4c7a23.jpeg

不知不觉又是写了几千字,OpenGL的东西细讲真的很多,要入门还真不容易。不过我们已经2座大山了,后面的路会相对稍微平坦一些了,下一篇文章一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(三)将继续讲下一个阶段——,敬请期待~~

Shader官方文档
Core Language (GLSL)
你好,三角形

作者:半岛铁盒里的猫
链接:https://juejin.cn/post//
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

【文章福利】免费领取更多音视频学习资料包、大厂面试题、技术视频和学习路线图,资料包括(C/C++,Linux,FFmpeg webRTCrtmp hlsrtsp ffplay srs 等等)有需要的可以点击加群领取哦~

目录导航

通过阅读本文,你将获得以下收获:

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

cea1ad7e7969943f471562de9ef28749.jpeg

此时故事的小马车已经到了第2个阶段: 1661561029612.png

之前在一看就懂的OpenGL ES——图形渲染管线的那些事一文中介绍过图元装配阶段主要工作就是根据开发者的需要将顶点连接成为一个图形,比如如上图所示将三个点连接为一个三角形。

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

 

比如对于只有1个点的情况,那么图元只有一种,那就是无法做连接操作,最终“连接”形成的图形就是一个点。

对于2个点的情形,可以选择为不连接,即形成2个点,也可以选择将2个点进行连接,形成一条线段。

对于3个点的情形,那情况就丰富了很多,可以选择不连接形成3个点,也可以只连接其中2个点形成一条线段和一个点。也可以将3个点连接起来,形成一个三角形。

那么对于3个以上的,那能提供的连接方式就多种多样了,:

1661569373941.png

如果现在传入顶点着色器的顶点数组的元素分别为:v0,v1,v2,v3,v4,v5,v6,v7,那么如下所示:

1661569521731.jpg

那么怎么确定图元装配的类型呢?

如果看过前面2篇文章的童鞋,可能会注意到一个方法:

 

这是OpenGL的,在它前面的一系列指令只是,而glDrawArrays就像一个,一踩下去,整个,传入OpenGL的数据才像一条河流一样在一个个阶段流动起来,直到形成的图像数据被绘制到对应的帧缓冲~

关于图元详细信息,可以看下官网Primitive

如果对于解。让我们先进入另一个主角——片段着色器(Fragment Shader)。

48390c6358fb97443a5c8c23bed9051d.jpeg

其实看过之前的文章的老哥应该知道,,这个操作非常重要,它将,而这一块块的片段,才。

1661574027237.png

介于光栅化的内容主要是图形学算法相关的,,所以在这里就只要明白这个阶段的作用即可。而接下来的片段着色器,才是我们的主角,也是一个相当有意思的家伙。

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

A Fragment Shader is the Shader stage that will process a Fragment generated by the Rasterization into 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 a Window Space position, a few other values, and it contains all of the interpolated per-vertex output values from the last Vertex Processing stage.

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——图形渲染管线的那些事一文的测试和混合章节已有提及)

 

一个片段着色器代码是什么样子的呢?()

 

这是一个的片段着色器,只做了一件事,就是。这里在GLSL2.0中有个表示该片段的最终颜色,但是在GLSL3.0已经被弃用。

第一行 不用解释了,还不清楚的请出门左转至 一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(二)

第二行表示精度修饰符,表示数据的精度,一般情况下,大。这是,这完全体现了Khronos组织对于嵌入式设备性能的体贴和照顾。

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

 

关键字,加上具体指定精度的修饰符,加上具体的修饰类型,便是它的全部。

取值可以是,精度从高到低。取值暂时只有和。

因为这里着色器使用的是类型,所以要指定为,精度根据设备情况指定,这里我就风行中庸之道,指定为。

下面一行是定义输出变量:

该(当然这不是片段的最终颜色值),它的类型是,每个分量分别表示,并且颜色每个分量的强度设置在之间。

接下来:

 

很好理解了,指定当前片段的颜色,之后这个片段渲染的时候(如果不在后续阶段“搞事”的话)。

eff6e76c135f27d2156f2bf5d5b70a97.jpeg

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

没错,和顶点着色器的编译一样,并且它们可以用同一个着色器对象加载,从通过同一个着色器对象而被OpenGL使用。

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

创建着色器对象着色器对象加载着色器代码编译着色器对象创建着色器程序关联着色器对象和着色器程序链接着色器程序使用着色器程序

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

 

接下来就和 一看就懂的OpenGL ES教程——再谈OpenGL工作机制 中所讲的相关了,此时OpenGL es程序处于已经。

 

首先是,这里比较啰嗦,需要2行代码(感觉是不是一行代码就能搞定?):

 

1.第一行是指定,参数为对应的。第二行为。可以说glClearColor函数是一个状态设置函数,而glClear函数则是一个状态使用的函数

glClear方法的参数为,可以传入的数值为:。

关于这三个缓冲:

前面说过光栅化后形成的片段包含绘制一个像素的所有信息,包含颜色、深度、模板等,所有这些信息会缓存在3个缓冲区中,分别就是颜色缓冲(color buffer)、深度缓冲(depth buffer)、模板缓冲(stencil buffer)。

2.接下来给这部分在上一篇文章 一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(二) 中已经有详细阐述,在这里就不赘述。

3.真正的绘制方法:

前面讲图元的时候已经讲过,它是。

4.最后是交换缓冲区:

在OpenGL程序中,会(我们也可以,后面章节会讲到),。

为什么要用双重缓冲呢?因为渲染图像是一个按照,不是像孙悟空编程超级赛亚人那样一瞬间就把颜色贴上去的。想象下,如果一个缓冲即要显示同时又在渲染,会发生什么呢?,如果的话,可能还会出现,所以双缓冲的引入是非常必要的。

在这里eglSwapBuffers方法是在绘制指令处理完成,即,将最新图像显示在屏幕上。

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

f04fef67ee6db3847b1a9e3e3abcb763.gif

既然片段着色器的主要作用是给当前片段赋值颜色,那么说明有几个片段就会执行几个片段着色器。假如当前渲染区域为800x600,则一共有480,000片段,假如帧率为30帧每秒,那么!

8a8de8aa2ebd2bc14ac0ebb10c27d83c.jpeg

因为着色器程序是的,这就要从GPU和CPU的区别说起了。

对比下CPU和GPU的结构图():

CPU:

image.png

可以看出CPU主要由,从图中可以看出。

GPU:

image.png

然而GPU就很不一样了,。

所以CPU擅长逻辑控制,串行的运算。GPU擅长的是大规模并发计算,计算量大,但没什么技术含量

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

同理,由于每个片段着色器的执行都是的,所以,即如下图所示:

image.png (the bookof shaders)

所以之前文章说过,OpenGL是一个也就在于此~

a806859f9b95e286b5ba42ef34f63221.jpeg

本文主要详细阐述了,下一篇博文一看就懂的OpenGL ES教程——这或许是你遇过最难画的三角形(四) 讲重点去,逐步接近完成我们的三角形绘制大业。

原创不易,如果觉得本文对自己有帮助,别忘了随手点赞和关注,这也是我创作的最大动力~

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

作者:半岛铁盒里的猫
链接:https://juejin.cn/post/
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

【文章福利】免费领取更多音视频学习资料包、大厂面试题、技术视频和学习路线图,资料包括(C/C++,Linux,FFmpeg webRTCrtmp hlsrtsp ffplay srs 等等)有需要的可以点击加群领取哦~

目录导航
到此这篇cmakelist教程(cmakelist有什么用)的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!

版权声明


相关文章:

  • awvs13使用教程(awvs13安装教程)2025-01-11 16:18:09
  • msp432单片机教程(msp430f5529单片机)2025-01-11 16:18:09
  • 程序员入门教程视频(程序员入门教程视频免费)2025-01-11 16:18:09
  • 2263xt量产(2246en量产教程)2025-01-11 16:18:09
  • ad20软件安装教程(ad2020安装步骤)2025-01-11 16:18:09
  • mac装双系统win10详细教程(mac装win10会毁电脑吗)2025-01-11 16:18:09
  • awvs怎么使用(awvs12使用教程)2025-01-11 16:18:09
  • st7735s驱动教程(st7701s驱动)2025-01-11 16:18:09
  • ubuntu镜像文件怎么安装(ubuntu镜像文件下载教程)2025-01-11 16:18:09
  • 新魔百和m301h刷机教程(新魔百盒m310刷机)2025-01-11 16:18:09
  • 全屏图片