---页首---

翻译:关于OpenGL ES 2.x(第一篇)

翻译:关于OpenGL ES 2.x(第一篇)

原文:All about OpenGL ES 2.x - (part 1 / 3)

大家好!
欢迎来到一个新的指导系列。这次让我们来谈谈3D世界的魔力。我们讨论下OpenGL。在过去的5个月中,我完全的致力于深入3D世界,我完成了我的新的3D引擎(看看起来是我做过最好的作品),现在是时候分享给你们我所有知道的、所参考的、所有的书籍、指导,当然会从你的反馈中学到更多。
这个系列由3部分组成:

Part 1: 3D世界的基本概念和OpenGL(初学者)
Part 2: OpenGL ES 2.0深度(中级)
Part 3: 在OpenGL ES 2.0中Jedi技巧和2D图形(高级)

所以,如果你只感兴趣某部分代码,可以直接踢到2或3部分,在第一部分,我将会只讨论些概念。

OK,我们开始吧!

前言

你从来没有听过OpenGL?OpenGL是指开源图形库,在今天计算机语言中被用到很多。OpenGL是CPU(我们开发人员,基于一种语言运行我们应用)和GPU(图形处理器,存在每一个图形卡中)之间最近的点。所以OpenGL需要图形卡制造商(如:NVidia)的支持,同时被操作系统供应商(如:Apple的Mac OS\iOS)实现,最终OpenGL给到我们开发者统一的API使用。这个API是“语言免费的”(或者几乎是免费的)。这很神奇,因为如果你使用C、C++、Objective-C、Perl、C#、JavaScript,不管你使用哪个,API总是相同的,呈现相同的行为,相同的函数,相同的命令,基于这一点有了这个系列的指导!开发者处理OpenGL的API。

在讨论OpenGL API之前,我们需要对3D世界有一个很好的了解。计算机语言的3D世界历史与OpenGL的历史密不可分。因此,让我们来回顾下历史。

一个小故事

translation-opengl-logo
translation-opengl-es-logo

20年前有一个小伙子,名叫Silicon Graphics(SGI)做了一个小装置。那个装置能够显示一种对现实的幻觉。在一个2D图像的世界里,该设备敢于去显示3D图像,模拟人眼的洞察力和深度。该装置被叫做IrisGL(可能是因为它试图模仿眼睛的红膜)。

说真的,该设备是第一个伟大的图形库。但是很快就消失了,因为要做到他所做的事,他需要在计算机上去控制很多东西,如图形卡,窗口系统,基本语言,甚至前端。对于一个公司来管理实在是太多了。因此,SGI将一些东西如“创建图形卡”,“管理窗口”,“前端实现”代理给其它公司,而自已专助于图形库最重要的部分。在1992年,首次发布了OpenGL。

在1995年,微软发布了Direct3D成为OpenGL主要的竞争者。随后就在1997年OpenGL 1.1就发布了。但OpenGL真正让我感兴是在2004年,那里OpenGL 2.0带着巨大的改变也已经发布了。着色器,可编程管线,我爱死它们了!最终,我们在2007年遇到了OpenGL ES 2.0,它给我们带来了着色器和可编程管道到嵌入式系统的力量。

今天,你可以在在很多的游戏,3D、2D应用和很多图形软件(尤其3D软件)中看到OpenGL的图标(或OpenGL ES的图标)。OpenGL ES被用在游戏机,安卓,任天堂 3DS,Nokia,三星,塞班,当然也包括Apple的Mac OS、iOS。

OpenGL的最大竞争对手

讨论下微软Windows系统。
你还记得我在前面说到的在1992年OpenGL第一次发布?在同一时期,微软有他们闪亮的Windows 3.1。当然,作为微软总是相信:没有什么是被创造的,一切都可以复制。,微软尝试去抄袭OpenGL,在他们那被叫做DirectX,同时在1995年应用到Windows 95上。

一年后,即1996年,微软引入了Direct3D,毫不夸张的说那是OpenGL的拷贝。重点是微软主导信息主场多年,而DirectX(或Drect3D)像温疫一样渗透并攫取了许多电脑(PC),当微软开始将他们的操作系统提供给手机和视频游戏时,DirectX与OpenGL碰面了。

现在的DirectX与OpenGL在结构上非常相似:有着色器语言,有可编程管线,也有固定的管线,甚至API函数的名字都差不多。不同的是OpenGL开源,DirectX不开源。OpenGL可以用于iOS,MacOS和Linux系统,而DirectX只能用于微软系统。

好极了,现在开始我们的3D世界之旅吧!

