BT

如何利用碎片时间提升技术认知与能力? 点击获取答案

什么是AGAL

| 作者 Marco Scabia 关注 0 他的粉丝 发布于 2011年11月11日. 估计阅读时间: 23 分钟 | QCon上海2018 关注大数据平台技术选型、搭建、系统迁移和优化的经验。

目录

需求

预备知识

需要基本了解Stage3D API。最好还拥有使用着色器的知识并理解可编程函数管道的操作原理。在阅读这些指令之前,一定要首先阅读这个关于Stage3D的系列中的前两篇教程(1. Stage3D的工作原理,2. 顶点和片段着色器)。

用户水平

中级

需要的产品

本文将介绍使用着色语言。我将介绍使用Stage3D API中所包含的低级着色语言AGAL(Adobe Graphics Assembly Language)的基本知识。您将了解AGAL是什么、它的工作原理,以及如何在基于Stage3D的ActionScript应用程序中使用它。

理解着色语言

在专门深入介绍AGAL之前,最好首先理解什么是着色语言,以及如何使用它创建着色器。

着色器不是使用ActionScript编写的。它们也不是使用C++或任何其他通用语言编写的。

着色器通常使用一种称为着色语言的特殊语言来编写。

着色器是在GPU上运行的程序,所以编写着色器的最有效方式是使用一种专为GPU而设计的语言。这正是您将使用3种特殊的皂色语言编写着色器,而砽专为CPU编码而设计的通用语言的原因。

有几种着色语言已使用了多年,用于两种标准的原生3D平台(OpenGL和DirectX):GLSL和HLSL是两种最常用的着色语言。

对于Stage3D API,Adobe创建了两种新的着色语言来为GPU创建程序:AGAL和Pixel Bender 3D。

AGAL和Pixel Bender 3D概述

AGAL(Adobe Graphics Assembly Language)是一种汇编语言。它是一种非常低级的语言,非常接近于GPU实际执行的指令。GPU(以及CPU)无法直接理解ActionScript等高级语言,这些语言中包含变量、类等。GPU只能理解基础的机器语言命令。管道中的某个位置有一种编译器,它将高级语言的复杂命令翻译为一系列更简单、更低级的机器语言命令。

使用AGAL,您可以直接在较低级别上编写命令,类似于GPU所理解的命令。

Pixel Bender 3D是一种更高级的语言,所以它比AGAL更容易使用。Pixel Bender 3D是Pixel Bender的一种扩展,但它没有更新来支持3D和着色器。

关于如何选择Pixel Bender 3D和AGAL,二者各有利弊。Pixel Bender 3D肯定是更容易使用的语言,所以它编写复杂的着色器所需的时间要少得多。

另一方面,AGAL更接近GPU的工作方式。因此,您会更好地理解您的呈现管道中到底发生了什么。您可以手动优化着色器,而不让编译器来为您这么做。所以,如果您花时间学习了它,可能能够使用AGAL创建进一步优化的着色器。

如果您的目标是学习Stage3D的工作原理,AGAL也是一个不错的选择。AGAL使您能够在更接近GPU的地方运行命令,所以更容易理解呈现管道中到底发生了什么。

一定要注意,在使用Pixel Bender 3D时,您会在编译时预编译您的着色器,当使用AGAL时,您的着色器显示为着色器程序字符串的形式,它们在运行时汇编为对象代码。所以,可以使用AGAL动态地创建着色器。

出于指导使用Stage3D的目的,我相信最佳的选择是首先学习AGAL。

了解AGAL的语法

AGAL是一种汇编语言。如果您熟悉ActionScript中的代码语法,一种类似AGAL的汇编语言乍看起来会比较陌生。

以下是AGAL顶点着色器的一个示例:

m44 op, va0, vc0 
mov v0, va1 

我将介绍上面这个示例的语法,以便您可以理解每行汇编代码的含义。

着色器的每一行是一个命令,由一个称为操作码的3字符字符串指定。

一行AGAL代码的语法如下:

