写在前面

啦啦啦,搞了很久的Unity Shaders,越学越觉得基础知识很重要。学Unity Shader的时候,总会想,shader到底是什么呢?shader的pipeline是什么呢?它们是怎么工作的?有哪些限制?等等问题。但这些问题,Unity是不负责告诉你的。它专注于how,而不是what和why。想要深入理解一些问题,感觉还是要从GL或者DX学起。后面会学习GL龙书第八版~当然Unity我也不会放弃的。

这篇文章旨在回答一些基本问题。We always rant about them...

什么是OpenGL

这个问题很简单,它就是应用程序接口,也就是API,用于访问图形硬件中的可编程特性。OpenGL和DX相比有一个很大的特点就是跨平台的特性。换句话说,它是不依赖硬件的接口,可以运行在各种不同类型的图形硬件系统上,甚至完全是一个软件(而没有图形硬件)。

OpenGL是一种客户端-服务器(client-server)类型的系统。我们编写的程序就是一个客户端,而我们的计算机图形硬件制造商提供的OpenG的实现就是服务器。在一些OpenGL的实现里(例如一些和X Window System相关的应用),客户端和服务器可能会在不同的机器上运行,中间用网络连接。在这种情况下,客户端可以发起OpenGL命令,然后转换成窗口系统特定的协议,再发送给服务器,最终在服务器上执行OpenGL进行图像显示。

为什么OpenGL不提供窗口操作

这个问题我经常会问。。。为什么写个GL还要用这么多第三方库!连个窗口都不能自己画吗!这其实不能怪OpenGL,这正是它的优点——跨平台的特点造成的。因为它可以不依赖硬件和系统,因此就不会包含执行窗口任务的函数,或者处理用户输入等。这些函数是由我们使用的应用或系统来提供。

为什么OpenGL没有读取三维模型或者图片的函数

我以前经常抱怨,发展这么多年的OpenGL,怎么连读取三维模型这么简单的画图需求都不提供呢!!!好吧,这也是它的跨平台特性造成的。和上一点一样,这些操作是和系统存储格式密切相关的,OpenGL不管的~我们必须从点、线、三角形和patches这样的几何图元集合中自己构建三维对象。

什么是Shader

这是一类在图形硬件上执行的特殊函数。我们可以理解成,Shader是一些为图形处理单元(GPU)编译的小程序。OpenGL包含了编译工具来把我们编写的Shader源代码编译成可以在GPU上运行的代码。在OpenGL中,我们可以使用四种shader阶段。最常见的就是vertex shaders——它们可以处理顶点数据;以及fragment shaders,它们处理光栅化后生成的fragments。

vertex shaders和fragment shaders是每个OpenGL程序必不可少的部分。

Shader有什么用

请直接看下一节~

OpenGL的渲染流水线(Rendering Pipeline)