3D世界第一点 - 眼睛

从我记事起,我就对3D世界和3D游戏充满热情。我们人类用3D幻觉来模仿真实世界都来自一个地方:我们的眼睛。

眼睛是3D世界一切事物的基础。我们所能做的就是模仿它的能力,人类眼睛的美丽和魔力。我不是医生,并且不想在本教程中谈论眼睛,但是如果你习惯了一些概念,比如视野,双目和单眼视觉,眼睛的晶状体,凹透镜和凸透镜,这将会很好,这可以帮助你理解一些概念。

在3D世界我们所做的一切都是重现人眼的感觉:透视,消失点,变形,景深,焦点,视场,一切都是为了模拟那种感觉。

第二点 - 第三维

这可能看起来有点愚蠢,但必须要说:3D只所以为3D是因为有3个维度。”什么?这不是很明显的事么!“,稍安勿躁,兄弟,我正在说的这是因为非常重要,与2D世界相比,加一维让我们陷入严重的麻烦。那不是产生一个或三个很少的麻烦,而是把我们推入麻烦的深渊。

在2D世界当我要去旋转一个正方形时非常简单,45度旋转总是让我们的正方形有一个特殊的旋转,但在3D世界,旋转一个简单的正方形,需要X,Y,Z的旋转。根据我们的旋转的顺序,最终的结果完全不同。当我们进行一系列的旋转时,事情会变得更糟,例如:旋转x=25再旋转y=20是一种情况,但旋转x=10,y=20,再旋转x=10
完全是一个新的结果。

好吧,这里的要点是,另一个维度的增加,会使我们的工作变得愚蠢。

第三点 - 非3D,可能经常是4D

什么?另现一个维度?
是的,兄弟,最后一个点我要说的就是它。通常我们工作不仅仅是在3D世界,我们还有第四个维度:时间。在3D世界中的事物需要互动,需要移动,需要加速,需要碰撞,需要改变其惯性。正如我前面说的,在3D世界中进行连续变化会使我们获得很多的结果。
OK,到现在我们可以用一个短语来定义3D世界:是人眼睛模拟和一切事物的移动。

OpenGL进入3D世界

现在,本教程开始变得更有趣。让我们一起来讨论伟大的引擎:OpenGL。首先,我们要感谢伟大的数学家,如Leonhard Euler, William Rowan Hamilton, Pythagoras等。多亏了他们,如今我们拥有用于3D空间的众多公式和技术。OpenGL运用所有这些的知识来构建一个3D世界在我们的面前。使用大量的公式来模拟人眼的美感,每秒进行数千甚至百万次的运算。

OpenGL是一个很棒的STATE MACHINE(这意味着整个OpenGL都可以使用状态设计模式进行工作)。为了说明什么是OpenGL,让我们想象下在某个港口有一个巨大的港口起重机。有很多装有板条箱的集装箱。OpenGL就像整个港口,其中:

集装箱是OpenGL的对象。(纹理,着色器,网格物体之类的东西)
在每个集装箱中的板条箱是我们在自己的应用中用OpenGL创建的。是我们的实例。
港口起重机就是OpenGL的给我们访问的API。

因此,当我们执行一个OpenGL的函数时就像给港口起重机一个命令一样。起重机将集装箱移入港口,将其抓起并保持一会儿,在集装箱内处理你想要的东西,最后再次将集装箱放下到港口的某个位置。你无权直接进入港口,也将无法看到或更改集装箱的内容,无法对其进行重组,也无法直接对港口中的集装箱进行任何操作。你所有能做的就是给起重机指令。起重机是港口唯一能控制集装箱的。这点要记住!到这里这是OpenGL最重要的信息。起重机是港口唯一能管理集装箱的。

唔,OpenGL以此方式来看好像是非常有限的API,但事实上不是。OpenGL这台起重机是非常非常强大的。它每秒可以重复处理保存和删除集装箱数千或数百万次。OpenGL的另一个巨大的优势是使用状态机模式,我们不需要持有任何一个实例,我们也不需要直接创建任何对象,我们只需持有id,或者说明的文字,我们只需知道集装箱的标识即可。

OpenGL如何工作