<opcode> <destination>, <source 1>, <source 2 or sampler>

此语法非常重要。请记住此语法,AGAL很快就会变成看起来难念的语言。

在操作码之后,依据命令,可能是一个目标,以及一个或两个来源。目标和来源称为寄存器:GPU中供着色器使用的小型内存区域。本章后面将更详细地介绍寄存器。来源包含操作中使用的值,目标是存储结恶果的地方。

识别AGAL中主要的操作码

AGAL包含大约30个不同的操作码。可用操作码的完整列表可在Program 3D参考文档中找到。以下一些最常用的操作码。

  • mov:将数据从来源1移动到目标,按成分运算
  • add:目标 = 来源1 + 来源2,按成分运算
  • sub:目标 = 来源1 – 来源2,按成分运算
  • mul:目标 = 来源1 * 来源2,按成分运算
  • div:目标 = 来源1 / 来源2,按成分运算
  • dp3:来源1和来源2之间的点乘(3个成分)
  • dp4:来源1和来源2之间的点乘(4个成分)
  • m44:来源1中的4个成本矢量和来源2中的4×4矩阵之间的倍乘
  • tex:纹理采样。从坐标“来源1”处的“来源2”处的纹理加载。

图1和图2概述了完整的AGAL命令集。

图1. 与操作码相关的AGAL内存、算法、三角法和代数。(+) 查看大图

图2. 与操作码相关的AGAL矢量和矩阵、条件和纹理采样。(+) 查看大图

使用AGAL寄存器

AGAL不使用变量来存储数据,像ActionScript和其他高级语言所做的一样。AGAL仅使用寄存器。

寄存器是GPU中的小型内存区域,AGAL程序(着色器)可在执行期间使用它们。寄存器用于存储AGAL命令的来源和目标。

您也可以通过这些寄存器将参数传递到您的着色器。

每个寄存器为128位宽,这意味着它包含4个浮点值。每个值称为寄存器的一个成分。

寄存器组件可通过坐标存取器(xyzw)和通过颜色存取器(rgba)存取。

寄存器的第一个组件可这样存取:

<register name>.x 

也可以这样存取:

<register name>.r 

有时寄存器包含类似坐标的数据,而其他时候它们包含颜色数据。通过使用正确类型的存取器,您可以是代码更简洁和容易阅读。

上面的一些操作码(比如add)按组件执行它们的运算。这意味着加运算逐个组件地执行,所以x组件与x组件相加,y组件与y组件相加,依此类推。

有6种类型的寄存器。

1. 属性寄存器

这些寄存器引用作为顶点着色器输入的VertexBuffer的顶点属性数据。因此,它们仅可用于顶点着色器。

这是顶点着色器负责处理的主要数据流。VertexBuffer中的每个顶点属性拥有自己的属性寄存器。

要将一个VertexBuffer属性分配给特定的属性寄存器,可以使用函数Context3D:setVertexBufferAt()和合适的索引。

然后从着色器,使用语法va<n>访问属性寄存器,其中<n>是属性寄存器的索引编号。

总共有8个属性寄存器可用于顶点着色器。

2. 常量寄存器

这些寄存器用于处理从ActionScript传递到着色器的参数。这由Context3D::setProgramConstants()系列函数执行。

这些寄存器使用以下语法从着色器访问:对于顶点着色器,使用vc<n>;对于像素着色器,使用fc<n>,其中<n>是常量寄存器的索引编号。

有128个常量寄存器可用于顶点着色器,28个常量寄存器可用于像素寄存器。

3. 临时寄存器

这些寄存器可用于着色器,它们用于临时计算。因为AGAL不使用变量,您将使用临时寄存器来存储整个代码中的数据。

临时寄存器使用语法vt<n>(顶点着色器)和ft<n>(像素着色器)来访问,其中<n>是寄存器编号。

有8个临时寄存器可用于顶点着色器,8个可用于像素寄存器。

4. 输出寄存器

