《点睛:ActionScript3.0游戏互动编程》——1.2 ColorTransform对RGB数值的操作及应用...

    xiaoxiao2024-03-22  114

    本节书摘来自异步社区《点睛:ActionScript3.0游戏互动编程》一书中的第1章,第1.2节,作者:游志德 , 彭文波 更多章节内容可以访问云栖社区“异步社区”公众号查看。

    1.2 ColorTransform对RGB数值的操作及应用

    flash.geom.ColorTransform是Flash内置的一个色彩变换类。它支持色彩通道值的线性变换。

    所谓线性变换,是指一次函数模式的变换:dst = src * multiplier + offset。我们可以将每个通道的值与常量进行四则运算。对于ColorTransform类而言,通道的原数值和变换后的数值存在如下关系。

    red(dst) = red(src) * redMultiplier + redOffset green(dst) = green(src) * greenMultiplier + greenOffset blue(dst) = blue(src) * blueMultiplier + blueOffset alpha(dst) = alpha(src) * alphaMultiplier + alphaOffset

    这当中,dst代表结果值,src代表初始值。

    这个运算看起来并不复杂,我们也能手工实现。但是,自己写的算式无法直接应用于显示对象或者BitmapData上,我们必须逐个像素点地去修改颜色(当然,ColorMatrixFilter、自定义滤镜、颜色表等方法也能实现),并且对于矢量图而言,处理前还不得不先转换成位图。而用ColorTransform的话则非常方便,我们只要对指定对象应用了它,每个像素点的颜色就会自动做出相应的变化,从而实现图形色调、亮度等属性的整体变换乃至反色效果,而且不会丢失矢量信息。

    此外,ColorTransform还支持通过color属性将对象的所有像素点设置成同一种颜色。假设myColorTransform.color = 0xFFFF00,被应用了myColorTransform的对象就会呈现为单一黄色的状态,它跟下面的代码完全等价。

    代码清单1-2

    myColorTransform.redMultiplier = myColorTransform.greenMultiplier = myColorTransform.blueMultipler = 0; myColorTransform.redOffset = 255; myColorTransform.greenOffset = 255; myColorTransform.blueOffset = 0;

    因为与src相乘的系数都被设置为0,所以不管src是多少,dst的值都不会受到任何影响,red(dst)和green(dst)始终等于255,blue(dst)始终等于0,于是所有像素点的RGB色彩值都等于0xFFFF00(黄色),而alpha(不透明度)则不发生变化。

    ColorTransform最常用的地方有两处。

    1 DisplayObject.transform.colorTransform = myColorTransform; 2 BitmapData.colorTransform(myColorTransform);

    这里我们只测试第一项,第二项在BitmapData的相关章节再进一步深入讨论。

    1.2.1 RGB测试用例的书写新建一个ActionScript项目,名为ShapeColorTransformTest,我们在里面创建几个不同颜色的小圆,观察它们在应用了colorTransform之后的变化,代码如下。

    代码清单1-3

    package{ [SWF(width = "800", height = "600")] public class ShapeColorTransformTest extends Sprite{ private var _testSprite_src:Sprite; //用于测试的显示对象(变换前) private var _testSprite_dst:Sprite; //用于测试的显示对象(变换后) private var _myColorTransform:ColorTransform; //颜色转换对象 public function ShapeColorTransformTest(){ init(); } private function init():void{ _testSprite_src = getTestSprite(); addChild(_testSprite_src); _testSprite_dst = getTestSprite(); _testSprite_dst.x = 200; addChild(_testSprite_dst); applyTransform(); } //创建用于测试的显示对象 private function getTestSprite():Sprite{ var _testSprite:Sprite = new Sprite(); var _shape1:Shape = new Shape(); //添加一个黑色的矩形底 _shape1.graphics.beginFill(0x000000); _shape1.graphics.drawRect(40, 30, 170, 290); _shape1.graphics.endFill(); _testSprite.addChild(_shape1); /*依次添加颜色分别为暗蓝、暗红、暗绿和纯白的4个圆*/ _testSprite.addChild(new Circle(50, 0x0000CC, 100, 200, "add")); _testSprite.addChild(new Circle(50, 0xCC0000, 150, 200, "add")); _testSprite.addChild(new Circle(50, 0x00CC00, 125, 250, 1,"add")); _testSprite.addChild(new Circle(37.5, 0xFFFFFF,125,100,1,add")); return _testSprite; } private function applyTransform():void{ _myColorTransform = _testSprite_dst.transform.colorTransform; //初始化颜色变换对象(从显示对象里获取) _myColorTransform.redMultiplier = 1.5; _testSprite_dst.transform.colorTransform = _myColorTransform; //应用到显示对象上(变换后必须重新赋值,否则变换效果不起作用,详情可查阅帮助文档) } }

    在后续的章节中,我们会经常使用圆来表示一个点或者创建一个色块。为便于这一功能的重用,我们可以把这个可爱的圆封装成一个Circle类并放入到公共类库。

    小提示有的书喜欢用Ball来命名这样的类,这里统一取名为Circle,因为纯色的圆并没有球的那种立体感。代码清单1-4

    package com.gemei.display{ public class Circle extends Sprite{ //为节省本书篇幅,我把x,y,alpha, blendMode这些基本属性都封装到Circle类里面了,项目开发中不推荐这么做 public function Circle(radius:Number = 50, color:uint = 0x000000, x:Number = 0, y:Number = 0, alpha:Number = 1, blendMode:String = BlendMode.NORMAL){ super(); this.x = x; this.y = y; this.blendMode = blendMode; graphics.beginFill(color, alpha); graphics.drawCircle(0, 0, radius); graphics.endFill(); } } }

    以上代码创建了两个外观一模一样的显示对象,然后添加到舞台上显示(使用BlendMode只是为了方便创建多个色块,后续章节笔者会给出详细的介绍),我们用ColorTransform对其中一个进行变换,从而跟不变换的那个进行比较。

    1.2.2 初始效果及颜色属性的测试按F11/Ctrl+F11测试,效果如图1-2所示。如果左右两组小圆色彩上看起来有所差别,那就请大家校对一下是否在输入代码的过程中出现了笔误,同时注意检查显示器是否因为老化、视角等问题而导致同一种颜色在不同的位置有不同的效果。因为现在的代码仅仅做了一次赋值,中途并没有对ColorTransform进行其他处理。

    下面我们先从效果最明显的color开始测试。设置一下_myColorTransform.color = 0xFFFF00看看,如图1-3所示。

    整个显示对象都变黄了。此外,我们用下面的代码也能实现同样的效果。

    代码清单1-5

    _myColorTransform.redMultiplier = _myTransform.greenMultiplier = _myColorTransform.blueMultiplier = 0; _myColorTransform.redOffset = _myColorTransform.greenOffset = 0xFF;

    代码起作用了,我们尝试复杂一点的变换。

    1.2.3 线性/倍乘提高降低亮度线性提高亮度(见图1-4):_myColorTransform.redOffset = _myColorTransform.greenOffset = _myColorTransform.blueOffset = 100。

    线性降低亮度(见图1-5):_myColorTransform.redOffset = _myColorTransform.greenOffset = _myColorTransform.blueOffset = -100。

    倍乘提高亮度(见图 1-6):_myColorTransform.redMultiplier = _myColorTransform. greenMultiplier = _myColorTransform.blueMultiplier = 1.5。

    倍乘降低亮度(见图 1-7):_myColorTransform.redMultiplier = _myColorTransform. greenMultiplier = _myColorTransform.blueMultiplier = 0.7。

    我们可以看到,通过ColorTransform增大R、G、B这三个通道的值,能够使所有色彩都变得明亮;相反,减小它们的值,色彩就变暗。然而,这种感觉多少有点别扭,它们看起来更像是给图形蒙上了一层灰,效果特别不自然。倍乘提高亮度的感觉稍好一点,因为黑色和白色在经过运算之后,通道值都没有发生变化,黑色的RGB均为0,乘以1.5后不变;白色的RGB均为255,乘以1.5后大于上限255,于是结果仍为255。

    我们可以尝试将线性变化和倍乘变化结合在一起,让亮度变暗的效果舒服些,比如让黑色的RGB结果小于0,而白色始终保持255。

    代码清单1-6

    _myColorTransform.redMultiplier = _myColorTransform.greenMultiplier = _myColorTransform. blueMultiplier = 1.5; _myColorTransform.redOffset = _myColorTransform.greenOffset = _myColorTransform.blueOffset = - 127;

    我们应用上了这个 ColorTransform 以后,255的变换结果等于 255,而 0 的变换结果等于−127,取下限之后就是 0,于是黑和白看起来就没有变化了,如图1-8所示。

    然而,这始终是针对极端数值的权宜之计,局限性很大,对其他颜色的可控性较差,可能在单色的矢量图下感觉不明显,但如果换成色彩比较丰富的照片,那么效果依然不尽如人意,大家可以找些照片来测试一下。而且,从这些例子我们可以看出,RGB的数值跟人类的视觉系统并不协调,至少亮度处理方面是这样的情况。

    以上例子对每个通道都做出了完全相同的变换,下面我们尝试给不同通道设置不同的值,看看有没有新的发现。

    1.2.4 单个通道的线性/倍乘变化线性提高红色的成分(见图1-9):_myColorTransform.redOffset = 127。

    线性减弱红色的成分(见图1-10):_myColorTransform.redOffset = -127。

    倍乘提高红色的成分(见图1-11):_myColorTransform.redMultiplier = 1.5。

    倍乘降低红色的成分(见图1-12):_myColorTransform.redMultiplier = 0.7。

    我们调整单个通道的值,图像的色彩发生了一些微妙的变化。因为现在只对单个通道进行计算,所以总的来说,反射出来的色光都会增多或者减少,如果想保持反射的色光总量不变,就应该多调整一个或者两个通道以抵消红色通道的改变。

    1.2.5 ColorTransform在色彩处理方面的不足在以上的测试中,有些本来不同的颜色在经过ColorTransform的转换以后变得出奇的一致。如果想给图像做变色效果,那这样的情况往往是我们需要避免的。此时,ColorTransform就有点心有余而力不足了。

    归根到底,这还是因为RGB模式在人类的视觉体验方面做得不够友好。

    接下来,我们看看之前没测试过的alpha变换。理所当然,之前的图像就不太适用了,因为图像里每个像素点的alpha都等于1。**1.2.6 Alpha测试用例的书写**我们把ShapeColorTransformTest复制为ShapeColorTransform ForAlphaTest,然后对代码进行如下调整。

    首先我们把getTestSprite修改为如下格式。

    代码清单1-7

    //创建用于测试的显示对象 private function getTestSprite():Sprite{ var _f:Array = [new BlurFilter(80, 80)]; var _testSprite:Sprite = new Sprite(); /*添加两个圆,颜色分别为白和黑*/ _testSprite.addChild(new Circle(50, 0xFFFFFF, 100, 100)).filters = _f; _testSprite.addChild(new Circle(50, 0xFFFFFF, 100, 250)).filters = _f; return _testSprite; }

    而applyTransform函数则调整如下。

    代码清单1-8

    private function applyTransform():void{ _myColorTransform = _testSprite_dst.transform.colorTransform; _myColorTransform.alphaMultiplier = 0; _myColorTransform.alphaOffset = 127; _testSprite_dst.transform.colorTransform = _myColorTransform; }

    运行效果如图1-13所示。以上代码用模糊滤镜BlurFilter生成了带两个透明渐变圆的Sprite。本来这里用透明渐变填充会更加合理,但考虑到渐变代码比较复杂,而且这里还没介绍到,就先用模糊滤镜。

    下面我们尝试一下alpha变换。

    ** 1.2.7 线性提高或降低alpha值** 提高alpha值(见图1-14):_myColorTransform.alphaOffset = 100。

    降低alpha值(见图1-15):_myColorTransform.alphaOffset = -100。

    粗略一看,它跟设置alpha似乎没什么两样,但仔细观察我们就会发现,当alphaOffset提高的时候,周边比较透明的像素会逐渐淡入到1;相反,降低的时候,周边会慢慢淡出到0。所以,虽然这仅仅是一个简单的线性变换,但是拿来做光圈或者黑洞的扩散/收缩效果将会相当不错。

    下面,我们做个直接设置alpha的版本来对照一下效果。

    1.2.8 设置alpha值倍乘降低alpha(见图1-16):_myColorTransform.alphaMultiplier = 0.7。

    注意,设置alpha跟设置颜色不同,它要在原有像素的基础上设置倍率才符合alpha的概念,效果才跟设置displayObject.alpha一致。

    倍乘提高alpha(见图1-17):_myColorTransform.alphaMultiplier = 1.5。

    我们可以看出,alpha的变换呈现为整体性,因此没有扩散收缩的效果,如果大家有跟着我们一起测试的话,就不妨用补间引擎或者enterFrame来测试两种效果在渐入渐出时的差别。我们认为alphaOffset的效果要比multiplier漂亮多了。

    如果我们像设置颜色那样,把multiplier弄成0,像素点的透明度就统一起来了。这个效果虽然不甚美观,但是很适合用来做一些图像的预处理工作。

    代码如下。

    代码清单1-9

    _myColorTransform.alphaMultiplier = 0; _myColorTransform.alphaOffset = 127;

    效果如图1-18所示。

    在alpha通道方面,ColorTransform没让我们失望,毕竟它独立于色彩模式,而且只有一个值,所以运算的处理也相对容易把握一些。

    1.2.9 用ColorTransform实现反色效果本节的最后,笔者给大家介绍一个稍稍有点意思的效果——反色。这种颠覆性的变换,线性的ColorTransform也能做到吗?答案是肯定的!所谓的反色,就是白变黑,黑变白,浅变深,深变浅,它的计算公式也非常简单,用100%减去原值就能得到结果色,即

    dst = 255 – src;

    套到ColorTransform的计算公式中,就有如下格式。

    red(dst) = red(src) * (-1) + 255。 green(dst) = green(src) * (-1) + 255 blue(dst) = blue(src) * (-1) + 255

    换而言之,只要把offset都设为255、multiplier都设成−1,就可以得到所谓的反色效果了(可能有的读者还没想过将multiplier设置为负数吧)。

    代码清单1-10

    _myColorTransform.redMultiplier = -1; _myColorTransform.greenMultiplier =-1; _myColorTransform.blueMultiplier = -1; _myColorTransform.redOffset = 255; _myColorTransform.greenOffset = 255; _myColorTransform.blueOffset = 255;

    可出来的效果跟预期不一致,如图1-19所示。

    经过将近两周的纠结,笔者终于找到了问题的症结所在——跟BlendMode发生冲突了。因为BlendMode.ADD也是像素运算,两者混合后的运算机制及其优先级规则有待作进一步的研究。

    我们把BlendMode.ADD一句去掉之后,反色效果跃然屏上,如图1-20所示。

    相关资源:ActionScript3.0游戏互动编程源文件
    最新回复(0)