在Flash Builder 4 beta中使用Pixel Bender作为数值运算引擎(三)

    技术2022-05-19  20

    本系列教程目录:http://www.pixelbender.cn/?p=323

    使用Pixel bender进行基本运算

    使用Pixel Bender我们可以进行简单的算术运算,比如加、减、乘、除,同样也可以进行复杂的函数运算,比如求正弦、余弦等等。但是你不能在Pixel Bender kernel中实现循环。

    在下面的例子中,我们将创建一个返回两个数乘积的简单的kernel,然后我们会利用该kernel建立一个计算两列数乘积的应用。

    创建该kernel

    通过以下步骤来创建这个kernel:

    如果你还没有安装 Pixel Bender Toolkit,请下载并安装。 打开Pixel Bender Toolkit然后单击Create A New Kernel。 粘贴以下代码至编辑器。

    <languageVersion : 1.0;>kernel SimpleCalculator<namespace : “PixelBender”;vendor : “Elrom LLC”;version : 1;description : “Simple calculator”;>{input image1 src;input image1 src2;output pixel3 result;void evaluatePixel(){float x = pixel1( sample(src, outCoord()) );float y = pixel1( sample(src2, outCoord()) );float z = x*y;result = pixel3( z, 0.0, 0.0);}}

    Kernel中的代码包含三个部分:元数据、申明以及函数。

    Kernel 元数据

    元数据包括:

    language-version element -语言版本,这是必须的元素,当前的版本是1.0 name -Kernel的名称,在这个例子中是“SimpleCalculator” Namespace –命名空间,便于对kernel进行分类 Vendor -版权所属,开发该kernel的公司或个人的名称 Description –描述,这是唯一可选(可有可无)的元数据 Version –版本号,必须是整数,从1开始

    <languageVersion : 1.0;>kernel SimpleCalculator<namespace : “PixelBender”;vendor : “Elrom LLC”;version : 1;description : “Simple calculator”;>

    Declarations 申明

    kernel的第二个部分是定义输入和输出。尽管kernel没有用到他们,这些申明同样可以包含函数中用到的参数、变量和常数,当然,需要的话也可以导入函数库。

    在这里例子中,我们会使用两个单通道的图像作为输入,将他们分别命名为 src 和 src2 :

    input image1 src;input image1 src2;

    定义输出为 pixel3 类型,pixel3 是Pixel Bender的一个数据类型,它持有三个通道。

    output pixel3 result;

    Pixel Bender 为图像和像素提供至多4个通道,即red,green,blue和alpha通道。数据类型image1、image2、image3和image4分别对应一个、两个、三个、四个通道的图像。同样的,pixel1、pixel2、pixel3 和 pixel4 分别对应一个、两个、三个、四个通道的像素。

    你可能疑惑上面的代码为什么返回的结果的类型为三个通道,而事实上我们只用到一个通道。Pixel Bender Toolkit规定输出必须是三通道类型,如果不是的话,你在编译的时候会遇到以下错误:

    “ERROR: (line 23): ‘result’ : cannot have 1 or 2 channel outputs”。

    函数申明

    Kernel的另一个部分是函数定义。每个kernel都必须定义 evaluatePixel() 函数,当然你可以定义其他的函数比如 evaluateDependents()、needed()、changed() 等等。请查看Pixel Bender手册获取更详细的信息。

    注意:要获取Pixel Bender手册,可以在Pixel Bender Toolkit中选择Help > Pixel Bender Language specification。

    在这个例子中,我们的程序以两幅单通道图像作为输入并对其像素进行乘法运算,然后返回 pixel3 类型的结果:

    void evaluatePixel(){float x = pixel1( sample(src, outCoord()) );float y = pixel1( sample(src2, outCoord()) );float z = x*y;result = pixel3( z, 0.0, 0.0);}

    你可能会想要看一看经过该 kernel 处理后的图像是什么样子。如果你点击Run,你会看见一个窗口提示你必须载入图像(见图2)。要运行这个kernel,请按照以下步骤:

    选择菜单 File > Load image 1然后在本地选择需要载入的图片选择菜单 File > Load image 2然后在本地选择需要载入的图片点击Run

    你将会看见一张将你所选的两张图像的每一个像素作了乘法运算之后的整合图像。

    图2.Pixel Bender提示载入图像

    如果想要在Flash Player中使用kernel,你必须先将其输出为编译好的文件(即.pbj文件)。

    创建PBJ文件,选择File > Export Filter for Flash Player(见图3),本例中命名为SimpleCalculator.pbj。

    图3.输出供 Flash Player 使用的 PBJ 文件

    建立Flex应用

    现在已经导出了 PBJ 文件,我们就可以创建一个Flex应用来对两列数值进行简单的乘法计算(z=x*y)了,程序如下:

    <?xml version=”1.0″ encoding=”utf-8″?><s:Application xmlns:fx=”http://ns.adobe.com/mxml/2009” xmlns:s=”library://ns.adobe.com/flex/spark” xmlns:mx=”library://ns.adobe.com/flex/halo”

    minWidth=”1024″ minHeight=”768″>

    <fx:Script><![CDATA[

    import mx.collections.IList;

    [Embed(source="SimpleCalculator.pbj", mimeType="application/octet-stream")]private var KernelClass:Class;private var result:ByteArray;

    protected function initializeHandler(list:IList, list2:IList):void{var byteArray:ByteArray = new ByteArray();var byteArray2:ByteArray = new ByteArray();var shader:Shader = new Shader(new KernelClass());var shaderJob:ShaderJob;var height:int;var width:int;byteArray = convertListToByteArray( list );byteArray2 = convertListToByteArray( list2 );width = byteArray.length >> 2;height = 1;shader.data.src.width = width;shader.data.src.height = height;shader.data.src.input = byteArray;shader.data.src2.width = width;shader.data.src2.height = height;shader.data.src2.input = byteArray2;result = new ByteArray();result.endian = Endian.LITTLE_ENDIAN;shaderJob = new ShaderJob(shader, result, width, height);shaderJob.addEventListener(Event.COMPLETE, onComplete);shaderJob.start();}

    protected function onComplete(event:Event):void{var length:int = result.length;var num:Number;var i:int;result.position = 0;for(i = 0; i < length; i += 4){num = result.readFloat();if(i % 3 == 0)listDest.dataProvider.addItem( num );}}/*** Static method to convert the list* object into byte array object.** @param array* @return**/private static function convertListToByteArray(list:IList):ByteArray{var retVal:ByteArray = new ByteArray();var number:Number;var len:int = list.length;var i:int;retVal.endian = Endian.LITTLE_ENDIAN;for (i; i<len; i++){retVal.writeFloat( Number(list[i]) );}retVal.position = 0;return retVal;}

    ]]></fx:Script>

    <s:List id=”listSrc” width=”57″ height=”158″><s:ArrayCollection><fx:Object>1</fx:Object><fx:Object>2</fx:Object><fx:Object>3</fx:Object><fx:Object>4</fx:Object><fx:Object>5</fx:Object><fx:Object>6</fx:Object><fx:Object>7</fx:Object></s:ArrayCollection></s:List>

    <s:List id=”listSrc2″ width=”57″ height=”158″ x=”122″ y=”0″><s:ArrayCollection><fx:Object>2</fx:Object><fx:Object>5</fx:Object><fx:Object>2</fx:Object><fx:Object>3</fx:Object><fx:Object>4</fx:Object><fx:Object>5</fx:Object><fx:Object>6</fx:Object></s:ArrayCollection></s:List>

    <s:Button x=”210″ y=”56″ label=”=” click=”initializeHandler(listSrc.dataProvider, listSrc2.dataProvider)”/>

    <s:List id=”listDest” width=”110″ height=”158″ x=”314″ y=”-1″><s:ArrayCollection /></s:List>

    <s:RichText x=”88″ y=”61″ text=”X”/></s:Application>

    使用Flash Builder 4 beta来编译上面的代码,当你运行该程序的时候,你可以看见两列数字,点击“=”按钮来利用刚才写好的Pixel Bender kernel进行计算。(见图4)

    既然已经了解该程序的功能,现在我们来仔细讲解代码。第一步是嵌入编译好的PBJ文件,嵌入方法同嵌入其他的资源(如字体)类似。

    [Embed(source="SimpleCalculator.pbj", mimeType="application/octet-stream")]

    Embed 标签会命令ActionScript编译器在生成SWF文件的时候嵌入Pixel Bender kernel,然后用一个Class类型的变量来调用它。在本例中,KernelClass 是一个未实例化的类,它用来供实例化Shader类的时候使用。

    private var KernelClass:Class;

    ByteArray类型的 result 将持有Shader返回的值。

    private var result:ByteArray;

    initializeHandler() 函数调用时便开始进行计算,它会对传入的两列数值进行乘法运算:

    protected function initializeHandler(list:IList, list2:IList):void

    每一列数值都会先被转换为ByteArray,因为Pixel Bender kernel是对输入图像的每一个像素逐个进行处理的。下面分别定义byteArray和byteArray2:

    var byteArray:ByteArray = new ByteArray();var byteArray2:ByteArray = new ByteArray();

    我们还需要一个Shader对象去执行kernel,来对图像的每个像素进行处理(在本例中是一列数值而不是图像)。

    var shader:Shader = new Shader(new KernelClass());var shaderJob:ShaderJob;var height:int;var width:int;

    convertListToByteArray() 方法可以将一列数值转换为ByteArray

    byteArray = convertListToByteArray( list );byteArray2 = convertListToByteArray( list2 );

    接着,我们设定shader job所需的所有数据和参数:

    // the width of data source “image” is the length of the// byteArray and height is set to one.width = byteArray.length >> 2;height = 1;shader.data.src.width = width;shader.data.src.height = height;shader.data.src.input = byteArray;shader.data.src2.width = width;shader.data.src2.height = height;shader.data.src2.input = byteArray2;result = new ByteArray();result.endian = Endian.LITTLE_ENDIAN;

    所有东西都设定完了之后,我们实例化ShaderJob对象并监听其COMPLETE事件来处理返回值,之后启动shaderJob

    shaderJob = new ShaderJob(shader, result, width, height);shaderJob.addEventListener(Event.COMPLETE, onComplete);shaderJob.start();}

    注意一下,图像的宽度是byteArray长度的1/4,因为一个浮点数需要4个字节来存储。运算完成的时候,onComplete()将结果再转换成一列数值。

    protected function onComplete(event:Event):void{var length:int = result.length;var num:Number;var i:int;result.position = 0;for(i = 0; i < length; i += 4){num = result.readFloat();if(i % 3 == 0)listDest.dataProvider.addItem( num );}}

    下面是convertListToByteArray(), 是一个通用的将列表转换为ByteArray的方法:

    private static function convertListToByteArray(list:IList):ByteArray{var retVal:ByteArray = new ByteArray();var number:Number;var len:int = list.length;var i:int;retVal.endian = Endian.LITTLE_ENDIAN;for (i; i<len; i++){retVal.writeFloat( Number(list[i]) );}retVal.position = 0;return retVal;}

    程序的最后一部分就是UI(用户界面)了,它包含两列数值,一个开始按钮以及一个列表来显示结果。

    <s:List id=”listSrc” width=”57″ height=”158″><s:ArrayCollection><fx:Object>1</fx:Object><fx:Object>2</fx:Object><fx:Object>3</fx:Object><fx:Object>4</fx:Object><fx:Object>5</fx:Object><fx:Object>6</fx:Object><fx:Object>7</fx:Object></s:ArrayCollection></s:List>

    <s:List id=”listSrc2″ width=”57″ height=”158″ x=”122″ y=”0″><s:ArrayCollection><fx:Object>2</fx:Object><fx:Object>5</fx:Object><fx:Object>2</fx:Object><fx:Object>3</fx:Object><fx:Object>4</fx:Object><fx:Object>5</fx:Object><fx:Object>6</fx:Object></s:ArrayCollection></s:List>

    <s:Button x=”210″ y=”56″ label=”=” click=”initializeHandler(listSrc.dataProvider, listSrc2.dataProvider)”/>

    <s:List id=”listDest” width=”110″ height=”158″ x=”314″ y=”-1″><s:ArrayCollection /></s:List>

    <s:RichText x=”88″ y=”61″ text=”X”/></s:Application>


    最新回复(0)