顶点和片段着色器

  • Marco Scabia

2011 年 11 月 3 日

话题:语言 & 开发

目录

需求

预备知识

最好对 Stage3D API 有基本的了解。最好拥有使用 VertexBuffer 的经验。在完成本教程之前,一定要阅读这个关于 Stage3D 的系列的上一篇教程(Stage3D 的工作原理)。

用户水平

中级

需要的产品

本教程将介绍着色器。着色器是 Stage3D 呈现管道的核心。您将学习什么是顶点着色器和片段着色器,它们如何适合 3D 呈现管道,以及为什么需要它们。

使用着色器

在本系列的上一篇介绍 Stage3D 工作原理的文章中,您已知道 Stage3D 基于一种可编程图形管道。

Stage3D 的可编程图形管道非常有用,因为您可以对呈现行为进行编程。这是对旧有的固定函数图形管道的一项重大改进,涉及到向 GPU 提供数据,以便它可以在无需任何控件的情况下呈现。

但是,它还意味着为了使用 Stage3D,您需要负责编写这些实际使管道可编程的可编程代码。

图形管道中的可编程代码是一些称为着色器的程序。着色器有两种工作方式:顶点着色器和片段着色器(也称为像素着色器)。要完全使用 Stage3D 呈现所有内容,您需要编写至少 1 个顶点着色器和 1 个片段着色器,否则您的图形管道将无法编程,也不会生效。

着色器是一些在您的图形硬件 GPU 上运行的小程序,不会像正常的 ActionScript 代码一样由 CPU 执行。这是着色器程序与常规代码之间最重要的区别。

着色器就是 GPU 程序。

在 Stage3D 内,着色器使用一个名为 Program3D 的特殊的 Stage3D API 类包装。Program3D 提供了创建着色器实例并将它上传到 GPU 所需的功能。着色器程序然后即可执行,Stage3D API 允许在您的主要 ActionScript 代码与着色器程序之间通信。

着色器运行速度很快。但请注意,ActionScript 是一种较慢的语言。但是当运行 ActionScript 时,因为您是在一个虚拟机内执行代码,所以该语言的运行速度比 C++ 等原生语言更慢。这就是如今的许多 AAA 游戏仍然使用 C++ 编写的原因。

在首次使用 Flash 时,您可以创建 GPU 原生的着色器程序——以便它们可以完整的(GPU)原生速度运行。

理解着色器如何适合可编程图形管道

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

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

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

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

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

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

这些插入的未处理的片段然后作为输入发送到片段着色器,后者使用该数据建立最终的像素颜色。

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

使用顶点着色器

顶点着色器是在 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 效果。例如,动态光线效果大部分都是使用片段着色器完成的。可以想象,动态光线意味着依据场景中存在的光线、它们相对于我们的几何体的位置,以及几何体的材质来计算像素颜色。这正是动态光线最常使用片段着色器创建的原因。

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

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

延伸阅读

在本教程中,您学习了使用顶点着色器和片段着色器的概念。这些着色器构成了 Stage3D API 中的呈现管道的核心。着色器可用于创建所有类型的 3D 图形效果,有大量在线文献介绍了基于着色器的技术。请继续阅读这个 Stage3D 系列中的下一篇教程,了解着色语言,专门学习一下 AGAL (Adobe Graphics Assembly Language)。

要更详细地研究着色器,我建议阅读 nVidia 所编写的流行的“GPU Gems”图书,它们已免费在线发布。它们没有专门介绍使用 Flash 和 Stage3D,但提供了对您可以使用着色器创建的效果的有用概述:

查看原文:Vertex and Fragment Shaders

语言 & 开发