OpenGL Shader笔记

  • OpenGL执行模型可以被描述为客户-服务器。应用程序(客户)发出OpenGL命令,然后由一个OpenGL实现(服务器)解释和处理这些命令。应用程序和OpenGL实现可以在一台单独的计算机或者两台不同的计算机上执行。一些OpenGL状态被存储在应用程序的地址空间中(客户状态),但它的主要部分是存储在OpenGL实现的地址空间中(服务器状态)。
  • OpenGL命令总是按服务器接收它们的顺序来处理,不过命令的完成可能会由于引起OpenGl命令被缓冲的中间操作而延迟。
  • OpenGl的数据绑定发生在发出的命令时而不是执行命令时。
  • 图形加速器通常还有一个名为“屏幕外内存(OFFSCREEN MEMORY)”的内存区域,它是不可显示的,用来存储不可见的内容。
  • 由于OpenGL渲染而修改的图形内存区域被称为“帧缓冲区(FRAME BUFFER)”。
  • 在非窗口系统中,OpenGL帧缓冲区对应的是整个显示屏。
  • 支持OpenGL渲染的窗口(也就是帧缓冲区)可能由以下内容的某种组合构成:

至多4个颜色缓冲区、一个深度缓冲区、一个模板缓冲区、一个积聚缓冲区(accumulation buffer)、一个多重采用缓冲区(multisample buffer)。

  • 大多数图形硬件支持一个前台缓冲区和一个后台缓冲区,以便执行“双重缓存(DOUBLE BUFFERING)”。
  • 模板缓冲区“(STENCIL BUFFER)”用来执行复杂的遮掩操作。模板缓冲区中可以存储一个复杂的形状,后续的绘制操作可以使用模板缓冲区的内存来确定是否更新各个像素。
  • 积聚缓冲区“(ACCUMULATION BUFFER)”是一个颜色缓冲区,它通常具有比颜色缓冲区精度更高的组件。
  • 显示列表是OpenGL管理的一种数据结构,它存储了以后要执行的命令。显示列表可以包括设置状态的命令以及绘制几何对象命令。显示列表存储在服务器端,以后可以通过调用glCalList或glCallLists来处理。
  • 顶点位置会通过模型矩阵和投影矩阵来转换,法线会通过从模型矩阵获得的左上角的3X3矩阵的逆变换来转换,纹理坐标会通过纹理矩阵来转换,光照算法会得到应用以修改基色,而且可能会自动生成纹理坐标,应用彩色材质状态,并计算点的大小。
  • 顶点阶段有时也叫做“转换和光照(TRANSFORMATION AND LIGHTING)”,或者我们更熟悉的“T&L”。
  • OpenGL中的每一个模拟光源都具有一些属性,以使其成为一个定向光源、点光源或者聚光灯。应用程序可以调整的光照属性包括发出的光的颜色(定义为环境、漫反射以及镜面的RGBA强度值),光源位置,衰减因子(定义了强度降低的速度,是一个距离的函数),以及聚光灯的方向、指数和截止因子。
  • 可以通过一个值来修改多边形中的各个片元的深度值,这个值是使用glPolygonOffset所设置的状态计算得到的。通过glFrontFace,可以设置多边形朝向前面的那个方向。平滑图元的锯齿状外观的过程被称为“反走样(ANTIALIASING)”,通过使用适当的符号常量(GL_POINT_SMOOTH、GL_LINE_SMOOTH或者GL_POLYGON_SMOOTH)调用glEnable,可以启用图元反走样。
  • 如果当前视图是透视视图,那么各个顶点都会将其x、y和z部分除以齐次坐标w。紧接着,将由当前的视口变换(通过调用glDepthRange和glViewPort来设置)来转换各个顶点,以便生成窗口坐标。
  • 控制或影响整个帧缓冲区的过程被称为“帧缓冲区操作(FRAME BUFFER OPERATIONS)”。
  • 按照OpenGL的说法,图像被称为“像素矩形(PIXEL RECTANGLES)”。
  • 从内存中读取各个像素时,像素会被转换为一个其中包含了颜色、深度或模板值的“像素组(PIXEL GROUP)”。
  • 为了把对象放在世界坐标中的正确位置,通常也需要进行一次变换。可以将所有这些单独的变换并放在一个矩阵中,即“模型变换矩阵(MODEL TRANSFORMATION MATRIX)”,它表示从对象坐标到世界坐标的变换。
  • OpenGL中的光照计算被定义为发生在眼睛坐标系中的各个顶点上。为了执行必要的反射计算,光源位置和表面法线必须在相同的坐标系中。OpenGL实现经常选择在眼睛空间中执行光照计算。因此还必须将输入的表面法线变换到眼睛空间中。这是通过使用模型矩阵的逆转置矩阵来变换表面法线而完成的。这时,可以应用OpenGL定义的各顶点光照公式来确定各个顶点上的光照颜色。
  • 图形图元的栅格化是使用窗口坐标执行的。
  • OpenGL有一个“纹理单元(TEXTURE UNIT)”的概念。纹理单元对应于执行不同纹理操作的图形硬件的底层部分。
  • 压缩的纹理在图像加速器上使用的内存可能会少的多,因此可以增强应用程序的功能或性能。标准OpenGL没有定义任何特定的压缩图像格式,因此为了确定一个特定实现所支持的压缩纹理格式,应用程序需要查询扩展字符串。
  • Mipmap纹理是一组表示同一幅图像的有序数组。每一个数组都有一个分辨率,它是每一维的上一个分辨率的一半。Mipmap背后的思路是,如果所使用纹理的分辨率与要在显示器上绘制的对象的分辨率大致相同,则将获得更令人满意的最终图像。
  • 对图形图元应用纹理值的方式是由纹理环境的参数控制的,这些参数可以使用glTexEnv函数来设置。使用通过纹理访问计算得到的值来替换对象颜色的固定公式组是相当冗长的。只要说明一点就足够了:纹理函数包括了替换、调制、贴花、混合、添加、以及更为复杂的红色、绿色、蓝色和alpha组合。
  • 纹理坐标还可以由OpenGL自动生成。控制自动纹理坐标生成的参数是使用glTexGen命令在各个纹理单元的基础上设置的。这个函数会让应用程序选择一个纹理生成函数并针对当前活动的纹理单元为这个函数提供系数。所支持的纹理生成函数有对象线性(对于自动生成地势模型的纹理坐标非常有用)、眼睛线性(对于生成移动对象上的动态轮廓线非常有用)和球面贴图(对于只需要一个纹理的环境贴图类型非常有用)。
  • OpenGL着色语言是一种高级的过程语言。

