- 最后登录
- 2018-12-19
- 注册时间
- 2012-8-20
- 阅读权限
- 90
- 积分
- 54706
- 纳金币
- 32328
- 精华
- 41
|
前面一篇如果大家跟着做也能够作出同样的效果,但是对于AGAL部分我们没有进行讲解,因为这部分内容连接着另外一个世界。第三篇我来带着大家把这个世界的大门打开。 相信大家开始接触AGAL的时候都会感觉很蛋疼,确实,作者开始接触的时候也感觉蛋疼。不得不说AGAL的资料网上并不多,adobe player开发者中心的资料也不是非常多。大家都觉得百度googleBing了变天还是那几篇文章,没有太多的资料。别纠结,笔者也找不到。但是当你发现了AGAL的奥秘之后,你就会觉得这东西其实非常简单。单单从语法上来讲会非常的容易。 AGAL的命令或者说opcode并不多,而且大部分opcode都是进行数学运算的,所以理解起来并不难,举个简单的例子: 1mov vt0, va0 这个命令其实就是一个赋值语句,我们可以把它理解为 1vt0 = va0; 至于vt0,va0是什么东西待会说。上面AGAL中的mov就是一个命令(opcode),这样的命令一个有32个,至于都是那些,都可以干什么下面有个表格,大家看完就可以理解其中的意思了。
名称opcode 操作 说明 mov 0×00移动 将数据从 source1 移动到 destination,按组件 add0×01 相加 destination = source1 + source2,按组件 sub0×02 相减 destination = source1 – source2,按组件 mul 0×03相乘 destination = source1 * source2,按组件div 0×04 div 0×04 除以 destination = source1 / source2,按组件 rcp 0×05 倒数 destination = 1/source1,按组件 min 0×06 最小值 destination = minimum(source1,source2),按组件 max 0×07 最大值 destination = maximum(source1,source2)destination = maximum(source1,source2),按组件 frc 0×08 分数 destination = source1 – (float)floor(source1)destination = source1 – (float)floor(source1),按组件 sqt 0×09 平方根 destination = sqrt(source1),按组件 rsq 0x0a 平方根倒数 destination = 1/sqrt(source1),按组件 pow 0x0b 幂 destination = pow(source1,source2),按组件 log 0x0c 对数 destination = log_2(source1),按组件 exp 0x0d 指数 destination = 2^source1,按组件 nrm 0x0e 标准化 destination = normalize(source1),按组件(仅生成一个 3 组件结果,目标必须遮罩为 .xyz 或更小) sin 0x0f 正弦 destination = sin(source1),按组件 cos 0×10 余弦 destination = cos(source1),按组件 crs 0×11 叉积 destination.x = source1.y * source2.z – source1.z * source2.ydestination.y = source1.z * source2.x – source1.x * source2.zdestination.z = source1.x * source2.y – source1.y * source2.x(仅生成一个 3 组件结果,目标必须遮罩为 .xyz 或更小) dp3 0×12 点积 destination = source1.x*source2.x + source1.y*source2.y +source1.z*source2.z dp4 0×13 点积 destination = source1.x*source2.x + source1.y*source2.y +source1.z*source2.z + source1.w*source2.w abs 0×14 取绝对值 destination = abs(source1),按组件 neg 0×15 求反 destination = -source1,按组件 sat 0×16 饱和 destination = maximum(minimum(source1,1),0),按组件 m33 0×17 矩阵连乘3×3 destination.x = (source1.x * source2[0].x) + (source1.y * source2[0].y)+ (source1.z * source2[0].z)destination.y = (source1.x * source2[1].x) + (source1.y * source2[1].y)+ (source1.z * source2[1].z)destination.z = (source1.x * source2[2].x) + (source1.y * source2[2].y)+ (source1.z * source2[2].z)(仅生成一个 3 组件结果,目标必须遮罩为 .xyz 或更小) m44 0×18 矩阵连乘4×4 destination.x = (source1.x * source2[0].x) + (source1.y * source2[0].y)+ (source1.z * source2[0].z) + (source1.w * source2[0].w)destination.y = (source1.x * source2[1].x) + (source1.y * source2[1].y)+ (source1.z * source2[1].z) + (source1.w * source2[1].w)destination.z = (source1.x * source2[2].x) + (source1.y * source2[2].y)+ (source1.z * source2[2].z) + (source1.w * source2[2].w)destination.w = (source1.x * source2[3].x) + (source1.y *source2[3].y) + (source1.z * source2[3].z) + (source1.w *source2[3].w) m34 0×19 矩阵连乘3×4 destination.x = (source1.x * source2[0].x) + (source1.y * source2[0].y)+ (source1.z * source2[0].z) + (source1.w * source2[0].w)destination.y = (source1.x * source2[1].x) + (source1.y * source2[1].y)+ (source1.z * source2[1].z) + (source1.w * source2[1].w)destination.z = (source1.x * source2[2].x) + (source1.y * source2[2].y)+ (source1.z * source2[2].z) + (source1.w * source2[2].w)(仅生成一个 3 组件结果,目标必须遮罩为 .xyz 或更小) kil 0×27 丢弃 如果单个标量源组件小于零,则将丢弃片段并不会将其绘制到帧缓冲区。(目标寄存器必须全部设置为 0) tex 0×28 纹理取样 destination 等于从坐标 source1 上的纹理 source2 进行加载。在这种情况下, source2 必须采用取样器格式 sge 0×29 大于等于时设置 destination = source1 >= source2 ? 1 : 0,按组件 slt 0x2a 小于时设置 destination = source1 < source2 ? 1 : 0,按组件 seq 0x2c 相等时设置 destination = source1 == source2 ? 1 : 0,按组件 sne 0x2d 不相等时设置destination = source1 != source2 ? 1 : 0,按组件opcode表 看完了上述的表格之后,我们对照着来说明一下如何使用这些opcode。首先你要知道AGAL的格式。很多教程将这部分定位为语法,笔者认为这是不太准确的,因为AGAL本书是一段二进制的shader,而我们目前能拿到的是AGAL的二进制格式,也就是说,你既然用后了数据格式,那么至于数据如何生成,以什么形式生成都无所谓了。甚至你可以通过自己编写一套引擎来对你自己的编码习惯进行翻译。不过大部分工作我们目前都不考虑,毕竟adobe已经为我们提供了一个工具类,这个类可以很方便直观的帮助我们编写AGAL代码。我们先从格式开始说起,关于二进制的其他数据内容我们先不做过多的讨论,因为在后面的章节中,我将带领大家编写一个逆向工程工具,用于将二进制的AGAL还原为可以直接读取的源代码。 [opcode][destination][source1][source2 or sampler] 上面这行内容是AGAL最核心的内容,也是你将直接使用的内容,其中opcode为上面表格中的32个命令,destination则是目标内容,source1,source2则数数据源。大家可以自行根据你需要的功能编写一个AGAL尝试一下。至于实际使用,后面会根据不同内容的算法来向大家实际展示AGAL的最后编写结果。 下面我要说明的则是destination和source1,source2中的内容。这三部分的值到底从何二来,有如何使用呢? 首先我们要理解寄存器,这部分和我们的AS3程序息息相关,环环相扣。在我们的AGAL中寄存器的访问也非常简单,你无需访问创建或者销毁之类的操作。而相关的寄存器也非常的简单,让我们以分类的形式来记忆。我们将寄存器按照功能分为两大类,对于顶点的操作我们有op,vc,vt,va,对于像素的操作我们有oc,ft,fs,fc。另外还有一个比较特殊的v。很多教程中对于每个寄存器都有一个非常让人琢磨不透的名字,这些名字看上去晦涩难懂,但当你了解他的功能之后你会发现它的名字非常的直观。让我们来一一看一下。va 属性寄存器很多人看到这个名字的时候都会认为va中应该存的是某一个对象的属性,或者一些其他什么东西。但实事并非如此。当你上载一些顶点数据之后,我们通过AS3来制定这些顶点所存在的位置,那么你就可以通过va来直接使用。举个简单的例子,在上一篇文章中,我们使用了va0,而va0的数据又是从etVertexBufferAt来直接指定的,所以在你编写代码的时候,你的AGAL和AS3有着直接的联系。vc 常量寄存器顾名思义,与程序中的常量概念类似,这部分数据是从外部直接传递进来的,至于你如何使用完全取决于你自己。关于用法我们在后面会涉及到。vt 临时寄存器应该说vt的概念最接近于我们程序中的变量,因为他可以被你临时读取,并且允许你在AGAL运行时来动态改变其中的数值。op 输出寄存器好吧,当你吧所有的操作都完成后,那么你的结果应该有一个归宿,这个op就是你顶点数据最后的归宿。每次运行顶点数据后,你都应该看到op,否则你的程序运行后没有任何效果。 fs 纹理采样寄存器教程到目前位置,我们还没有看到任何和贴图有关系的内容。不错,笔者打算将这部分内容放在后面讲解。因为还有不少好东西放在后面等着大家呢。这里简单解释一下,所谓纹理采样,则是调用你显存中的纹理数据。纹理我们可以理解为贴图,或者理解为你上载到GPU的那张图片。fc 常量寄存器又遇到常量了。这里和刚才的vc其实是一样的,因为顶点和纹理这两部分的程序不可交叉访问,所以贴图也有自己的常量寄存器。ft 临时寄存器和vt概念相同,但只用于纹理操作。oc 输出寄存器和op相同,但只能用于纹理操作。 v 插值寄存器这是比较特殊的一个寄存器,因为刚才我们所说的8个寄存器类型只能在自己负责的领域操作,顶点的寄存器不可访问纹理的内容,同样,纹理操作中也无法访问顶点数据,那么如果他们的数据有交集如何呢?这里就体现了v的作用(当然,在微博中,v也是一种身份的象征)。v可以在在顶点操作中使用,也可以在纹理操作中使用。 上面说了这么多,相信大家对AGAL中的语法和opcode有了一个大致的了解。下面我们就来解释一下上一篇中的AGAL代码。 首先是顶点处理。 123varvagalcode:String= "mov op, va0 " + "mov v0, va1";这一步处理中,我们将va0直接赋值给op,也就是说va0本身就是我们的顶点数据。将顶点数据原封不懂的输出到op中以便得到结果。第二句中我们将va1中的数据移动到v0中,这里va1的数据是我们当前顶点的RGB颜色值。移动到v0是为了让我们的纹理着色器访问并使用。 下面我们来解释一下纹理着色器。AGAL代码如下: 1varfagalcode:String= "mov oc, v0";仅此一句,因为刚才在顶点着色器中我们已经把RGB数值传递给了v0,这里我们将RGB颜色值原封不动的输出到oc中,以便GPU来进行渲染。之后我们就可以得到一个正确的图像结果。 |
|