深入到OpenGL的核心,直接在GPU中用硬件加速器完成浮点数的计算。Hugh?CPU(中央处理单元)是一台计算机或设备的处理器。GPU(图形处理单元)是一台计算机或设备的显卡。显卡减轻了处理器的负担,因为它可以在图片显示到屏幕上之前大量的计算来处理。因此,更深入的讲,OpenGL将大量的计算交给GPU,而不是全部在CPU中进行计算。GPU在处理浮点数时要比CPU快的多。这就是为什么一个3D游戏在更好的显卡上运行更快的根本原因。这也是为什么3D专业的软件给你提供软件渲染(CPU处理)或显卡处理(GPU处理)选项的原因。现在有些软件也给你提供OpenGL的选项。那个选项是GPU。所以,OpenGL是全部工作在GPU中?不完全是。只是一些硬图像处理和其它一些事情。OpenGL给我们提供了很多功能以优化的格式来存储图像、数据和信息。这些优化的数据稍后会直接被GPU处理。所以,OpenGL硬件是独立的?不幸的是,是的!如果硬件(显卡)不支持OpenGL,我们就不能使用它。新的OpenGL版本通常需要新的GPU特性。这是我们要知道,但不要担心。由于OpenGL始终需要供应商的实现,我们(开发者)将在设备准备就绪时使用新的版本。实际上,今天所有显卡芯片都实现了OpenGL。所以你可以在很多语言和设备上使用OpenGL。甚至微软系统也可以。

OpenGL的逻辑

OpenGL是一个非常简洁且专注的图形库。你在专业的3D软件中看到的是OpenGL之上的一个超级超级复杂的作品。因为,从更深层次来讲,OpenGL的逻辑了解一些事情:

基元(Primitives)
缓存(Buffers)
光栅化(Rasterize)
只是这些?3件小事?
你要相信,OpenGL是围绕这3个概念工作。让我们来独立的看看每个概念,看这3个概念如何连接来创建非常高级3D图形库(你也可以使用为2D图形使用OpenGL。2D图像对于OpenGL来说就是一个3D工作在所有Z轴深度为0,我们将在后面会讨论到)。

元语(Primitives)

OpenGL的元语限制于3个小的对象:

关于空间的3维点(x,y,z)
关于空间的3维线(由两个3D点组成)
关于空间的3维三角形(由3个3D点组成)
一个3维空间的点可被当作空间粒子来使用
一条3维空间的线是一条线,可被当作3维空间向量使用
-个3维空间三角形可以是数千或数百万面的网格的一个面
某些版本的OpenGL也支持四边形,这仅仅是三角形的分支。但由于OpenGL ES旨在实现最高性能,因此不支持四边形。

缓存(Bufffers)

现在我们讨论下缓存。简而言之,缓存是一个优化临时存储。存什么呢?很多东西。OpenGL处理3类缓存:

Frame Buffers (帧缓冲区)
Render Buffers (渲染缓冲区)
Buffer Objects (缓冲区对象)
帧缓冲区是三个中最抽象的。当你创建一个OpenGL渲染缓冲区,你可以直接发送最终的图像到设备的屏幕或者到帧缓冲区。所以帧缓冲区是一个临时图像数据吗?
不是很准确。你可以想你它是从OpenGL的渲染缓冲区的输出,这意味着可能是一组图像,而不仅仅是一个。什么类型的图像呢?有关3D对象,有关对象在空间中的尝深度,有关对象的交集,有关对象可见部分的图像。所以帧缓存更像一个图像的集合。所有这些以像素信息的二进制数组存储。

渲染缓冲区是临时存储一个单一的图像。现在你可以看得更清晰帧缓存区是渲染缓冲区的集合。存在几种渲染缓冲区:颜色,深度,模板。

  • 颜色渲染缓冲区存储由OpenGL渲染生成的最终彩色图像。颜色渲染缓冲区是一种彩色图像。
  • 深度渲染缓冲区存储最终Z深度对象的信息。如果你熟悉3D软件,你就知道什么是Z深度图像。这是关于在3D空间对象的Z位置灰度拉伸图像,其中全白代表最接近可见对象,黑代表最远对象(全黑就不可见)
  • 模板渲染区是知道对象的可见部分,像可见部分的面具。模板渲染缓冲区是黑白图像。

缓冲区对象是OpenGL称为“服务器端”(或服务器地址空间)的存储。缓冲区对象也是临时存储,但不像其它那样的临时。一个缓冲区对象可以在整个应用程序执行过程中持久存在。缓冲对象可以以优化的格式保存有关3D对象的信息。这些对象的信息可以是两种类型:结构和索引。

结构是用来描述你的3D对象的数组,如顶点数组,纹理坐标数组或者其它你想要的数组。索引更具体。数组索引将被用来指示你的网格面将根据数组结构构建。

是不是更疑惑?

OK,那我们来看个例子。想象一个3D立方体。这个立方体有6个面和8个顶点组成,是不是?

