PixelBenderのサンプルをちょこっとずつやってみる。

日曜プログラミングってことでPixelBenderのサンプルをちょこっとずつやってみてます。
ファイル元はこちら。
Pixel Bender Toolkit:Gallery

置き換えマップ(DisplacementMap )

オリジナルページ
Flexでの同様のフィルター
置き換えマップフィルタ
出力画像

入力画像その1(マップ画像

入力画像その2(マップ適応先画像

ソースコードにちょびっとコメントを追加してます。
そしてなんか日本語が挿入でいない。

//
// DisplacementMapFilter.pbk
//
// Copyright (c) 2008 Ryan Taylor | http://www.boostworthy.com
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// //
// +          +          +          +          +          +          +          +          +
//
//
<languageVersion : 1.0;>
kernel DisplacementMapFilter
<
    namespace   : "Boostworthy::Filters";
    vendor      : "Ryan Taylor";
    version     : 1;
    description : "Standard filter for displacing source pixels based on map color values.";
>
{
    //setting parameter
    parameter   int2        component
    <
        minValue        : int2(0, 0);
        maxValue        : int2(3, 3);
        defaultValue    : int2(1, 2);
    >;
    parameter   float2      offset
    <
        minValue        : float2(-256.0, -256.0);
        maxValue        : float2(256.0, 256.0);
        defaultValue    : float2(0.0, 0.0);
    >;
    parameter   float       scale
    <
        minValue        : -1.0;
        maxValue        :  1.0;
        defaultValue    :  1.0;
    >;
    //input output image
    input       image4      source;
    input       image4      map;
    output      pixel4      result;
    //main function
    void evaluatePixel()
    {
        //get now point x , y;
        float2  coord       = outCoord();
        //get pixels from now point
        pixel4  mapPixel    = sampleLinear(map, coord);
        // Note: The following statements are used for the sake of
        // Flash compatibility. mapPixel[component[0]], for instance,
        // is not allowed because the index must be a constant.
        float   componentX;
        if(component[0] == 0)
            componentX = mapPixel[0];
        else if(component[0] == 1)
            componentX = mapPixel[1];
        else if(component[0] == 2)
            componentX = mapPixel[2];
        else if(component[0] == 3)
            componentX = mapPixel[3];
        float   componentY;
        if(component[1] == 0)
            componentY = mapPixel[0];
        else if(component[1] == 1)
            componentY = mapPixel[1];
        else if(component[1] == 2)
            componentY = mapPixel[2];
        else if(component[1] == 3)
            componentY = mapPixel[3];
        //set new points x, y
        float x  = coord.x + (componentX - 0.5) * offset[0] * scale;
        float y  = coord.y + (componentY - 0.5) * offset[1] * scale;
        //output image from new point
        result  = sampleLinear(source, float2(x, y));
    }
}

色相・彩度・明度を変更するフィルター

画像の色相・彩度・明度を変更するフィルター。

HLS て Hue , Lightness , Saturation の略なんだね。
ソース元

HLSについて

HSVの計算方法

// *****************************************************************************************
// HSLFilter.pbk
//
// Copyright (c) 2008 Ryan Taylor | http://www.boostworthy.com
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// *****************************************************************************************
//
// +          +          +          +          +          +          +          +          +
//
// *****************************************************************************************
<languageVersion : 1.0;>
kernel HSLFilter
<
    namespace   : "Boostworthy::Filters";
    vendor      : "Ryan Taylor";
    version     : 1;
    description : "Basic filter for adjusting the hue, saturation, and lightness of an image.";
>
{
    //setting parameter
    parameter   float       hue
    <
        minValue        :  -180.0;
        maxValue        :   180.0;
        defaultValue    :   0.0;
    >;
    parameter   float       saturation
    <
        minValue        :  -100.0;
        maxValue        :   100.0;
        defaultValue    :   0.0;
    >;
    parameter   float       lightness
    <
        minValue        :  -100.0;
        maxValue        :   100.0;
        defaultValue    :   0.0;
    >;
    //input output image
    input       image4      source;
    output      pixel4      result;
    //main function
    void evaluatePixel()
    {
        // Convert sampled pixel from RGB space to HSL space.
        pixel4 samp;
        float  sampMin;
        float  sampMax;
        float  sampDiff;
        float  sampSum;
        float  sampH;
        float  sampS;
        float  sampL;
        samp     = sampleNearest(source, outCoord());
        sampMin  = min(samp.r,  samp.g);
        sampMin  = min(sampMin, samp.b);
        sampMax  = max(samp.r,  samp.g);
        sampMax  = max(sampMax, samp.b);
        sampDiff = sampMax - sampMin;
        sampSum  = sampMax + sampMin;
        sampL    = sampSum * 0.5;
        if(sampMin == sampMax)
            sampH = 0.0;
        else if(sampMax == samp.r)
            sampH = mod(60.0 * ((samp.g - samp.b) / sampDiff), 360.0);
        else if(sampMax == samp.g)
            sampH = 60.0 * ((samp.b - samp.r) / sampDiff) + 120.0;
        else if(sampMax == samp.b)
            sampH = 60.0 * ((samp.r - samp.g) / sampDiff) + 240.0;
        else
            sampH = 0.0;
        if(sampMin == sampMax)
            sampS = 0.0;
        else if(sampL > 0.5)
            sampS = sampDiff / (2.0 - sampSum);
        else
            sampS = sampDiff / sampSum;
        // Transform the sampled HSL values by the amounts specified
        // by the hue, saturation, and lightness parameters.
        float outH;
        float outS;
        float outL;
        outH  = sampH - hue;
        outS  = sampS * (saturation / 100.0 + 1.0);
        outL  = sampL - (1.0 - (lightness / 100.0 + 1.0));
        // Convert the transformed HSL values back to RGB space.
        float q;
        float p;
        float h;
        if(outL < 0.5)
            q = outL * (1.0 + outS);
        else
            q = outL + outS - outL * outS;
        p = 2.0 * outL - q;
        h = outH / 360.0;
        float  oneOverThree = 1.0 / 3.0;
        float  twoOverThree = 2.0 / 3.0;
        float  oneOverSix   = 1.0 / 6.0;
        float3 t            = float3(h + oneOverThree, h, h - oneOverThree);
        if(t.r < 0.0)
            t.r += 1.0;
        else if(t.r > 1.0)
            t.r -= 1.0;
        if(t.g < 0.0)
            t.g += 1.0;
        else if(t.g > 1.0)
            t.g -= 1.0;
        if(t.b < 0.0)
            t.b += 1.0;
        else if(t.b > 1.0)
            t.b -= 1.0;
        pixel4 c = pixel4(0.0, 0.0, 0.0, samp.a);
        if(t.r < oneOverSix)
            c.r = p + (q - p) * 6.0 * t.r;
        else if(t.r >= oneOverSix && t.r < 0.5)
            c.r = q;
        else if(t.r >= 0.5 && t.r < twoOverThree)
            c.r = p + (q - p) * 6.0 * (twoOverThree - t.r);
        else
            c.r = p;
        if(t.g < oneOverSix)
            c.g = p + (q - p) * 6.0 * t.g;
        else if(t.g >= oneOverSix && t.g < 0.5)
            c.g = q;
        else if(t.g >= 0.5 && t.g < twoOverThree)
            c.g = p + (q - p) * 6.0 * (twoOverThree - t.g);
        else
            c.g = p;
        if(t.b < oneOverSix)
            c.b = p + (q - p) * 6.0 * t.b;
        else if(t.b >= oneOverSix && t.b < 0.5)
            c.b = q;
        else if(t.b >= 0.5 && t.b < twoOverThree)
            c.b = p + (q - p) * 6.0 * (twoOverThree - t.b);
        else
            c.b = p;
        // Apply the final ARGB color to the pixel.
        result = c;
    }
}

画像をグレースケールにして足し算


2枚の画像で片方をグレースケールにして合成。
合成用画像


画像の足し算は簡単で
dst = src1 * src2 でOK
#掛け算になるところがミソ。
src1 + 0.1 とすると明るくなり
src1 - 0.1 とすると暗くなる。
AlphaMatteComposite

// *****************************************************************************************
// AlphaMatteComposite.pbk
//
// Copyright (c) 2008 Ryan Taylor | http://www.boostworthy.com
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// *****************************************************************************************
//
// +          +          +          +          +          +          +          +          +
//
// *****************************************************************************************
<languageVersion : 1.0;>
kernel AlphaMatteComposite
<
    namespace   : "Boostworthy::Filters";
    vendor      : "Ryan Taylor";
    version     : 1;
    description : "Given a source image and grayscale matte which represents alpha, a composite of the two will result in a masked source image.";
>
{
    input       image4      source;
    input       image4      alphaMatte;
    output      pixel4      result;
    void evaluatePixel()
    {
        // Note: The 'alphaMatte' input is of type image4 rather
        // than image1 for maximum compatibilty with Flash.
        float2 coord            = outCoord();
        pixel4 sampSource       = sampleNearest(source,     coord);
        pixel4 sampAlphaMatte   = sampleNearest(alphaMatte, coord);
        pixel1 alphaMatte       = (sampAlphaMatte.r + sampAlphaMatte.g + sampAlphaMatte.b) / 3.0;
        result = sampSource * alphaMatte;
    }
}

バンプマップ

Bump Mapping
テクスチャで疑似的に凹凸を見せる方法。

テクスチャ画像

内積関数 dot を使って影を作成。
dot って関数は dot ( a , b )で a と b の内積を計算します。
内積についてはこちらがとても参考になります。

GLSLでの例

計算部分

float3 normal = float3( sampleNearest(src, outCoord()).rgb  ) ;
float dp = dot(normal, light);

ソースコード一覧

<languageVersion: 1.0;>
kernel map
<
    namespace : "AIF";
    vendor : "Valentin valyard Simonov";
    version : 2;
    description : "A simple bump mapping shader"; >
{
   input image4 src;
   input image4 map;
   output float4 dst;
   parameter float3 light
   <
       minValue:float3(0, 0, 0);
       maxValue:float3(255, 255, 255);
       defaultValue:float3(0, 0, 40);
   >;
   void evaluatePixel()
   {
       float3 light = light.xyz - float3(outCoord().x, outCoord().y, 0);
       light = normalize(light);
       float3 normal = 2.0*(sampleNearest(map, outCoord()).rgb - 0.5);
       float dp = dot(normal, light);
       if (dp < 0.0) dp = 0.0;
       if (dp > 1.0) dp = 1.0;
       dst = float4(0, 0, 0, 1);
       dst.rgb = sampleNearest(src, outCoord()).rgb * dp;
   }
}

チューブ型フィルター


センターからの距離計算

float2 p    = outCoord() - param_center;
float  dist = length(p);

画像のミックス

mix( pixel4 , param_bgColor , a );

pixel4 (たいてい src 画像 )に param_bgColor を a の割合で足す。
リファレンスには
Returns x * ( 1.0 - a ) + y * a.
linear interpolation between x and y
#xとyの直線的な挿入
って書かれてます。

/*****************************************************************************
 *
 * Petri Leskinen, Espoo, Finland, Jan 2008, May 2008
 * Tube-View ' effect
 * leskinen.petri [at] luukku.com
 *
 *****************************************************************************/
<languageVersion: 1.0;>
kernel TubeView
<
    namespace   : "Tube View";
    vendor      : "your vendor";
    version     : 1;
    description : "Tube view";
>
{
    parameter float radius
    <
        minValue    :float(0.1);
        maxValue    :float(300.0);
        defaultValue:float(50.0);
    >;
    parameter float turbulence
     <
        minValue    :float(-3.14);
        maxValue    :float(3.14);
        defaultValue:float(1.0);
        description :"Turbulence";
    >;
    parameter float fade1
     <
        minValue    :float(-1.0);
        maxValue    :float(1.0);
        defaultValue:float(0.2);
        description :"Fading on the edge of the tube";
    >;
    parameter float fade2
     <
        minValue    :float(0.0);
        maxValue    :float(2.0);
        defaultValue:float(0.4);
        description :"Fading by distance";
    >;
    parameter float2 center
    <
        minValue    :float2(-200.0, -200.0);
        maxValue    :float2(2048.0, 2048.0);
        defaultValue:float2(64.0, 128.0);
        description :"Center point";
    >;
    parameter float4 bgColor
    <
        minValue    :float4(0.0, 0.0, 0.0, 0.0);
        maxValue    :float4(1.0, 1.0,1.0,1.0);
        defaultValue:float4(0.0, 0.0, 0.0, 1.0);
        description :"Background color";
    >;
    //input output image
    input  image4 src;
    output float4 pxl;
    //main function
    void evaluatePixel()
    {
        // point relative to center
        float2 p = outCoord() - center;
        // relative distance from center, rel<0.0 if inside the circle
        float rel = length(p) /radius;
        float tmp = rel*rel;
        rel -= 1.0;
        // if outside, modify point location, new coordinates inside circle
        p /= (rel < 0.0) ? 1.0 : tmp ;
        // 'turbulence'-rotation
        float rotAngle =  turbulence *( rel < 0.0 ? 0.0 : rel ) ;
        p = float2( p.x*cos(rotAngle)-p.y*sin(rotAngle),
                    p.x*sin(rotAngle)+p.y*cos(rotAngle)
                   );
        // sample pixel from new location
        pxl= sample(src, center+p);
        // mix with bgColor according to distance
        float mx = fade1+ rel*fade2;
        pxl= mix(pxl, bgColor, rel < 0.0 ? 0.0 : mx );
   }
}

クリスタル化

//  Jan 2008, Espoo, Finland
// updated April 2008
//  leskinen 'dot' petri [at] luukku (dot) com
// - please send me a note if you use the code for anything cool !
//
<languageVersion: 1.0;>
kernel Crystallize
<
    namespace   : "by Petri Leskinen";
    vendor      : "";
    version     : 1;
    description : "Crystallize -filter";
>
{
    parameter float size
    <
        minValue    : float(1);
        maxValue    : float(300);
        defaultValue: float(20);
        description : "size";
    >;
    // rot1: rotation matrix, 18 degrees
    // rot1r: reverse rotation, -18 degrees
    // base1: rotation base point (somewhere far enough from origin)
    const float2x2 rot1  = float2x2(0.951,0.309,-0.309,0.951);
    const float2x2 rot1r = float2x2(0.951,-0.309,0.309,0.951);
    const float2   base1 = float2(2400,-100);
    // rot2: rotation matrix, 30 degrees
    // rot2r: reverse rotation, -30 degrees
    // base2: base point
    const float2x2 rot2  = float2x2(0.866,0.5,-0.5,0.866); // 30 degress
    const float2x2 rot2r = float2x2(0.866,-0.5,0.5,0.866);
    const float2   base2 = float2(-100,2400);
    input  image4 src;
    output pixel4 dst;
    void evaluatePixel()
    {
        // Crystallize, pseudo Voronoi-diagram using three nearby points,
        // calculated from 'randomly' placed and rotated rectangular grids
        // 1st grid and point
        float div=size;
        float2 newP= base1 + rot1r*div*( floor( rot1*(outCoord()-base1)/div ) +0.5);
        // 2nd grid
        div= 21.0/20.0*size; // factor 21, I picked some number that has no common denominators with the default size 20
        float2 p= base2 + rot2r*div*( floor( rot2*(outCoord()-base2)/div  ) +0.5);
        // comparing distance to the 1st sample point
        newP =  length(p-outCoord()) < length(newP-outCoord()) ? p : newP;
        // 3rd grid
        div= 19.0/20.0*size;
        p= div*( floor( outCoord()/div  ) +0.5);
        // comparing distance
        newP =  length(p-outCoord()) < length(newP-outCoord()) ? p : newP;
        // the new color is picked from the nearist point
        dst = sampleNearest(src,newP);
    }
}