暑促寄存器由顶点和像素寄存器用于存储它们的计算的输出。对于顶点着色器,此输出是顶点的位置。对于像素着色器,它是像素的颜色。

这些寄存器可使用以下语法访问:op用于顶点着色器,oc用于像素着色器。

显然只有一个输出寄存器可用于顶点和像素寄存器。

5. 可变寄存器

这些寄存器用于将数据从顶点着色器传递到像素着色器。传递的数据由GPU恰当地插入,使像素着色器能够收到被处理的像素的正确值。

以这种方式传入的典型数据是顶点颜色或纹理的UV坐标。

这些寄存器可使用语法v<n>访问,其中<n>是寄存器编号。

有8个可变寄存器可用。

6. 纹理采样器寄存器

纹理采样器寄存器用于基于UI坐标,从纹理挑选颜色值。

要使用的纹理通过ActionScript调用Context3D::setTextureAt()来指定。

使用纹理采样器的语法为:fs<n> <flags>,其中<n>是采样器索引,<flags>是一个或多个指定应该如何采样的标志。

<flags>是一个逗号分隔的字符串集,定义:

  • 纹理维度。选项:2d、cube
  • mip 映射。选项:nomip(或 mipnone,它们是相同的)、mipnearest、miplinear
  • 纹理过滤。选项:nearest、linear
  • 纹理重复。选项:repeat、wrap、clamp

例如,一个没有MIP映射和现象过滤的标准2D纹理可以使用以下代码采样到临时寄存器ft1中:

tex ft1, v0, fs0 <2d,linear,nomip> 

在上面的示例中,可变寄存器v0持有插入的纹理UV。

创建示例AGAL着色器

在本节中,您将看到一个着色器示例,更好地理解它的操作原理。

假设顶点缓冲区中的顶点包含顶点位置(位于偏移0)和顶点颜色(位于偏移3)。该代码类似于:

var vertices:Vector.<Number> = Vector.<Number>([ 
-0.3,-0.3,0, 1, 0, 0, // x, y, z, r, g, b 
-0.3, 0.3, 0, 0, 1, 0, 
0.3, 0.3, 0, 0, 0, 1]); 

这段代码的目标是使顶点着色器正确地转换顶点位置,将每个顶点颜色传递到像素着色器。

您可以通过以下代码实现此目的:

m44 op, va0, vc0 // pos to clipspace 
mov v0, va1 // copy color 

第一行在顶点输入属性寄存器0(va0)和一个从ActionScript传入的变换矩阵之间执行一个4×4矩阵乘。当在透视图中呈现时,这通常是从模型空间到剪贴空间的变换矩阵,我们假设它从ActionScript传递到常量寄存器0(vc0)。

注意:剪贴空间和透视投影将在本系列的下一个教程中更详细探讨,该教程名为使用Stage3D和透视投影

矩阵可通过以下调用传入到着色器中的寄存器vc0中:

Context3D::setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, matrix, true ); 

顶点着色器的第二行将顶点颜色数据复制到可变寄存器0(v0),以便它可以由GPU插入,传递到像素寄存器。

像素着色器将可变寄存器v0颜色内容复制到它的输出寄存器oc:

mov oc, v0 

所以,这个顶点/像素着色器对使用从ActionScript传入的一个变换矩阵转换3D模型顶点,并应用顶点颜色。

就这么简单!您的第一个顶点和像素着色器。

使用Program3D和AGAL Mini Assembler构建示例ActionScript应用程序

那么,如何实际地将这些AGAL代码放在有效的ActionScript应用程序中?这正是Stage3D API发挥作用的地方。

var program:Program3D = context3D.createProgram(); 

在能够使用Program3D(一个着色器)进行呈现之前,您首先需要将它上传到GPU。为此,可以调用方法

Program3D::upload(vertexByteCode: ByteArray, fragmentByteCode:ByteArray); 

此方法调用需要以输入的形式获得顶点和片段着色器的已编译的对象代码版本,以便将它上传到GPU。

