现在的位置: 首页 > 综合 > 正文

理解顶点着色器和像素着色器

2013年07月15日 ⁄ 综合 ⁄ 共 2744字 ⁄ 字号 评论关闭

首先,看一下通用着色器原理图:

再看一下可编程图形管道描述了 图形 内容呈现的方式(参见图1)。

可编程图形管道结构图。
图1. 可编程图形管道结构图。
 

查看上图,留意顶点着色器和片段着色器是如何构成管道中的重要部分的。

当呈现一个几何形状时,您将有一个称为 VertexBuffer 的顶点流,它生成几何三角形。这个来自顶点缓冲区的顶点流提供作为顶点着色器的输入,它可以以可编程的方式处理顶点数据。顶点着色器输出供 GPU 用于组装三角形。这些三角形然后在视口中经过恰当地切分和剔除,随后发送到Rasterizer 块 中,该块生成一个新的输出流,其中包含所谓的片段:微型的数据结构,每个片段与屏幕上出现的一个三角形像素相对应【可以简单的想象成等大的小圆片】。

片段的数据内容主要有顶点着色器确定。事实上,顶点着色器有能力将顶点属性参数作为它的输出传递。Rasterizer 所做的是将顶点着色器输出的针对每个顶点的数据插入到三角形中,使屏幕上的每个片段(三角形像素)获得这个特定像素的正确值。

例如,想象您的顶点缓冲区指定了一种顶点颜色作为顶点属性,您的三角形有两个顶点指定为白色,一个顶点指定为黑色。顶点着色器将把这些顶点颜色传递到它的 输出,传递到管道中的以下块中。然后,一个与三角形中某个位置相对应的片段将收到一个具有一定灰度的颜色:白色顶点和黑色顶点颜色的插值。这种灰度对于接 近白色顶点的片段会更亮,对于接近黑色顶点的片段会更黑。

这些插入的未处理的片段【所谓未处理是指片段还不具有颜色】然后作为输入发送到片段着色器,后者使用该数据建立最终的像素颜色。

除了它作为输入收到的这些片段,您也可以使用 代码向片段着色器发送一个或多个输入纹理,片段着色器可对这些纹理进行采样。

使用顶点着色器

顶点着色器是在 GPU 上运行的小程序。从名称可以看出,可通过处理它们来处理顶点。顶点着色器是处理顶点的微型程序。

一般而言,您将使用您的几何信息创建一个 VertexBuffer 并将它传递给 GPU。然后您将使用一种着色语言(比如 AGAL)编写一个顶点着色器。会为您的 VertexBuffer 中的每个顶点调用顶点着色器程序。

这就像有一个围绕顶点程序,执行以下任务的 for 循环:

for (var i:int = 0; i < vertexBuffer.length; i++)
{
    executeVertexShader(vertexBuffer[i]);
}

即使您没有看到 for 循环。所以会处理 VertexBuffer 中的所有顶点。

您也可以将常量值以常量寄存器的形式从 ActionScript 传递到 VertexShader。每次您希望运行着色器(每次您调用 Context3D::drawTriangles 方法来呈现一个结构),您都可以传入一个不同的值。着色器可以处理这个常量值,以调整它的算法和输出。

顶点着色器的输入是一个 VertexBuffer,它由一个或多个顶点属性流组成。在最低限度,一个顶点缓冲区必须包含顶点位置。这些顶点位置通常指一个 位于每个 3D 模型本地的坐标系统(每个模型拥有自己的原点)。顶点着色器将位置传输到屏幕空间,以便它们可以正确地显示。顶点缓冲区可能还包含其他顶点属 性,比如顶点颜色或纹理UV坐标。顶点着色器通常传递这些值作为输出(最终在处理它们之后),以便它们可以由 Rasterizer 插入并作为输入传入到片 段着色器中。

顶点着色器最明显和自然的用途是对屏幕中的几何形状执行矩阵变换。您会获得本地空间中的所有顶点。并将变换矩阵传递给顶点着色器。顶点着色器将使用矩阵转 换 VertexBuffer 中的所有顶点。它会非常块地执行此任务。比您在 ActionScript 中编写代码要快得多,因为它是硬件加速的。

有趣的是,顶点着色器完全是可编程的。您可通过您喜欢的任何方式修改您的几何形状。例如,一个修改顶点位置的典型应用是骨头的创建:您可以定义一组骨头、 您的几何形状的一个框架,以及一个皮肤(网格)。当骨头旋转时,它们位于一个层次结构中,它们修改其皮肤的形状。这正是创建手指动画的方式。这么做的最佳 方式是将骨头的旋转(转换)传递给一个顶点着色器,让顶点着色器修改皮肤,以便它看起来得到了恰当的绘制和变形。

顶点着色器还有许多其他的应用:模拟软布的外观,或者变形物体。创建两个网格,它们具有相同数量的顶点,让顶点着色器依据变形参数将网格1变形为网格2。

使用片段着色器

像顶点着色器一样,片段着色器也是在 GPU 上运行的小程序。从名称可以看出,片段着色器处理片段——它们负责输出每个呈现的三角形像素的最终像素颜色。

基本而言,它的工作原理如下:片段着色器以输入的形式收到顶点着色器通过管道传递的所有这些片段。如上所述,到达片段着色器的片段是顶点着色器的顶点属性输出的插入版本。

片段着色器执行流程在本质上就像一个隐藏的循环。如果您想象将您未处理的片段添加到某种称为的 fragmentStream 的流中,那么片段着色器的执行将等效于以下代码:

for (var i:int = 0; i < fragmentStream.length; i++)
{
    executeFragmentShader(fragmentStream[i]);
}

可以说,输入 fragmentStream 是未处理的。这意味着片段着色器可以处理它,计算该三角形屏幕像素的最终颜色。

片段着色器真正位于可编程图形管道的核心。片段着色器的最常见用途是计算从顶点属性颜色(用于顶点着色几何体)或从纹理和相关的顶点属性 UV 纹理坐标(用于纹理几何体)开始的各种三角形像素颜色。

但片段着色器并不仅限于创建这些简单效果,实际上,片段着色器可用于创建您在现代 3D 游戏中看到的所有令人惊艳的 3D 效果。例如,动态光线效果大部分都是 使用片段着色器完成的。可以想象,动态光线意味着依据场景中存在的光线、它们相对于我们的几何体的位置,以及几何体的材质来计算像素颜色。这正是动态光线 最常使用片段着色器创建的原因。

反射效果,比如水或环境映射,都是使用片段着色器创建的。可使用片段着色器创建的效果非常丰富,这些基本效果仅仅是所有可能性的冰山一角。

最后,您在屏幕上看到效果取决于片段着色器。所以片段着色器是管理实际呈现的内容的代码。

【我的总结】:讲到片段时,从光栅器Rasterizer中出来的未处理的片段流是二维的东西,所以讨论片段着色器时从它的输入开始就全部是二维的东西了,不用再想三维的场景了,可以完全和三维图形场景割裂开来思考想象。这些和屏幕对应的片段本质上是一个个数据结构,含有很多属性,足够形成最终的颜色了。也正是因此,纹理映射实际上都是平面对平面的映射,本质上是二维的东西铺在二维的片段们上。光栅器的作用可以简单的理解为就是通过差值运算生成片段流。

抱歉!评论已关闭.