对顶点着色器和片元着色器使用的是同样的语言(只有很小的一些差异)。

它是基于C和C++语法以及流程控制;

它生来就支持矢量和矩阵操作,因为这些是许多图形算法所固定的;

在类型方面,它比C和C++ 更严格,并且调用函数时必须返回值;

它使用类型限定符而不是通过读取和写入操作来管理输入和输出;

着色器的长度没有实际的限制,也不需要查询长度。

  • 在顶点着色器中定义的变量可能被限定为“属性变量(ATTRIBUTE VARIABLES)”。

这些变量代表了非常频繁地从应用程序传递到顶点处理器的值。属性变量有两种:内置的和用户定义的。

  • “一致变量(UNIFORM VARIABLES)”用来将数据值从应用程序传递到顶点处理器或者片元处理器。一致变量通常用来提供变换相对不频繁的值。
  • 顶点着色器必须计算坐标在裁剪空间中的齐次位置并将结果存储在特殊的输出变量gl_Position中。将在用户裁剪和点栅格化过程中使用的值可以存储在特殊的输出变量gl_ClipVertex和gl_PointSize中。
  • 定义了从顶点处理器传递到片元处理器的数据的变量被称为“易变变量(VARYING VARIABLE)”。将它们称为易变变量是因为这些值在各个顶点可能是不同的,并且程序会执行透视校正插值,以便提供各个片元的值,供片元着色器使用。
  • 片元处理器的一大优点是它可以任意多次地访问纹理内存,并可以任意方式结合所读取的值。对于各个片元来说,片元着色器可以计算颜色和深度(将这些值写入特殊的输出变量gl_FragColor和gl_FragDepth)或者完全丢弃片元。
  • OpenGL着色语言不允许使用指针,它使用按值-返回的调用来避免这种别名。总的来说,就是不允许存在别名,从而简化了优化器的工作。
  • 要想访问颜色值,可以向矢量变量的名称添加.r以访问第一组分,添加.g以访问第二组分,添加.b以访问第三组分,添加.a以访问第4组分。使用.x、.y、.z和.w可以访问位置值,使用.s、.t、.p和.q可以访问纹理值。通过指定多个名称,例如.xy,就可以选定多个组分。
  • 被添加的还有一组名为取样器(sampler)的基本类型,它可以创建着色器用来访问纹理内存的机制。取样器是一种特殊的不透明变量类型,可用来访问特定的纹理贴图。
  • 属性变量用来将经常变化的值从应用程序传输到顶点着色器,一致变量用来将不经常变化的值从应用程序传输到任何着色器,而易变变量是用来将插值得到的值从顶点着色器传输到片元着色。
  • 添加了类型限定符attribute、uniform和varying,以便描述管理着色I/O的变量。

