守望者--AIR技术交流

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

搜索
热搜: ANE FlasCC 炼金术
查看: 1879|回复: 2

[技术资料] AGAL介绍系列

[复制链接]
  • TA的每日心情
    擦汗
    2017-10-18 10:18
  • 签到天数: 3 天

    [LV.2]偶尔看看I

    2

    主题

    4

    帖子

    461

    积分

    管理员

    Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20

    威望
    0
    贡献
    0
    金币
    308
    钢镚
    2852
    发表于 2015-1-21 22:26:15 | 显示全部楼层 |阅读模式
    AGAL介绍
        Flash 11Stage3D类为Flash引入了一套全新的字节码:AGAL(Adobe Graphics Assembly Language)。今天我第一次开始写一系列的文章来讨论什么是AGAL,怎样生产这些字节码,以及这些着色器是怎么工作的。请继续阅读这一系列文章中的第一张来学习AGAL的基础。

        首先我们要来理解一些术语。AGAL表示两层意思:
        第一个,Stage3D着色器字节码格式的名称,就像我们常常谈论的游AS3C++(炼金术)或者Haxe编译而来的AVM2字节码。
        第二个意思就是编译成AGAL字节码的汇编语言。我将用“AGAL字节码”和“AGAL汇编”来对这两个意思加以区别。
    AGAL汇编不是产生AGAL字节码的唯一途径,就现在而言,有以下几种方式可以生产它(使用16进制编辑器除外):
    语言
    语言类型
    编译器/汇编器
    API
    AGAL
    汇编
    Adobe’s AGALMiniAssembler AS3 class
    None (directly use Stage3D)
    Pixel Bender 3D
    高级语言
    Adobe’s beta compiler
    Pixel Bender 3D API
    HxSL
    高级语言
    HaXe
    HaXe Language
        在这些方式之中,AGAL汇编提供了最低级的代码,让你能够最大幅度地提升性能和使用最多的特性。它也不需要任何庞大的API库或者切换到Haxe语言。不过缺点是你不得不使用汇编语言来编写你所有的着色器,因此会键入许多非常难以阅读的代码。不过,着色器总是出现在和性能相关的代码区域,所以他不会出现在你不想优化的地方。相对于你的整个应用程序或者游戏来说,你不会写很多的着色器代码,所以你是不是能够很快键入成千上万行的着色器代码并不重要。因此,我将只介绍AGAL,他也是我现在选择的着色器语言。

        在你开始写代码之前理解一些基本的Stage3D着色器工作原理是非常重要的。首先,它们被分为两部分:第一部分被称作“顶点着色器”(Vertex Shader),他的作用是定义每个顶点的位置。第二部分被称作“片段着色器”(Fragment Shader),他的作用是为每个片段(粗略的一个像素)定义颜色。这两个部分组成了“着色器程序”(Shader Program)通过Stage3D API被上传和使用。
    1. // Create a shader program. It is initially unusable.
    2. var shaderProgram:Program3D = myContext3D.createProgram();

    3. // Upload vertex shader and fragment shader AGAL bytecode. It is now usable.
    4. shaderProgram.upload(vertexShaderAGAL, fragmentShaderAGAL);

    5. // Use the shader program for subsequent draw operations
    6. myContext3D.setProgram(shaderProgram);

    7. // Draw some triangles with the shader program
    8. myContext3D.drawTriangles(someTriangleIndexBuffer);
    复制代码
       为了设置顶点位置和片段颜色,我们需要能够给着色器程序传递数据。这些数据包括摄像机矩阵的变换,材质,颜色,向量等等。下图描述了数据是怎样从你的AS3程序传递到顶点着色器,到片段着色器最终到你要绘制的目标(屏幕或者材质)上的。



    一些数据类型需要解释:
    数据类型
    绑定目标
    例子
    Vertex Constants
    Vertex Shader
    Transformation matrices, bones (for skeletal animation)
    Vertex Attributes
    Vertices
    Positions, normals, colors
    Varying Values
    Vertices
    Texture coordinates, colors
    Textures
    Fragment Shaders
    Art-driven colors, normal maps
    Fragment Constants
    Fragment Shaders
    Fog colors, object-global alpha values

        最棘手的是这些类型是“变量”,他们的值是顶点着色器根据每个顶点算出来的。当三角形被绘制完成的时候,这些变化着的值才被插入。例如如果你的顶点着色器在一个顶点上输出红色,一个顶点上输出绿色,第三个输出蓝色,那么你的片段着色器会输出变化的值,你会得到这样一个三角形:


    在以后的文章中,你会看到这一点在很多3D绘制操作上非常高效。例如:一个简单的带有材质的三角形能够查找这个三角形上的每个片段的材质坐标,但是这个查找操作极度浪费性能。而在每个顶点上面查询材质坐标,然后简单地使用一个可变量插入要好得多。

        这个系列的第一篇文章就到此为止,等到下次我们会深入AGAL汇编语言,并编写一些着色器。




    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?立即注册

    x
    守望者AIR技术交流社区(www.airmyth.com)
    回复

    使用道具 举报

  • TA的每日心情
    擦汗
    2017-10-18 10:18
  • 签到天数: 3 天

    [LV.2]偶尔看看I

    2

    主题

    4

    帖子

    461

    积分

    管理员

    Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20

    威望
    0
    贡献
    0
    金币
    308
    钢镚
    2852
     楼主| 发表于 2015-1-21 22:30:55 | 显示全部楼层
        这一部分我们将看看你的着色器可使用的几种类型,最后再深入到一些AGAL汇编程语言的语法。
    一开始,让我们来看看顶点着色器和片段着色器处理的几种不同的数据类型:

    类型


    个数


    名称


    来源于


    从哪儿访问


    目的


    Vertex Temporary


    8


    vt0–vt7


    Vertex shader code


    Vertex shader


    顶点着色器计算时的临时变量


    Fragment Temporary


    8


    ft0–ft7


    Fragment shader code


    Fragment shader


    片段着色器计算时的临时变量


    Vertex Attribute


    8


    va0–a7


    Context3D.setVertexBufferAt


    Vertex shader


    Vertex-specific data (e.g. texture coordinates, color)


    Vertex Constant


    128


    vc0–c1


    Context3D.setProgramConstants


    Vertex shader


    在调用Context3D.drawTriangles函数时顶点着色器用到的常量


    Fragment Constant


    28


    fc0–c27


    Context3D.setProgramConstants


    Fragment shader


    在调用Context3D.drawTriangles函数时片段着色器用到的常量


    Fragment Sampler


    8


    fs0– fs7


    Context3D.setTextureAt


    Fragment shader


    片段着色器用到的材质


    Varying


    8


    v0– 7


    Vertex shader code


    Vertex and Fragment shader


    在自动插值时从顶点着色器为片段着色器传值


    Output Position


    1


    op


    Vertex shader code


    Vertex shader


    顶点着色器的输出


    Output Color


    1


    oc


    Fragment shader code


    Fragment shader


    片段着色器的输出


        这些数据类型就是你绘制你的3D场景的工具(除了背景色),因此为了充分利用他们,你需要掌握每一个数据类型是非常重要的。在我们讲解AGAL汇编语法之前,首先让我们讨论一下这些数据类型到底是些什么东西:除了片段采样器(fragment samplers)以外,其它的数据类型实质上是四个浮点数的集合。这和大多数的CPU编程的基本单元不一样,它的基本单元不是一个字节,而是四个浮点数。尽管如此,你任然可以通过点“.”来读写数据的属性:

    1. vt0.x
    2. vt0.y
    3. vt0.z
    4. vt0.w
    复制代码
    这是访问属性的简单方式,你也可以一次访问两个,三个或者4个属性
    1. vt0.yx
    2. vt0.xyz
    3. vt0.xyzw
    复制代码
    你甚至还可以调整属性的位置
    1. vt0.yx
    2. vt0.zyx
    3. vt0.wzyx
    4. vt0.xzwy
    复制代码
    也可以一次重复访问同样的属性
    1. vt0.xx
    2. vt0.xxx
    3. vt0.xxxx
    4. vt0.xyyx
    5. vt0.zzzy
    复制代码
    现在让我们来认识一些基本的AGAL汇编语法:
    1. mov ft0, va0
    复制代码
    每一行着色器程序包含了一个指令,这个指令完成一个对应的操作,为了分析这个指令,我先这样分解它:
    1. Instruction  | Argument 1  | Argument 2
    2. mov          | ft0,           | va0
    复制代码
    这行代码复制了一份存储在顶点属性(va0)上的数据到一个顶点着色器的临时变量(ft0)中。同时va0种存储的数据在这条指令之后也不会发生改变。
    大多数指令会用到3个参数
    1. add vt0, vt1, vt2
    复制代码
    这行代码把vt1vt2相加之后,把结果存储到vt0中。到这里,你应该总结出了一种模式:操作的结果存储在第一个参数中(除了kil指令),其它的参数则是参与运算的变量。现在让我们看看把我们上面所讲到的联系到一起写出的代码是个什么样子:
    1. add vt0.x, vt1.x, vt2.x
    2. add vt0.xy, vt1.zw, vt2.yy
    3. add vt0.xyz, vt1.yzw, vt2.zzz
    4. add vt0, vt1.zyxw, vt2.yzwy
    复制代码
    你必须确保结果中的属性和参与运算的变量中的属性个数一致。现在让我们来看看最难以理解的指令:
    1. kil ft0.x
    复制代码
    Kil指令只能在片段着色器中使用,它只用到一个参数,这个参数必须是一个属性。如果这个属性小于0,片段着色器则立刻中止,不会有片段,像素产生或者绘制。另外两个奇怪的指令是AGAL中仅有的两个逻辑控制指令。sge(“set if greater or equal”)和slt(“set if less than”)。如果条件不满足,则返回0到结果中。
    1. Instruction  | Destination   | Check Value  | Value to set
    2. sge            | ft0,             | ft1.x,           | ft2
    3. slt              | ft0,             | ft1.x            | ft2
    复制代码
    现在我们来看看最重要也是最难以理解的指令:
    1. tex ft0, v0, fs0 <2d, linear, nomip>
    复制代码
    tex指令从给定的材质坐标上取样(绑定到片段取样器),并返回一个RGAB的颜色值。最难的地方是你必须用参数后面的尖括号中的标识(flags)指出你想怎样取样,以下是被允许的值:
    材质类型
    。 2d
    。 cube
    过滤方法
    。 Nearest
    。 linear
    MIP映射
    。 mipnone/nomip
    。 mipnearest
    。 miplinear
    纹理重复
    。 clamp
    。 wrap
        。 repeat

    守望者AIR技术交流社区(www.airmyth.com)
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    擦汗
    2017-10-18 10:18
  • 签到天数: 3 天

    [LV.2]偶尔看看I

    2

    主题

    4

    帖子

    461

    积分

    管理员

    Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20

    威望
    0
    贡献
    0
    金币
    308
    钢镚
    2852
     楼主| 发表于 2015-1-21 22:33:43 | 显示全部楼层
    今天我们来讨论创建一个基本着色器的过程。这些是所有flash 11 Stage3D引擎的基础,所以你将学习3d着色器是怎样被硬件加速的。
        创建着色器的第一步是把AGAL汇编代码编译成AGAL字节码。我们常常使用AdobeAGALMiniAssembler这个类来完成。下面就是使用AGALMiniAssembler来编译你的AGAL汇编代码。
    1. // 创建一个 AGALMiniAssembler 类的实例来编译你的AGAL汇编代码
    2. var assembler:AGALMiniAssembler = new AGALMiniAssembler();

    3. // 编译顶点着色器代码(字符串)
    4. assembler.assemble(Context3DProgramType.VERTEX, “顶点着色器代码”);

    5. // 编译时检查错误
    6. if (assembler.error)
    7. {
    8.     trace("Error assembling the vertex shader: " + assembler.error);
    9.     return;
    10. }

    11. // 如果没有错误,获取顶点着色器的字节码
    12. var vertexShaderBytecode:ByteArray = assembler.agalcode;

    13. // 编译片段着色器代码(字符串)
    14. // 你不用重新创建一个新的 AGALMiniAssembler 实例
    15. // 你可以重用其它任何一个 AGALMiniAssembler的实例
    16. assembler.assemble(Context3DProgramType.FRAGMENT, “片段着色器代码”);


    17. // 编译时检查错误
    18. if (assembler.error)
    19. {
    20.     trace("Error assembling the fragment shader: " + assembler.error);
    21.     return;
    22. }

    23. // 如果没有错误,获取片段着色器的字节码
    24. var fragmentShaderBytecode:ByteArray = assembler.agalcode;
    复制代码
    下一步你必须从Context3D这个类来获取一个着色器程序,下面的代码为你演示了怎样得到一个Context3D的实例:
    1. // 得到Stage3D 实例
    2. var stage3D:Stage3D = stage.stage3Ds[0]; // or 1 or 2 or 3

    3. // 监听Context3D 创建完成事件
    4. stage3D.addEventListener(Event.CONTEXT3D_CREATE, onContext3DCreated);

    5. // 请求Context3D默认值为 Context3DRenderMode.AUTO,
    6. // 如果可能,运行时将为该值创建硬件加速上下文,否则转向软件加速功能。
    7. // 使用 Context3DRenderMode.SOFTWARE 请求软件渲染上下文。
    8. // 软件渲染在移动设备上不可用。
    9. stage3D.requestContext3D(Context3DRenderMode.AUTO);

    10. // Context3D 创建完成的时间监听函数
    11. function onContext3DCreated(ev:Event): void
    12. {
    13.     // 从Stage3D中获取创建好的Context3D
    14.     var context3D:Context3D = stage3D.context3D;

    15.     // 为Context3D设置渲染缓冲区的视口尺寸和其他属性
    16.     context3D.configureBackBuffer(
    17.         stage.stageWidth,
    18.         stage.stageHeight,
    19.         0, // 消除锯齿品质
    20.         true // true 会创建深度和印模缓冲区,false 指示未创建深度或印模缓冲区。
    21.     );

    22.     // ......使用Context3D
    23. }
    复制代码
    现在我们有了一个Context3D的实例,我们就能把得到的着色器字节码上传到一个Program3D的实例中了。
    1. // 为我们的着色器字节码创建一个着色器程序
    2. var program:Program3D = context3D.createProgram();

    3. // 上传顶点着色器和片段着色器字节码到着色器程序中
    4. try
    5. {
    6.     program.upload(vertexShaderBytecode, fragmentShaderBytecode);
    7. }
    8. catch (err:Error)
    9. {
    10.     // 在上传着色器的过程中可能发生的错误很多. 这些错误中许多
    11.     // 都是简单的错误检查 (例如bytecode为空null) 但是更多的都是字节码
    12.     // 的错误,比如着色器程序超过了200个硬件指令
    13.     trace("Couldn't upload shader program: " + err);
    14.     return;
    15. }
    复制代码
    现在我们可以使用我们刚刚编译好的着色器了,这非常简单
    1. // 为所有的drawTriangles函数调用设置着色器程序
    2. context3D.setProgram(program);

    3. // 用相同的着色器程序绘制多少个三角形
    4. context3D.drawTriangles(myTriangles);
    5. context3D.drawTriangles(myOtherTriangles);

    6. // 结束绘制
    7. context3D.present();
    复制代码
    最后,我们来看看AGAL程序,顶点着色器代码只做了一件事:输出存储在第一个顶点属性中的向量作为顶点坐标。片段着色器也只做一件事:输出存储在第一个常量中的向量作为顶点颜色。这些着色器都满足所有着色器的基本要求:输出一个位置(顶点着色器)和一个颜色(片段着色器),下面是顶点着色器的源代码
    1. mov op, va0
    复制代码
    下面是片段着色器的代码
    1. mov oc, fc0
    复制代码
    按照前面的文章中我们讨论过的,我们需要使用Context3DAS3程序中传递这些数据到着色器。
    1. ////////////////////
    2. // 处理一次
    3. ////////////////////

    4. // 创建一个值在0到1之间(不是0到0xff之间)的颜色常量作为RGBA向量
    5. var color:Vector.<Number> = new <Number>[0.9296875, 0.9140625, 0.84765625, 1];

    6. // 为一个三角形创建一个顶点位置的顶点缓冲,每个顶点位置有3个属性(x,y,z)
    7. var positions:VertexBuffer3D = context3D.createVertexBuffer(3, 3);

    8. // 上传三角形的位置信息
    9. positions.uploadFromVector(new <Number>[
    10.      0,  1, 0, // 顶点
    11.     -1, -1, 0, // 左下角
    12.      1, -1, 0  // 右下角
    13. ], 0, 3);

    14. // 为三角形创建一个索引缓冲区
    15. var tris:IndexBuffer3D = context3D.createIndexBuffer(3);

    16. // 上传三角形的所有索引到索引缓冲区
    17. tris.uploadFromVector(new <uint>[0, 1, 2], 0, 3);


    18. /////////////////////////////////
    19. // 每一帧的绘制代码
    20. /////////////////////////////////
    21. // 清空上一帧绘制的内容
    22. context3D.clear();

    23. // 为所有的drawTriangles函数调用设置着色器程序
    24. context3D.setProgram(program);

    25. // 上传顶点位置到va0
    26. context3D.setVertexBufferAt(0, positions, 0, Context3DVertexBufferFormat.FLOAT_3);

    27. // 上传颜色到片段着色器常量 fc0
    28. context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, color);

    29. // 绘制三角形
    30. context3D.drawTriangles(tris);

    31. // 结束绘制
    32. context3D.present();
    复制代码


    守望者AIR技术交流社区(www.airmyth.com)
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    
    关闭

    站长推荐上一条 /4 下一条

    QQ|手机版|Archiver|网站地图|小黑屋|守望者 ( 京ICP备14061876号

    GMT+8, 2017-12-14 04:34 , Processed in 1.359375 second(s), 30 queries .

    守望者AIR

    守望者AIR技术交流社区

    本站成立于 2014年12月31日

    快速回复 返回顶部 返回列表