translate_cube_example

这6个面都是一个四边形,但是你要记得OpenGL只知道三角形?所以我们需要变换这些四边形为三角形让OpenGL处理。当我们在做这些时,6个面将会变成12个面!上面的图片是用Modo制作,看右下角。由Modo提供的信息关于这个网格。正如你看到的8个顶点和12个面(GL:12)。

现在,让我们想想。

在OpenGL中三角形由3个3D顶点组合。因此为了构建立方体的前面我们需要这样命令OpenGL:{vertex1, vertex2, vertex3},{vertex1, vertex3, vertex4}。对吗?

换句话说,我们要在每个立方体的面要重复2个顶点。这可能是比较糟糕的,如果我们网格是五角形,我们需要重复4个顶点信息,如果是六角形,需要重复6个顶点信息,7边形重复8个顶点信息等等。这将是非常非常夸张的。

因此,OpenGL给我们提供了一种更简单的方式。称做数组索引!在上面的立方体的例子中,我们有一个8个顶点的数组:{vertex1, vertex2, vertex3, vertex4, ...},为了每个立方体的面不重写这些信息,我们构建了一个索引数组:{0, 1, 2, 0, 2, 3, 2, 6, 3, 2, 5, 6, ...}。在这个索引数组中,每三个元素的结合({0,1,2 - 0,2,3 - 6,3,2})代表一个三角形面。由于这个特性我们可写一次顶点信息,可以在索引数组中重用多次。

现在,返回到缓冲区对象,第一类是结构数组,如L{vertex1, vertex2, vertex3, vertex4, ...},第二类是索引数组,如:{0, 1, 2, 0, 2, 3, 2, 6, 3, 2, 5, 6, ...}。

缓冲区对象的巨大优势是是它们被优化了,可以直接在GPU处理器中工作,同时在创建缓冲区对象后,你也不再需要在你的应用中持有数组。

光栅化

光栅化是OpenGL获取所有关于3D对象信息(所有坐标,顶点,数学等)来创建2D图像。图像会遭到一些改变,然后将会(通常)显示在设备的屏幕上。

但是最后一步,桥接像素信息和设备的屏幕是供应商的责任。Khronos小组提供另一套API,被称为EGL,但是在这里厂商可能会干涉。我们开发者不直接使用Khronos EGL,而是使用开发商修改的版本。

所以,当你使用OpenGL渲染时,你可以选择使用开发商的实现直接渲染到屏幕上,或者渲染到一个帧缓冲区。渲染到帧缓冲区,仍然是在OpenGL API中,但内容不会显示在设备的屏幕上。直接渲染到设备的屏幕,你就离开了OpenGL API进入EGL API。所以在渲染时候,你可以选择两个输出其中一个。

但现在不用担心这些,我说过,每个开发商实现他们自己的EGL API。如:Apple不让你直接渲染到设备的屏幕,你总是需要渲染到一个帧缓冲区,然后使用Apple的EGL的实现去显示内容到设备的屏幕上。

OpenGL管线

我前面说了可编程管线和固定管线。但到底啥是可编程管线?能用一句话描述?

可编程管线是图形库委派给我们开发者,负责与相机,灯光,材质,效果相关的所有事。我们可以用这些去处理非常好的着色器。所以每当你听到关于可编程管线可以想象成着色器!

但是现在,着色器到底是什么?

着色器就像小块代码,小程序一样,直接在CPU中进行复杂的计算。复杂的类似:具有T纹理的曲面的点的最终颜色,由TB凹凸纹理修改,使用具有镜面反射级别SL的镜面颜色SC,在具有功率LP的光L下,从距离入射角LA和衰减F,所有这一切由位于P位置的摄像机C的镜头捕获通过镜头T投射。

不管这意味着什么,由CPU来处理是太复杂了,对于图形库继续关心这些也是很复杂的。因此图形管道是我们用来管理这类的事。

那固定管线呢?

正好相反,固定管线是图形库所关心的所有种类的事,并为我们提供API来设置相机、材质、灯光和效果。

创建着色器我们用的语言像C,我们使用OpenGL着色器语言(GLSL)。OpenGL ES使用用更加严格的版本,叫做OpenGL ES着色器语言(也称为GLSL ES 或 ESSL)。不同点是GLSL比ESEL中有更多固定的功能和可写更多的变量,但它们的语法是一样的。

好啦,但这些着色器是怎么工作的?

你在一个独立的文件中创建或者直接写在代码中,无论哪种方式,重要的是最终的字符串包含SL,将会发送到OpenGL的内核,同时内核将会为你编译着色器(你也可以使用一个预编译的二进制着色器,但这是另一系列的一个部分)。