Attribute类型的变量允许在应用程序与顶点着色器之间交换经常更改的变量。

Varying类型的变量是顶点着色器的输出和片元着色器的输入。
Uniform类型的变量允许应用程序向顶点着色器和片元着色器提供更改相对较少的变量。

为了访问纹理,添加了数据类型sampler。sampler只能放在片元着色器中。

可以使用内置的变量名称来访问标准的OpenGL状态并与OpenGL固定功能进行通信。

包括了各种内置的函数,以便执行一般的图形操作。

像在C++中一样,需要有函数声明,并且支持基于参数数量和类型的重载。

可以在需要时声明变量。

  • OpenGL着色语言对待类型匹配是非常严格的。
  • 在声明时不能初始化属性变量、一致变量和易变变量。
  • 对varying限定变量的自动插值是以一种透视校正方式完成的。无论要进行插值的数据属于什么类型,这都是有必要的。否则,这些值就不会在因为表面细分而导致的边缘上平滑更改。插值得到的结果是连续的,但是它的导数不一定是连续的。
  • 函数可以返回一个值,也可以不返回任何值,如果一个函数不返回任何值,那么必须将它声明为void类型。如果函数返回一个值,那么其类型可以是除了数组之外的任何类型。不过,可以返回结构,且结构可以包含数组。
  • 有一个特殊的分支discard可以避免片元更新帧缓冲区。当流控制遇到这个关键字时,正在处理的片元就会被标记为将要丢失。
  • OpenGL着色语言是一种高级过程语言,它被专门设计成用于OpenGL环境。这种语言允许应用程序指定可编程的、高度并行的图形硬件的行为。它所包含的结构使得用户能以一种对于经验丰富的C和C++程序员来说非常自然的方式将图形着色算法简明地表达出来。

OpenGL着色语言包括了对以下元素的支持:标量、矢量和矩阵类型,结构和数组,

用来访问纹理的取样器类型,用来定义着色器输入和输出的数据类型限定符,用于初始化和类型转换的构造函数,以及类似于c/c++的操作符和流控制语句。

  • gl_Color和gl_SecondaryColor名称与可以在顶点着色器中使用的内置属性变量名称是相同的。但是,这不会导致名称冲突,因为属性只有在顶点着色器中才能看到,而下面这些变量只有在片元着色器中才能看到。
  • 位置不变性,对于多程渲染,即其中一些通道是使用顶点着色器执行的,而其他通道是使用固定功能管线的渲染,位置的不变性是非常重要的。这意味着对于对象坐标中的相同顶点位置以及相同的模型矩阵和投影矩阵,顶点着色器和固定功能都会计算裁剪坐标中的完全相同的顶点位置。在顶点着色器中按下列方式使用内置函数ftransform就可以实现位置不变性:gl_Position = ftransform();通常,因为可能存在的编译器优化以及潜在的底层硬件的差异,顶点着色器代码gl_Position = gl_ModelViewProjectMatrix * gl_Vertex不会导致位置不变性。
  • OpenGL着色语言对OpenGl所做的一个重要改进就是在纹理处理方面。其中一点就是,纹理操作可以在顶点着色器中执行。但是,管线的片元端也得到了改进。片元着色器可以访问的纹理图像单元要比固定功能管线更多。这意味着在一个单独的渲染通道中可以执行更多的纹理查找。而且有了可编程能力,所有这些纹理查找的结果就能够以着色器编写人员认为合适的任何方式结合起来。
  • 着色器会忽略纹理启用的固定函数分层结构(GL_TEXTURE_CUBE_MAP 、GL_TEXTURE_3D、GL_TEXTURE_2D和GL_TEXTURE_1D)。例如,即使对一个纹理图像单元启用了纹理目标GL_TEXTURE_1D,也可以使用了一个采样器来访问这个纹理图像单元的GL_TEXTURE_2D目标。
  • OpenGL中增加了两个新的可编程单元:顶点处理器和片元处理器。用来表示专门在这两种处理器上运行的算法的是同一种语言(只有一些很小的差别)。顶点处理器取代了OpenGL的固定功能顶点处理,专门在这种处理器上执行的着色器叫顶点着色器。作为当前状态的一部分安装时,顶点着色器会针对OpenGL处理的每一个顶点都执行一次。片元处理器取代了OpenGL的固定功能片元处理,专门在这种处理器上执行的着色器叫做片元着色器。作为当前状态的一部分安装时,片元着色器会针对栅格化所产生的每一个片元都执行一次。