一种将AGAL着色器编译为对象代码的不错方式是使用AGAL Mini Assembler:一个使用工具,以字符串形式接收顶点和像素着色器源代码作为输入,在运行时将它们编译为对象代码。

您可以在此处下载AGAL Mini Assembler。

所以,首先您将使用AGAL Mini Assembler编译上面讨论的顶点和像素着色器。

var vertexShaderAssembler: AGALMiniAssembler = new AGALMiniAssembler();
vertexShaderAssembler.assemble(Context3DProgramType.VERTEX, "m44 op, va0, vc0\n" + // pos to clipspace 
"mov v0, va1" // copy color 
);
var fragmentShaderAssembler: AGALMiniAssembler = new AGALMiniAssembler();
fragmentShaderAssembler.assemble(Context3DProgramType.FRAGMENT, "mov oc, v0");

然后将顶点和像素着色器程序都上传到GPU:

var program:Program3D = context3D.createProgram(); 
program.upload( vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode); 

使用ActionScript与AGAL通信

着色器无法自行运行。它由您基于Stage3D的主要的ActionScript应用程序使用。因此,它需要应用程序向它发送需要处理的数据。

一般而言,着色器将需要VertexBuffer数据(顶点属性)、纹理和着色器可能需要从ActionSript获得的其他参数,比如一个变换矩阵。

在呈现时,在使用Program3D以及相关的VertexBuffer和纹理之前,您需要使用以下调用启用它们:

  • Contex3D::setProgram(program:Program3D)
  • Context3D::setVertexBufferAt(index:int, buffer:VertexBuffer3D, bufferOffset:int, format:String)
  • Context3D::setTextureAt(sampler:int, texture:TextureBase)

请注意,setVertexBufferAt在顶点缓冲区中某个偏移位置启用一个特定的顶点属性(参数3),将它与索引(第一个参数)所指定的一个属性寄存器(流)相关联。

setTextureAt调用启用一个Texture,并将它与第一个参数“sampler”所指定的某个纹理采样器相关联。

以下是在呈现之前使用这些调用的语法:

// vertex position to attribute register 0 
context3D.setVertexBufferAt (0, vertexbuffer, 0, Context3DVertexBufferFormat.FLOAT_3); 
// assign texture to texture sampler 0 
context3D.setTextureAt( 0, texture ); 
// assign shader program 
context3D.setProgram( program ); 

在启用任何这些属性之前,您需要确保调用相应的上传方法将它们上传到了GPU。

  • Program3D::upload()
  • VertexBuffer3D::uploadFromVector(data:Vector.<Number>, startVertex:int, numVertices:int)
  • Texture::uploadFromBitmapData(source:BitmapData, miplevel:uint = 0)

然后,您需要能够以常量的形式向您的着色器传入参数,将它们存储在常量寄存器中。为此,执行以下调用:

  • Context3D::setProgramConstantsFromVector(programType:String, firstRegister:int, data:Vector.<Number>, numRegisters:int = -1)
  • Context3D::setProgramConstantsFromMatrix(programType:String, firstRegister:int, matrix:Matrix3D, transposedMatrix:Boolean = false)

以便分别编写一个ActionScript Vector或Matrix3D。

所以,如果您需要将一个基本的旋转矩阵传入您的着色器,您需要运行以下代码:

var m:Matrix3D = new Matrix3D(); 
m.appendRotation(getTimer()/50, Vector3D.Z_AXIS); 
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, m, true); 

该矩阵存储在常量寄存器0中,供顶点着色器使用。

延伸阅读

本文向您介绍了着色器的概念和AGAL着色语言。如果您希望构建基于Stage3D API的ActionScript 3D应用程序,必须创建一个着色器。尽管我们还没有构建完全有用的Stage3D应用程序,但本文内容为使用Stage3D API的能力奠定了基础。在本系列的下一篇文章中,我会将所有知识点衔接起来,向您展示如何构建一个使用Stage3D呈现简单几何体的应用程序。

查看原文:What is AGAL

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我
社区评论

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT