Stage3D 翻译系列之五:何为AGAL(Adobe 图形汇编语言)

何为AGAL

 

 

必备知识

要求对Stage3D API基本熟悉。理解Shaders使用原理及可编程功能管道如何运作。如果你还没有阅读过本系列教程的前两章(1、Stage3D如何工作 2、顶点和片段着色器),请先行阅读。
用户级别

中级
在本章中,你 将对着色器语言(shading languages)有一个基本的了解。本章会涉及着色器语言:AGAL(Adobe Graphics Assembly Language)的基本使用知识,AGAL语言已经包含在了Stage3D的API中。你将学到什么是AGAL,它如何运作以及在基于Stage3D的 ActionScript应用中如何使用。

理解着色器语言

在进入AGAL的大门之前,让我们首先来了解一下什么是着色器语言以及怎样用它来创建着色器。

 

着色器不是用 ActionScript编写的,也不是用C++或者其它任何通用语言来编写的。着色器一般用一种专门的语言:着色器语言来编写。着色器只运行于GPU 中,所以编写着色器最有效的方法就是用一种专门为GPU设计的语言,这就是为什么你要用着色器语言而不是其它通用语言来编写着色器的原因。在标准原生3D 平台(OpenGL和DirectX)中,有几种着色器语言已经使用了很长一段时间,其中GLS和HLSL是最常用的两种。在Stage3D API中,Adobe发明了两种新的着色器语言:AGSL和Pixel Bender 3D,这两种语言都可以为GPU编写程序。
关于AGAL和PixelBender 3D的概述

AGAL(Adobe Graphics Assembly Language)是一种汇编语言。这是一种非常低级的语言,能够直接控制GPU。GPU或CPU并不能识别ActionScript等高级语言中的变 量、类等等,它们只能识别基本的机器语言指令。在管道中的某一个地方有一个编译器,用来将高级语言中的复杂命令翻译成系列更为简单,低级的机器语言指令。 你可以用AGAL直接编写低级指令,这些指令与GPU能识别的指令很接近。

 

Pixel Bender 3D是一种比AGAL更高级的语言,所以比AGAL要易用一些。Pixel Bender 3D是Pixel Bender的扩展,加入了3D及着色器的内容。那我们编写Stage3D中的着色器到底用以上那种语言呢,好,让我们来权衡一下它们的利弊。Pixel Bender 3D肯定会更易于使用,编写复杂着色器所用的时间会更少一些。另一方面来说,AGAL更接近GPU,因此,用它你会对渲染管道的工作原理有更加深入的理 解。你可以亲自优化的着色器,而不是让编译器来帮你。所以,如果肯多花点儿时间学习的话,AGAL会让你的着色器更加高效。

 

如果你的目标是学习Stage3D的原理,AGAL则是一个不错的选择。AGAL能够直接操作GPU指令,所以对我们理解渲染管道中究竟发生了什么会更简单。

 

需要着重说明的是,当使用Pixel Bender 3D时,着色器在编译阶段会被预先编译处理,但是对于AGAL来说,着色器直接由着色器程序字符串组成,在运行时被组装成目标代码,所以,AGAL可以创建出真正动态的着色器。

 

出于教学目的来使用Stage3D的话,我相信最好的选择是从学习AGAL开始。
AGAL的语法

AGAL是一种汇编语言。如果你之前熟悉用ActionScript来编写程序,那么当你第一次见到诸如AGAL的汇编语言时,你会觉得很陌生。

 

下边是关于AGAL顶点着色器的一个例子:

 

  1. m44 op, va0, vc0
  2. mov v0, va1

复制代码

我将解释以上例子的语法,这样你就可以理解以上汇编代码是什么意思了。

 

着色器程序的每一行都是一个指令,指令由一个3个字符组成的叫做opcode的东东指定。

 

一行AGAl代码的语法规则如下:

  1. ,,

复制代码

 

以上规则很关键,把它深深的记在你的大脑中,你就会发现AGAL看起来并不像一堆不可读的乱码了。

 

根据指令的不 同,opcode后跟的可能为一个目的(destination),再加上一两个源(sources)。以上所说的目的和源即为寄存器 (registers):即GPU中供着色器专用的小块内存区。在本教程的后边我会更加详细的介绍寄存器。源寄存器中存放着操作所需的变量,而目的寄存器 中则存放运算结果。
认识一下AGAL中主要的opcode,AGAL中有大约30个不同的opcode。完整的介绍列表可以在3D编程参考文档中查看。以下是AGAL中最常用的一些opcode:

  1. mov: moves data from source1 to destination, component-wise(移动 分量形式)
  2. add: destination = source1 + source2, component-wise(加法 分量形式)
  3. sub: destination = source1 – source2, component-wise(减法 分量形式)
  4. mul: destination = source1 * source2, component-wise)(乘法 分量形式)
  5. div: destination = source1 / source2, component-wise(除法 分量形式)
  6. dp3: dot product (3 components) between source1 and source2(点积运算(三分量))
  7. dp4: dot product (4 components) between source1 and source2(点积运算(四分量))
  8. m44: multiplication between 4 components vector in source1 and 4×4 matrix in source2(4×4矩阵乘)
  9. tex: texture sample. Load from texture at source2 at coordinates source1.(纹理采样)

复制代码

图1和图2为AGAL指令集概况

 

图1:AGAL中与内存、算术、三角形和袋鼠相关的opcode

图2:AGAL中与向量、矩阵、条件和纹理采样相关的opcode
使用AGAL寄存器

AGAL不像ActionScript和其它高级语言那样用变量来存储数据,而只使用寄存器。寄存器是GPU中的小块内存区域,供AGAL程序(着色器)运行时使用。寄存器用来存储AGAL运算过程中的数据及结果。你也可以通过寄存器向着色器传递参数。

 

每个寄存器为128位,这意味着其可以存放4个浮点数,每一个浮点数被称为寄存器的一个分量(component)。

 

这些寄存器中的分量既可以通过坐标访问器(xyzw)也可以通过颜色访问器(RGBA)进行访问。

 

寄存器中第一个分量,既可以通过坐标访问器访问:

  1. .x

复制代码

 

也可以通过颜色访问器访问:

  1. .r

复制代码

 

有些时候寄存器中存放的是坐标数据,有些时候存放颜色数据。通过使用正确的访问器,可以使你的代码清晰易读。

 

以上opcode中的有些指令比如:add,执行的是分量形式(component wise)的操作,意思是说,按分量相加,即:x分量加x分量,y分量加y分量等等。

 

在AGAL中有6种可用的寄存器。

 

1、属性寄存器

 

这些寄存器存放 VertextBuffer中定义的顶点属性数据,即顶点着色器的输入,因此,它们只能在顶点着色器中使用。

 

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

 

用函数:Context3d:setVertexBufferAt(),将一个VertexBuffer属性以合适的索引,赋值给特定的属性寄存器。在着色器中可以通过语法:va的形式来访问这个属性,为寄存器的索引编号。

 

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

 

2、常量寄存器

 

这些寄存器的 作用为从ActionScript像着色器传递参数,由Context3D:setProgramConstants()等一系列函数执行。在顶点着色器 和片段着色器程序中,分别以vc和fc的形式访问这些寄存器,同样为寄存器的索引编号。

 

顶点着色器中可用的常量寄存器为128个,片段着色器中为28个。

 

3、临时寄存器

 

这些寄存器在着色器中用来做临时计算。既然AGAL没有变量,所以你需要临时寄存器来保存计算过程中的中间数据。

 

临时寄存器以vt(在顶点着色器中)和ft(在片段着色器中)的语法形式访问,代表寄存器编号。

 

顶点着色器和片段着色器各有8个临时寄存器可用。

 

4、输出寄存器

 

输出寄存器用来存放顶点和片段着色器的运算结果,即顶点着色器中顶点的坐标值和片段着色器中像素的颜色值。

 

在顶点着色器程序中通过语法:op的形式访问,在片段着色器中通过语法:oc的形式方式。

 

很显然,顶点和片段着色器各只有一个输出寄存器。

 

5、可变寄存器

 

这些寄存器用来从顶点着色器向片段着色器传递数据。传递过来的数据已经被GPU进行了适当的插值,以便片段着色器得到正确的像素数据。

 

这种数据的典型代表为顶点颜色或者纹理的UV坐标值。

 

这些寄存器可以通过v的形式访问,为寄存器编号。

 

共有8个可变寄存器。

 

6、纹理采样寄存器

 

纹理采样寄存器用来存放从纹理贴图中基于UV坐标采集的颜色值。

 

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

 

使用纹理采样寄存器的语法形式为:fs 代表采样寄存器编号,代表一些列指定如何进行采样的标识。是一个逗号分割的字符串,它由如下值可用:

  1. 纹理维度。包括:2d,cube.
  2. 多重材质映射,包括:nomip(或者mipnone,意思相同),mipnearest,miplinear。
  3. 纹理过滤,包括:nearest,linear。
  4. 重复纹理贴图,包括:repeat,wrap,clamp。

复制代码

例如:一个标准的不带多重材质映射和渐变过滤的2D纹理,采样到临时寄存器ft1中,由如下代码表示:

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

复制代码

 

上例中,可变寄存器v0中存放着经过插值的纹理UV坐标。
创建一个AGAL着色器实例

在本节中,我讲详细的讲解一个着色器完整的例子,这样对你理解其如何运作更有帮助。

 

如下所示,定义了一个VertexBuffer,里面包含3个顶点,位置信息由偏移量0开始,颜色信息从偏移量3开始:

  1. var vertices:Vector. = Vector.([
  2.         -0.3,-0.3,0, 1, 0, 0, // x, y, z, r, g, b
  3.         -0.3, 0.3, 0, 0, 1, 0,
  4.         0.3, 0.3, 0, 0, 0, 1]);

复制代码

 

我们的目标是让着色器适当的转换每个顶点的位置,然后将顶点颜色数据传递给片段着色器。

 

实现代码如下:

  1. m44 op, va0, vc0 // pos to clipspace(计算三角形在屏幕上位置)
  2. mov v0, va1 // copy color(拷贝颜色)