在定义OpenGL的可编程阶段与中间的固定功能之间的接口时,人们投入了很多精力。结果是,允许使用可编程的顶点处理和固定功能片元处理进行渲染,反之亦然。内置的变量允许访问标准的OpenGL属性、与实现相关的常量以及各种当前状态。它们还允许着色器与图形处理管线中之前和之后的固定功能阶段进行通信。

  • OpenGL着色语言为标量和矢量操作定义了各种内置的方便函数。内置的函数基本上有三个类别。
    1. 它们以一种方便的方式体现某些必要的硬件功能,如访问一个纹理贴图。该语言中没有办法用着色器模型这些函数。
    2. 它们代表一个琐细的操作(限定、混合等),用户很容易编写这种代码,但是它们代表非常普通,且硬件可能直接支持它们。对编译器来说,如何将表达式映射到复杂的汇编程序指令中是一个问题。
    3. 它们代表图形硬件很可能在某些地方加速的操作。三角函数就属于这一类。
  • 除了ftransform之外,几何函数都可以在顶点着色器或片元着色器中使用。这些函数在矢量上就像矢量一样操作,而不是对各个组件进行操作。
  • 利用OpenGL着色语言带来的可编程能力,纹理内存就可以不仅仅是图像数据的存储空间。
  • OpenGL着色语言包含了大量的内置函数。其中一些函数与C/C++中的函数类似,还有一些函数与RenderMan中的函数类似。这些函数的目标是展示硬件功能(如纹理访问)或者提供对常见操作(如平方根、限定范围等)的支持,或者表示在将来的图形硬件产品中可能得到加速的操作(例如三角函数、噪声等)。

与在向量或者标量上操作的许多函数一样,函数重载被广泛使用。预计支持OpenGL着色语言的开发商们会提供这些函数的最优实现,所以应该尽可能地使用它们。

内置的数学函数可以通过某些独特并且可能是出乎意料的方式使用,从而创建出过程纹理。

  • 顶点着色器包含了在提供给OpenGL的各个顶点上发生的操作。为了定义顶点着色器,需要先回答三个问题。
  1. 对于每一个顶点,必须向顶点着色器传递哪些数据(也就是属性变量)?
  2. 顶点着色器需要有哪些全局状态(也就是一致变量)?
  3. 顶点着色器要计算哪些值(也就是易变变量)?
  • 每一个顶点着色器都必须计算齐次顶点位置并将它的值存储在标准变量gl_Position中。
  • 为了确定一个OpenGL实现是否支持OpenGL着色语言API,可以调用带符号常量GL_EXTENSIONS的glGetString,并查看返回的字符串是包含了扩展名称”GL_ARB_shader_objects”、”GL_ARB_vertex_shader”和“GL_ARB_fragment_shader”。
  • 在着色器内部,应用认为采样器是一个不透明的数据结构。
  • 添加到OpenGL中的用来创建和操作着色器的函数调用组实际上是相当小的。这个接口专门用来模拟c/c++程序员所遵循的软件开发过程。要想安装和使用OpenGL着色器,需要做以下工作。
  1. 使用glCreateShaderObjectARB创建一个或多个(空白)着色器对象。
  2. 通过调用glShaderSourceARB,为这些着色器提供源代码。
  3. 通过调用glCompileShaderARB编译各个着色器。
  4. 通过调用glCreateProgramObjetARB创建一个程序对象。
  5. 通过调用glAttachObjectARB将所有着色器对象附加到程序对象上。
  6. 通过调用glLinkProgramARB连接程序对象。
  7. 通过调用glUseProgramObjectARB,将可执行程序作为OpenGL当前状态的一部分安装。