着色器成对工作:顶点着色器和片段着色器。这个主题需要更多的注意,所以让我们仔细看看顶点和片段着色器。为了理解每个着色器是什么,让我们回到立方体的例子。

translate_cube_example

顶点着色器

顶点着色器,也称为VS或VSH的一个小程序,它将在每个顶点的网格执行。看上面的立方体,如我前面说的一样,此立方体需要8个顶点(在这个图片中5号顶点不可见,你马上就会明白是为什么)。因此立方体的VSH会被CPU处理8次。顶点着色器将要做的是定义最终的顶点的位置。你还记得可编程管线留给我们自己负责相机?现在是时候了。

相机的位置和镜头可以干扰最终的位置。顶点着色器也负责准备和输出一些变量给片段着色器。在OpenGL中我们可以为顶点着色器定义变量,但是不能直接给片段着色器定义变量。因为片段着色器的变量必须从顶点着色器传入。

为什么我们没有直接访问片段着色器呢?好吧,让我们看看FSH你就会明白。

片段色器

再次看下那张立方体图片。

你注意到了5号顶点不可见了吗?这是因为在当前特定的位置和特定的旋转,我们只能看见3个面,而这3个面由7个顶点组成。

这就是片段着色器干的活!在最终图片每个可见片段的FSH将会被处理。到这里你可以理解一个片段作为一个像素。但通常不是确切的一个像素,因为在OpenGL的渲染与在最终呈现图片的设备之间可能会有拉伸。因此一个片段最终少于一个像素还是多于一个像素,取决于设备和渲染器的配置。在上面的立方体,片段着色器将会处理由7个顶点组成的3个可见面的每一个像素。

在片段着色器的内部,我们将处理所有与网格表面相关的,像材质,碰撞效果,阴影和光照效果,反射,折射,纹理,以及其它任何我们想要的效果。片段着色器最终的输出是以RGBA格式的一像素的颜色。

现在,最后一件你需要知道的事情是VSH和FSH怎么一起工作。对于一个片段着色器来说,它是强制性的一个顶点着色器,不多也不少,必须完全一对一。

总结

Very Well!!

这就是所有关于OpenGL的概念。让我们记住一切。

  1. OpenGL的逻辑仅由3个概念组成:Primitives(基元)、Buffers(缓冲区)、Rasterize(光栅化)
  • 基元是点、线和三角形
  • 缓冲区可以是Frame Buffer,Render Buffer,或 Buffer Objects
  • Rasterize是将OpenGL数学转化为像素数据的过程
  1. OpenGL与固定或可编程管线一起工作
  • 固定管线很老,慢,大。有大量的固定的函数处理相机,光照,材质,和效果
  • 可编程管线比固定管线更简单,快速,简洁,因为以可编程的方式OpenGL让我们开发者,负责处理相机、光照,材质和效果
  • 可编程管线与着色器同义,在每个网格的顶点顶点着色器和,在每个网格的可见片段的片段着色器。每对顶点着色器和片段着色器被编译进一个称作Program的对象。

看到这3个主题,OpenGL看起来好像理解和学习起来很简单。是的!理解起来是简单,但学习起来。。。hmmm。。。这三个小主题有很多的分支,并且学习起来可能需要数月或更多的时间。

我将尝试在这个系列接下来的两个部分告诉你我在六个沉浸的学习中学到的所有的关于深入OpenGL的研究。在下一文中,我将会展示基础的函数和一个使用OpenGL的3D应用的结构,独立于你使用的编程语言或你最终的设备。

但在这之前,我想介绍你们一个新的OpenGL的概念。

OpenGL的错误API

OpenGL是一个像港口超重机样的强大工作的状态机,你没有机会访问内部发生的。因此一个错误在内部发生,将不会有什么发生在你的应用,因为OpenGL是一个完全的外部内核。

但是怎样知道你的着色器有一个小的错误?怎样知道你的渲染缓冲区没有配置恰当?

处理所有这些错误,OpenGL提供了一个错误的API。这个API非常简单,它有几个成对的固定函数。其中一个是简单的检查,对或错,仅仅是知道是否成功了。另一对是检索错误信息。非常简单。第一个检查非常快,如果有一个错误你将得到信息。

通常,我们在关键的位置放置一些检查,像编译着色器或缓冲区配置,保持关注最严重的错误。

posted @ 2020-09-04 08:44  20190311  阅读(312)  评论(0编辑  收藏  举报
---页脚---