复制代码

 

代码的第一行表示将顶点属性寄存器0中(va0)的数据和从ActionScript中传来的变换矩阵(vc0)做4×4矩阵相乘,用来实现将三维物体中的顶点坐标通过转换为屏幕上的二维投影矩阵。提示:剪辑空间和透视投影会在本系列教程的下一讲,标题为: Working with Stage3D and perspective projection中详述。
通过以下代码来将矩阵常量赋值给着色器中的常量寄存器0(vc0):

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

复制代码

 

着色器代码的第二行表示将顶点颜色数据拷贝到可变寄存器0(v0)中,以便GPU对其进行颜色插值,再传递给片段着色器。

 

片段着色器只是简单的将vo中的颜色内容拷贝到输出寄存器oc中:

  1. mov oc, v0

复制代码

 

你看,顶点/片段着色器只是将从ActionScript传递来的3D模型顶点数据进行矩阵变换,然后应用顶点颜色信息。

 

ok,你的第一个顶点/片段着色器就完成了。
用Program3D和AGAL迷你编译器来构建一个ActionScript应用

如果将这些看来很费劲的汇编AGAL代码和ActionScript结合起来呢?当当当当!Stage3D API大显身手的时刻到了。

  1. var program:Program3D = context3D.createProgram();

复制代码

在使用Program3D(着色器)进行渲染之前,首先需要将其上传给GPU,调用函数如下:

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

复制代码

 

这个函数的参数为顶点、片段着色器经过编译后的目标代码,只有这样才能传给GPU。

 

用AGAL mini Assembler(编译器)将AGAL着色器编译成GPU目标代码,AGAL mini Assembler将顶点和片段着色器源代码作为输入,在运行时将其编译成目标代码。你可以从这里下载Adobe官方的AGAL Mini Assembler。

 

接下来,你要做的第一件事就是用AGAL mini Assembler来编译顶点和片段着色器源代码:

  1. var vertexShaderAssembler : AGALMiniAssembler = new AGALMiniAssembler();
  2. vertexShaderAssembler.assemble( Context3DProgramType.VERTEX,
  3. “m44 op, va0, vc0\n” + // pos to clipspace
  4. “mov v0, va1″ // copy color
  5. );
  6. var fragmentShaderAssembler : AGALMiniAssembler= new AGALMiniAssembler();
  7. fragmentShaderAssembler.assemble( Context3DProgramType.FRAGMENT,
  8. “mov oc, v0″
  9. );

复制代码

 

然后将两个着色器程序上传至GPU:

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

复制代码

AGAL和ActionScript之间的通信

着色器程序不能脱离基于Stage3D的ActionScript应用而独立存在,因此需要有程序将其要处理的数据传递给着色器。

 

一般来说,着色器需要ActionScript提供:VertexBuffer数据(顶点属性),纹理数据和一些额外的诸如变换矩阵参数数据等。

 

在使用一个Program3D之前,你必须用下面的函数将相关的VertexBuffers和纹理数据准备好:

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

复制代码

 

注意setVertextBufferAt函数将顶点缓冲数据中的顶点数据(第二个参数)用一个偏移量(第三个参数)赋值给给第一个参数指定的属性寄存器中。

 

调用setTextureAt函数将第一个参数指定的采样器(sampler)和一个纹理(Texture)联系起来。

 

在渲染之前,如以下代码所示调用这些函数:

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

复制代码

 

在调用以上代码之前,请确保其已经通过一下相应的方法上传给了GPU:

  1. Program3D::upload()
  2. VertexBuffer3D::uploadFromVector(data:Vector., startVertex:int, numVertices:int)
  3. Texture::uploadFromBitmapData(source:BitmapData, miplevel:uint = 0)

复制代码

 

调用以下函数将常量寄存器(vc)中的数据作为参数传递给着色器:

  1. Context3D::setProgramConstantsFromVector(programType:String, firstRegister:int, data:Vector., numRegisters:int = -1)(传递向量给vc)
  2. Context3D::setProgramConstantsFromMatrix(programType:String, firstRegister:int, matrix:Matrix3D, transposedMatrix:Boolean = false)(传递3D矩阵给vc)

复制代码

 

如果你想给你着色器加入一个旋转着的矩阵的话,可以用以下代码:

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

复制代码

 

这个矩阵会被存入常量寄存器0中,供顶点着色器使用。
接下来看哪些内容

本文介绍了着 色器的概念和AGAL着色语言。想要构建一个基于Stage3D API 的ActionScript3D应用,必须要自己写一个着色器。当然,本文还没有构建一个完全可用的Stage3D应用,只是介绍了一些用Stage3D API开发的一些基本手段和内容。在本系列教程的下一文中,我会将之前所讲的内容都利用起来,构建一个完整的应用,演示如何用Stage3D来渲染一个简 单的几何图形。
作者:MarcoScabia

原文链接:http://www.adobe.com/devnet/flashplayer/articles/what-is-agal.html

本文固定链接: http://www.gisthink.com/blog/guoguogis/?p=335 | GUOGUOGIS


你可能感兴趣的:(as3,web,game,3d引擎)