渲染流水线,就是一系列有序的处理阶段的序列,用于把我们应用中的数据转化到OpenGL生成一个最终的图像。下图是OpenGL4.3使用的流水线。(跟早期的相差很大)(左图来源:OpenGL Wiki

上图中,蓝色的方块表示是可编程的shader阶段。

OpenGL从我们提供的几何数据(顶点和几何图元)出发,首先使用了一系列shader阶段来处理它:vertex shading,tessellation shading(它本身就包含了两种shaders),接着是geometry shading,然后再传递给光栅化程序(rasterizer);光栅化程序将会为每个在裁剪区域(clipping region)内部的图元生成fragments,然后再为每个fragment执行一个fragment shader。

如你所见,shaders真是无处不在啊!不是所有的阶段都是需要的。如上面所说,只有vertex和fragment shaders是我们必须实现的。Tessellation和geometry shaders都是可选的。

下面,我们对每个阶段进行更深入地解释。下面的内容难度系数五颗星(对新手。)!但是,请坚持看下去!它们很重要!

Vertex Specification

准备顶点数组数据(vertex array data)

即进行顶点规格定义。应用将会建立一个有序的顶点列表,然后发送给OpenGL。这些顶点定义了图元的边界。图元是基本的绘制形状,如点、线、三角形。这些顶点列表是如何被组织成一个个图元的会在后面的阶段里进行处理。

这个阶段会处理一些顶点数组对象(Vertex Array Objects)顶点缓存对象(Vertex Buffer Objects)。VAO定义了每个顶点包含的信息,而VBO则是顶点本身的数据所在。

一个顶点的数据就是一系列顶点属性(vertex attributes)。每一个attribute都是一个数据的集合,用于后面的阶段进行处理。虽然这些attributes定义了一个顶点,但没有要求说一个顶点的attributes集合中必须包含了位置和法线信息。实际上,attribute数据是完全任意的,它们仅仅是“数据”,在这个准备阶段不会有人在意你传递的到底是什么attribute,它们的真正含义会在顶点处理阶段进行解读。

OpenGL要求所有的数据都必须存储在缓存对象中(buffer objects)。缓存对象是OpenGL管理的一些内存空间。“想要让我办事,请先把你的东西放到我的地盘!”把数据放到这些缓存里有很多方法,但最常见的是使用glBufferData()来实现。当然这里面还有一些其他步骤,我们后面会讲到。

向OpenGL发送数据

在我们定义了顶点相关信息后,我们可以通过调用OpenGL的绘图操作来要求按一个个几何图元绘制到屏幕上。这些操作例如有glDrawArrays()。我们后面会讲到。这个绘制的过程意味着我们把顶点数据传递给OpenGL服务器。

Vertex Processing

对于通过绘制命令绘制的每一个顶点,OpenGL将调用一个vertex shader来处理关于这个顶点的相关信息。Vertex shader接受从上个阶段传递来的attribute作为输入,然后把每一个输入顶点转换成一个输出顶点(这个关系是1对1的,不会多也不会少)。和输入的顶点信息不同,输出的顶点数据有一些必需的要求——vertex shader必须填充一个位置信息。

Vertex shaders的复杂性可以变化非常大,有的很简单,就是仅仅复制数据然后传递给下一个流水线阶段,我们称这种为pass-through shader;有的很复杂,会执行很多操作来计算顶点的屏幕位置(通常使用变换矩阵来完成,后面会讲到),还可能会进行光照计算来计算顶点的颜色,或者其他技术。

通常,一个应用会包含多个vertex shader,但同一时间只有一个会被激活(active)。

Primitive Assembly

Primitive assembly就是把vertex shader输出的顶点数据集合在一起,并把它组合成一个图元的过程。用户渲染的图元的类型决定了这个过程是如何工作的。

这个过程的输出是一个有序的简单图元(点、线或者三角形)序列。

Tessellation Shading

Tessellation,读 泰斯类什,可以翻译成曲面细分。在vertex shader处理了每一个顶点的相关信息后,如果tessellation shader阶段被激活的话,它就会继续处理这些数据。在后面我们会看到tessellation使用patchs来描述一个对象的形状,并且允许细化(tessellate)相对简单的patch集合,来增加几何图元的数量,提高模型的平滑度和真实度。Tessellation Shading阶段可以使用两个shaders来控制patch数据,以及中间一个固定函数的tessellator来生成最终的形状。

更多内容可以参见这篇文章(虽然是DirectX的。。。)

Geometry Shading

这一阶段允许在光栅化之前处理单独的几何图元,包括创建新的图元。这一阶段同样是可选的,但是会很有用!后面会讲到。

Geometry shader会处理每一个输入的图元,然后返回0个或更多的输出图元。它的输入是primitive assembly的输出图元。因此如果我们按triangle strip看待一个图元,那么geometry shader看到的就会使一系列三角形。

然而也有一些输入的图元类型是专门为geometry shaders定义的。这些相邻的图元可以让GS了解关于相邻顶点的信息。

GS的输出可以是0个多更多的简单图元。GS可以移除图元,或者根据一个输入图元来输出更多的图元来细分(tessellate)它们。GS甚至可以改变图元的类型,比如把点图元编程三角形,把线图元变成点。

Transform Feedback

Geometry shader或者primitive assembly的输出会被写入一系列的缓存对象。这被称为transform feedback模式。它允许我们通过vertex和geometry shaders来变换数据,然后再等待后续使用。

通过舍弃光栅化的结果,流水线可以在这步就停止了。这允许transform feedback成为渲染的唯一输出。

裁剪(Clipping)和剔除(Culling)

然后就进入图元裁剪和适当的剔除阶段了。

裁剪以为着,如果有图元处于视野的边界上,即一部分在内部一部分在外部,那么它就会被裁剪成一些小的图元。而且,vertex shader可以在空间内定义一些裁剪平面,这些裁剪平面又会引起额外的裁剪。

三角面的剔除同样在这一阶段完成。处于视野范围以外,或者在裁剪平面的边界内部的图元,都会被提出。

光栅化(Rasterization)

在裁剪完成后,更新后的图元就会被发送给光栅化程序去生成fragments。那么什么是fragment呢?一个fragment可以看成是一个“候选像素”。这类像素在帧缓存中的一块区域中。一个fragment仍可以被拒绝(reject),并且永远不会更新它的相关像素位置。

Wiki上把fragment成为是一个状态的集合,用于计算一个像素的最终数据。一个fragment的状态包含了它在屏幕空间的位置信息,样本覆盖(sample coverage,如果开启了multisampling的话),以及一些其他由vertex或者geometry shader输出的数据。这些数据集合是通过该fragment对应的顶点数据进行插值计算而得的。这个插值计算是由输出这些数据的shader定义的。

处理fragments是后面两个阶段的任务——fragment shading和per-fragment操作。

Fragment Processing

最后一个我们可以编程控制颜色的阶段就是fragment shading。在这个阶段,我们使用一个shader来决定该fragment的最终颜色(其实下个阶段,per-fragment操作仍可以进行最后的颜色修改)和它的深度值(depth value)。在fragment shaders里我们可以进行非常强大的纹理映射的工作。如果一个fragment shader认为某个fragment不应该绘制出来,它还可以终结一个fragment的处理过程。这个过程称为fragment discard。

一个fragment shader会输出一个颜色列表、一个深度值和一个stencil值。Fragment shaders不可以为一个fragment设置stencil,但是它们可以控制颜色和深度值。

我们可以想来区分vertex shading(包括tessellation和geometry shading)和fragment shading:vertex shading决定了一个图元在屏幕上的位置,而fragment shading使用这些信息来决定该fragment的颜色。

Per-Fragment操作

从fragment处理器输出的fragment数据会再通过一系列的步骤。

第一个步骤就是各种剔除检验(culling tests)。如果开启了stencil test,如果一个fragment没有通过检验它就会被剔除,而不会写入到帧缓存中;如果开启了depth test,如果一个fragment没有通过检验它就会被剔除,而不会写入到帧缓存中。只要没有通过任何一个检验,fragments都会被剔除,不会添加到帧缓存中。

如果一个fragment成功通过了所有的检测,它就会直接写入帧缓存中,更新它的像素颜色(也可能是深度值)。如果blending被开启了,该fragment的颜色会和当前的像素颜色进行混合去产生一个新的颜色,再写入帧缓存中。

最后,fragment数据被写入帧缓存中。Masking operation允许用户避免写入特定的值。写颜色、深度和stencil都可以被mask成on或者off;单独的颜色通道也可以。

原文链接:http://blog.csdn.net/candycat1992/article/details/39675513

OpenGl 知识一的更多相关文章

  1. OpenGL 知识二

    OpenGL综述 September 14, 2014 学习OpenGL是学习计算机图形学的一个工具,因为计算机上图形的显示要依靠底层的软件和硬件,学习图形学除了学习基本的概念,线,曲面,图形生成,变 ...

  2. OpenGL判断一个点是否可见

    关于OpenGL中判断一个点是否可见,可以分成两种情况讨论:点在2D空间中和3D空间中的时候.并且"在2D空间中"可以看作"在3D空间中"的特殊情况. 温馨提示 ...

  3. Android中直播视频技术探究之---采集摄像头Camera视频源数据进行推流(采用金山云SDK)

    一.前言 在之前已经详细介绍了Android中的一种视频数据源:Camera,不了解的同学可以点击进入:Android中Camera使用详解 ,在这篇文章中我们介绍了如何采集摄像头的每一帧数据,然后进 ...

  4. 深入理解 iOS Rendering Process

    本文将从 OpenGL 的角度结合 Apple 官方给出的部分资料,介绍 iOS Rendering Process 的概念及其整个底层渲染管道的各个流程. 相信在理解了 iOS Rendering ...

  5. GOOGLE VR SDK开发VR游戏,VR播放器之中的一个

    近期一年来,VR虚拟现实和AR增强现实技术的宣传甚嚣尘上.事实上VR,AR技术非常早就有了,一直没有流行开来.不可否认价格是影响技术推广的最大壁垒. 谷歌对VR最大的贡献是提供了便宜的谷歌眼镜,依照G ...

  6. [Reversal 剧情设计] 序言

    想开发一个图形游戏已经很久了,这个寒假好不容易学了一些OpenGL知识,初步具备了开发图形游戏的能力,我觉得我可以开始了.当然,我的游戏并不是冷冰冰的PVE,肯定还需要一些剧情丰富色彩. 本来我的游戏 ...

  7. Vulkan Driver for VC4(Raspberry Pi 3b) base on mesa

    这是一篇关于在raspberry Pi 3b上移植实现vulkan 驱动的文章. 经过一段时间的代码搬运,终于实现了零的突破,可以在树莓派3B上运行Vulkan triangle/texture.当然 ...

  8. OpenGL———混合的基本知识

    混合是一种常用的技巧,通常可以用来实现半透明.但其实它也是十分灵活的,你可以通过不同的设置得到不同的混合结果,产生一些有趣或者奇怪的图象.混合是什么呢?混合就是把两种颜色混在一起.具体一点,就是把某一 ...

  9. OpenGL ES 3.0 帧缓冲区对象基础知识

    最近在帧缓冲区对象这里卡了一下,不过前面已经了解了相关的OpenGL ES的知识,现在再去了解就感觉轻松多了.现在就进行总结. 基础知识 我们知道,在应用程序调用任何的OpenGL ES命令之前,需要 ...

随机推荐

  1. PHP的Try, throw 和 catch

    PHP的Try, throw 和 catch   Try, throw 和 catch Try - 使用异常的函数应该位于 "try" 代码块内.如果没有触发异常,则代码将照常继续 ...

  2. [hadoop读书笔记]前言

    hadoop2.0+主要变化: 全新的MapReduce 2,它建立在一个新的分布式资源管理系统之上,该系统称之为YARN. YARN:分布式资源管理系统

  3. [kafka] 004_kafka_安装运行

    1.下载和安装 目前kafka的稳定版本为0.10.0.0 下载地址:http://kafka.apache.org/downloads.html 下载后解压缩安装包到系统即可完成安装 > ta ...

  4. listview item 动画

    http://blog.csdn.net/lixiaodaoaaa/article/details/8284246 先看实现的效果如下: 演示效果如上:   上面的列表是GridView 要给这个Gr ...

  5. js 实时数据显示

    js代码 <script type="text/javascript" src="jquery.js"></script> <sc ...

  6. 关于Unity中的光照(二)

    光源 1: 光照的本质:就是光的颜色和物体纹理的颜色的混合;2: 光源类型: 点光源,定向光源,聚光灯, 区域光源; 区域光的范围会在场景中用黄色的光显示出来; z轴是光的方向; 光的强度会随距离衰减 ...

  7. (笔记)如何安装Arm-linux-gcc

      如何安装Arm-linux-gcc   安装交叉编译工具链: 1.首先以root用户登入 2.复制arm-linux-gcc-4.3.2.tgz到根目录下tmp文件夹里 3.解压命令tar xvz ...

  8. 使用ClaimsIdentity来实现登录授权

    背景:以前做登录时用的都是FormsAuthentication.SetAuthCookie(model.UID, IsRemeber),但是有一个不好,不能存储多个值,有时候我们既想存储登录用户的U ...

  9. unity--------------------------WheelCollider和小车实验的总结

    WheelCollider总结 写了前面两篇文章,我想总结一下WheelCollider! 让我们能够更清晰的学会物理车的开发! 1.车的层次结构 一般这样分,车身,车身的包围盒,四个轮子和四个轮子的 ...

  10. LigerUI可编辑表格左下角出现白色小方块遮罩层问题解决办法

    LigerUI已经研究了一段时间,总体感觉还不错,基于jQuery开发,框架提供了丰富的UI组件,尤其LigerUI表格,功能还是挺强大的 在使用LigerUI可编辑表格的时候,发现一个小小的问题 当 ...