在链接操作之后,可以调用glGetUniformLocationARB来查询用户定义的一致变量的位置,然后就可以通过调用glUniformARB的某种变化形式在这些位置中存储值。

通过调用glBindAttribLocationARB可以明确地将用户定义的属性变量与通用顶点属性索引关联起来,也可以在链接期间暗中分配这种关联并使用glGetAttribLoactionARB进行查询。然后应用程序可以使用glVertexAttribARB的某种变化形式,一次一个顶点地提供通用顶点属性值,或者通过调用glEnableVertexArrayARB和glVertexAttribPointerARB将其作为顶点数组的一部分来提供。

  • 决定在何处执行计算还需要了解一个特定渲染操作的计算瓶颈是在什么地方。只需加快系统中最慢部分的速度,就可以看到系统性能的改进。
  • 如果在着色器中定义了所有变量的有效范围,那么查看需要处理的边界条件就会更加容易。
  • 片元着色器唯一可能的成果就是输出一个片元颜色、一个片元深度,或者同时输出一个颜色和一个深度,或者因为遇到了discard关键字而不更新帧缓冲区。
  • 因为discard关键字可以出现在片元着色器中的任何位置,所以它非常有用。可以将discard语句放在一个复杂片元着色器的开始位置,然后在确认了一切正常时,就逐渐在代码中将它向下移动。
  • 对于OpenGL中每一个纹理单元都定义有一个纹理矩阵,可以使用内置的一致矩阵数组变gl_TextureMatrix访问它。可以像变换顶点位置一样变换传入的纹理坐标。

gl_TextureCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;

  • 内置的纹理函数用来从纹理内存中读取值。从纹理内存读取的值可以有多种用法。OpenGL固定功能包括了对纹理应用公式的支持,可以使用符号常量GL_REPLACE、GL_MODULATE、GL_DECAL、GL_BLEND和GL_ADD来启用它们。这些模式的工作方式各个不相同,具体情况取决于所访问的纹理的格式。下面的代码展示了使用取样器text0访问一个RGBA纹理的情形,变量color被初始化为gl_Color,并根据需要进行了修改,

从而使之包含由于应用了纹理而得到的颜色值。GL_REPLACE是全部纹理应用模式中最简单的一个。它只是使用从纹理内存读取的值替代当前的片元颜色。

GL_REPLACE的计算。

Color = texture2D(tex0,gl_TexCoord[0].xy);

GL_MODULATE会将传入的片元颜色与从纹理内存检索的值相乘。这是一个很好的纹理函数,如果在纹理处理之前计算光照(例如:顶点着色器执行光照计算,而片元着色处理纹理),则可以使用它。对于使用这种技术渲染的对象,可以使用白色作为基色,然后纹理会提供漫反射颜色。

Color *= texture2D(tex0,gl_TexCoord[0].xy);

在将一幅不透明图像应用于对象的一部分时GL_DECAL是非常有用的。例如,我们可能想把编号和公司徽标应用于一辆赛车的表面,或者将文身应用于游戏中某个角色的皮肤上。在访问一个RGBA纹理时,会使用各个纹理像素上的alpha值在传入片元RGB值与纹理的RGB值之间进行线性插值。传入片元的alpha值会照原样使用。

Vec4 texture = texture2D(tex0,gl_TexCoord[0].xy);

Vec3 col = mix(color.rgb,texture.rgb,texture.a);

Color = vec4(col,color.a);

GL_BLEND是唯一一个采用了当前纹理环境颜色的纹理应用模式。从纹理中读取的RGB值会用来在传入片元的RGB值与纹理环境颜色之间进行线性插值。将传入片元的alpha值与从纹理读取的alpha值相乘,可以计算出新的alpha值。

Vec4 texture = texture2D(tex0,gl_TexCoord[0].xy);

Vec3 col = mix(color.rgb,gl_TextureEnvColor[0].rgb,texture.rgb);

Color = vec4(col,color.a * texture.a);

GL_ADD会计算传入的片元颜色与从纹理读取的值的总和。两个alpha值相乘可以计算新的alpha值。这是唯一一种得到的值可以超过范围[0,1]的传统纹理应用模式,因此我们要限定最终的结果。

Vec4 texture = texture2D(tex0,gl_TexCoord[0].xy);

Color.rgb += texture.rgb;

Color.a *= texture.a;

Color = clamp(color,0.0,1.0);

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值