Flashゲームプログラミング講座 for ActionScript3.0

 

Shader について

 


■ Shader について

 
■シェーダー機能について
 
Flash 10 以降から、Shader 機能が追加されました。
 
ピクセルシェーダーの一種です。画像処理に特化しています。
 
■シェーダーの開発ツールについて
 
Pixel Bender Toolkit というツールを使用します。
 
専用の言語を使用します。
 
Toolkit から、バイトコードファイル(PBJ)出力できます。
 
■ Shader クラスについて
 
Pixel Bender 内の、カーネル(Kernel) に該当します。
 

■ Shader オブジェクトを使用する

 
■ Shader オブジェクトを準備する
 
Shader オブジェクトに、バイトコード(PBJ) のバイナリを渡します
 
必要であれば、ソース用ビットマップを渡します
 
必要であれば、パラメータ情報もセットします。
 
■フィルタとして活用する
 
ShaderFilter クラスにて使用します。
 
■ブレンドとして活用する
 
blendShader プロパティに渡します。
 
■描画時にスタイルとして活用する
 
Graphics クラスにて使用します。
 


 

Shader オブジェクトを作成する

 


■ Shader オブジェクトを作成する

 
■ Shader オブジェクトを作成する
 
new 演算子を使って、Shader クラスをインスタンス化します。
 
引数に、バイトコードファイル(PBJ) のバイナリを指定します。
 
new Shader ( バイトコード ) :Shader
第01引数(略可)ByteArraybyteCode プロパティと同等
戻り値 ObjectObject オブジェクト
 
■作成例
 
Shader オブジェクトを作成する

import flash.display.Shader;

// ------------------------------------------------------------
// Shader オブジェクトを作成する
// ------------------------------------------------------------
var shader_obj:Shader = new Shader();

// 出力テスト
trace(shader_obj);
 


 

シェーダーバイトコードを設定する

 
 


■Shaer オブジェクトにバイトコードをセットする

 
byteCode プロパティを使用します。
 
このプロパティは、書き込み専用です。
 
セットした場合、内部の ShaderData オブジェクトも一新されます。
 

■バイトコードファイルを、静的に埋め込んで、バイナリを取得する

 
1.バイトコードファイルを埋め込む
 
Embed メタタグを使用します。
 
Adobe Flash CS4 以降から利用可能です。
 
source パラメータに、バイトコードファイル(PBJ) のパスを指定します。
 
プロジェクトからの相対パスを指定します。
 
mimeType パラメータに、"application/octet-stream" を指定します。
 
続けて、クラス名を宣言します。
 
ここでは、"MyByteCode" と名前を付けます。
 
埋め込みアセットクラスを設定する

[Embed( source="./sample.pbj" , mimeType="application/octet-stream" )]
var MyByteCode:Class;
 
2.Flash を書き出して、データの埋め込みを確認する
 
この状態で、Flash を書き出します。
 
すると、swf ファイルにデータが埋め込まれるようになります。
 
swf ファイルのサイズが増えたか、確認します。
 
3.ByteArray オブジェクトを作成する
 
MyByteCode クラスを、new 演算子を使ってインスタンス化します。
 
このクラスは、ByteArray クラスを継承しています。
 
埋め込んだファイルから、ByteArray オブジェクトを作成する

import flash.utils.ByteArray;
import flash.display.Shader;

// ------------------------------------------------------------
// 埋め込みアセットクラス
// ------------------------------------------------------------
[Embed( source="./sample.pbj" , mimeType="application/octet-stream" )]
var MyByteCode:Class;

// ------------------------------------------------------------
// 埋め込んだファイルから、ByteArray オブジェクトを作成する (自作クラス "MyByteCode" )
// ------------------------------------------------------------
var byte_code:ByteArray = new MyByteCode();

// 出力テスト
trace(byte_code);

// ------------------------------------------------------------
// Shader オブジェクトを作成する
// ------------------------------------------------------------
var shader_obj:Shader = new Shader(byte_code);

// 出力テスト
trace(shader_obj);
 

■バイトコードファイルを、動的に読み込んで、バイナリを取得する

 
外部ファイルを読み込んでバイナリを取得する
 
URLLoader クラスを使用します。
 
バイナリファイルの読み込みについては、こちらで解説しています。
 
取得例
 
外部のファイルを読み込んで、バイナリを取得する

import flash.display.Shader;
import flash.net.URLRequest;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.utils.ByteArray;
import flash.events.Event;

// ------------------------------------------------------------
// Shader オブジェクトを作成する
// ------------------------------------------------------------
var shader_obj:Shader = new Shader();

// ------------------------------------------------------------
// URLLoader オブジェクトを作成
// ------------------------------------------------------------
var url_loader:URLLoader = new URLLoader();

// 受信結果をバイナリ形式で取得する
url_loader.dataFormat = URLLoaderDataFormat.BINARY;

// ------------------------------------------------------------
// 読み込み完了時に実行されるイベント
// ------------------------------------------------------------
url_loader.addEventListener (Event.COMPLETE , function (e:Event):void{

	// ------------------------------------------------------------
	// 受信結果(レスポンスボティ)を取得する
	// ------------------------------------------------------------
	var byte_code:ByteArray = url_loader.data as ByteArray;

	// 出力テスト
	trace(byte_code);

	// ------------------------------------------------------------
	// バイトコードをセットする
	// ------------------------------------------------------------
	shader_obj.byteCode = byte_code;
});

// ------------------------------------------------------------
// 読み込みを開始する
// ------------------------------------------------------------
// URLRequest オブジェクトを作成する(アクセス先 URL を指定)
var url_request:URLRequest = new URLRequest( "./sample.pbj" );

// 読み込みを開始する
url_loader.load(url_request);
 


 

パラメータを設定する

 
 


■ ShaderData クラスについて

 
ShaderData クラスについて
 
シェーダー内の情報にアクセスするには、ShaderData クラスを使用します。
 
ShaderData オブジェクトを取得する
 
data プロパティを使用します。
 
Shader から、ShaderData オブジェクトを取得する

import flash.display.Shader;
import flash.display.ShaderData;

// ------------------------------------------------------------
// Shader オブジェクトを作成する
// ------------------------------------------------------------
var shader_obj:Shader = new Shader();

// ------------------------------------------------------------
// ShaderData オブジェクトを取得する
// ------------------------------------------------------------
var shader_data:ShaderData = shader_obj.data;

// 出力テスト
trace(shader_data);
 
ShaderData オブジェクトについて
 
ShaderData オブジェクトは、連想配列です。
 
以下のデータが、混在して格納されています。
 
データ型説明
String カーネルの「メタデータ」関連の情報。
ShaderParameterカーネルの「パラメータ変数」関連の情報。
ShaderInput カーネルの「入力変数(ビットマップ)」関連の情報。
 
カーネルメタデータ情報を取得する
 
カーネルのメタデータ情報を取得できます。
 
以下のプロパティ名を使って、読み取りアクセスします。
 
省略されている場合もあります。
 
プロパティ名 データ型説明
name String カーネル名を取得。
nameSpace String カーネルの名前空間を取得。
vendor String カーネルのベンダー情報を取得。
version String カーネルのバージョン値を取得。
description String カーネルの説明情報を取得。
 
ShaderParameter オブジェクトを取得する
 
パラメータ変数に付けた変数名で、プロパティにアクセスします。
 
ShaderInput オブジェクトを取得する
 
入力変数に付けた変数名で、プロパティにアクセスします。
 
ShaderParameter オブジェクトを抽出取得する
 
for..in 文を使って、ShaderParameter 型のオブジェクトを抽出します。
 
index プロパティは、パラメータ変数の宣言順です。
 
ShaderParameter オブジェクトをまとめて取得する関数

import flash.display.ShaderData;
import flash.display.ShaderParameter;

// ------------------------------------------------------------
// ShaderParameter オブジェクトをまとめて取得する関数
// ------------------------------------------------------------
function ShaderDataGetParameterList(shader_data:ShaderData):Array{
	var a:Array = new Array();
	var p:ShaderParameter;
	var k:String;
	for(k in shader_data){
		p = shader_data[k] as ShaderParameter;
		if(!p) continue;
		a[p.index] = p;
	}
	return a;
}
 
ShaderInput オブジェクトを抽出取得する
 
for..in 文を使って、ShaderInput 型のオブジェクトを抽出します。
 
index プロパティは、入力変数の宣言順です。
 
ShaderInput オブジェクトをまとめて取得する関数

import flash.display.ShaderData;
import flash.display.ShaderInput;

// ------------------------------------------------------------
// ShaderInput オブジェクトをまとめて取得する関数
// ------------------------------------------------------------
function ShaderDataGetInputList(shader_data:ShaderData):Array{
	var a:Array = new Array();
	var i:ShaderInput;
	var k:String;
	for(k in shader_data){
		i = shader_data[k] as ShaderInput;
		if(!i) continue;
		a[i.index] = i;
	}
	return a;
}
 
取得例
 
ShaderData オブジェクト内のすべてのデータにアクセスする

import flash.utils.ByteArray;
import flash.display.Shader;
import flash.display.ShaderData;
import flash.display.ShaderParameter;
import flash.display.ShaderInput;

var i:int;
var num:int;
var key:String;

// ------------------------------------------------------------
// 埋め込みアセットクラス
// ------------------------------------------------------------
[Embed( source="./sample.pbj" , mimeType="application/octet-stream" )]
var MyByteCode:Class;

// ------------------------------------------------------------
// 埋め込んだファイルから、ByteArray オブジェクトを作成する (自作クラス "MyByteCode" )
// ------------------------------------------------------------
var byte_code:ByteArray = new MyByteCode();

// ------------------------------------------------------------
// Shader オブジェクトを作成する
// ------------------------------------------------------------
var shader_obj:Shader = new Shader(byte_code);

// ------------------------------------------------------------
// ShaderData オブジェクトを取得する
// ------------------------------------------------------------
var shader_data:ShaderData = shader_obj.data;


// ------------------------------------------------------------
// ShaderParameter オブジェクトをまとめて取得する関数
// ------------------------------------------------------------
function ShaderDataGetParameterList(shader_data:ShaderData):Array{
	var a:Array = new Array();
	var p:ShaderParameter;
	var k:String;
	for(k in shader_data){
		p = shader_data[k] as ShaderParameter;
		if(!p) continue;
		a[p.index] = p;
	}
	return a;
}

// ------------------------------------------------------------
// ShaderParameter オブジェクトをまとめて取得する
// ------------------------------------------------------------
var shader_parameter_list:Array = ShaderDataGetParameterList(shader_data);

num = shader_parameter_list.length;
for(i=0;i < num;i++){

	// ShaderParameter オブジェクトを取得する
	var shader_parameter:ShaderParameter = shader_parameter_list[i];

	// 出力テスト(プロパティ)
	trace("index" , shader_parameter.index);
	trace("type"  , shader_parameter.type);
	trace("value" , shader_parameter.value);

	// 出力テスト(メタデータ)
	for(key in shader_parameter){
		trace(key , shader_parameter[key]);
	}

	trace(" --- ");
}


// ------------------------------------------------------------
// ShaderInput オブジェクトをまとめて取得する関数
// ------------------------------------------------------------
function ShaderDataGetInputList(shader_data:ShaderData):Array{
	var a:Array = new Array();
	var i:ShaderInput;
	var k:String;
	for(k in shader_data){
		i = shader_data[k] as ShaderInput;
		if(!i) continue;
		a[i.index] = i;
	}
	return a;
}

// ------------------------------------------------------------
// ShaderInput オブジェクトをまとめて取得する
// ------------------------------------------------------------
var shader_input_list:Array = ShaderDataGetInputList(shader_data);

num = shader_input_list.length;
for(i=0;i < num;i++){

	// ShaderInput オブジェクトを取得する
	var shader_input:ShaderInput = shader_input_list[i];

	// 出力テスト(プロパティ)
	trace("index"    , shader_input.index);
	trace("channels" , shader_input.channels);
	trace("width"    , shader_input.width);
	trace("height"   , shader_input.height);
	trace("input"    , shader_input.input);

	// 出力テスト(拡張情報)
	for(key in shader_input){
		trace(key , shader_input[key]);
	}

	trace(" --- ");
}
 

■ ShaderParameter クラスについて

 
ShaderParameter クラスについて
 
ShaderParameter クラスは、カーネルのパラメータ変数に該当します。
 
数値情報などを、シェーダー側に渡す事ができます。
 
プロパティ一覧
 
プロパティ名 データ型説明
index int パラメータ変数の宣言位置を取得。(上から昇順)
type String パラメータ変数の型名を取得。(ShaderParameterType.*)
value Array 値情報をセットする。(パラメータ変数に渡す)
 
メタデータ情報を取得する
 
ShaderParameter オブジェクトは、連想配列です。
 
メタデータ情報も、格納されています。
 
for..in 文を使うと、メタデータ情報のみを、すべて抽出できます。
 
メタデータの一例です。
 
プロパティ名 データ型説明
name String パラメータ変数名を取得。
defaultValue Array 初期値を取得。
minValue Array 最小値を取得。
maxValue Array 最大値を取得。
stepInterval Array 1ステップごとに変化する最小単位を取得。
previewValue Array プレビュー時に最適な値を取得。
 
「値情報」を「パラメータ変数」に渡す
 
value プロパティを使用します。
 
値情報を、配列に格納して渡す必要があります。
 
例えば、float 型であれば、要素は 1 個必要です。
 
例えば、float3 型であれば、要素は 3 個必要です。
 
例えば、float4 型であれば、要素は 4 個必要です。
 
例えば、float3x3 型であれば、要素は 9 個必要です。
 
例えば、float4x4 型であれば、要素は 16 個必要です。
 

■ ShaderInput クラスについて

 
ShaderInput クラスについて
 
ShaderInput クラスは、カーネルの入力変数に該当します。
 
ソース用ビットマップを、シェーダーに渡す事ができます。
 
プロパティ一覧
 
プロパティ名 データ型説明
index int 入力変数の宣言位置を取得。(上から昇順)
channels int 入力変数のカラーチャンネル数を取得。
input Object ビットマップ情報をセットする。(入力変数に渡す)
width int ビットマップの幅を設定。
height int ビットマップの高さを設定。
 
拡張情報を取得する
 
プロパティ名 データ型説明
name String 入力変数名を取得。
 
「BitmapData オブジェクト」を「入力変数」に渡す
 
input プロパティに、BitmpData オブジェクトを渡します。
 
width、height プロパティの設定は不要です。
 
「ByteArray オブジェクト」を「入力変数」に渡す
 
最初に、widthheight プロパティを設定します。
 
次に、input プロパティに、ByteArray オブジェクトを渡します。
 
用意するバイナリは、0.0 ~ 1.0 の連続した数値データです。
 
赤、緑、青、透過チャンネルの順序で書き込みます。
 
(赤|緑|青|透過) | (赤|緑|青|透過) | (赤|緑|青|透過) ...
 
エンディアンを、リトルエンディアンに変更します。
 
writeFloat() メソッドを使って書き込みます。
 
「Vector.<Number> オブジェクト」を「入力変数」に渡す
 
最初に、widthheight プロパティを設定します。
 
次に、input プロパティに、Vector.<Number> オブジェクトを渡します。
 
0.0 ~ 1.0 の連続した数値データです。
 
赤、緑、青、透過チャンネルの順序で書き込みます。
 
(赤|緑|青|透過) | (赤|緑|青|透過) | (赤|緑|青|透過) ...
 

■パラメータの設定例

 
■使用例(Pixel Bender 側のコード)
 
「元のグラフィック」「1枚目のソースイメージ」間をマージする

<languageVersion : 1.0;>

// ------------------------------------------------------------
// カーネルを宣言
// ------------------------------------------------------------
kernel MyKernel < namespace:""; vendor:""; version:1; > {

	// ------------------------------------------------------------
	// 入力変数の宣言(ビットマップ)
	// ------------------------------------------------------------
	// 1枚目の入力イメージ(フィルタを適用する直前のグラフィック)
	input image4 src0;
	// 2枚目の入力イメージ(外部から渡される1枚目のソース)
	input image4 src1;


	// ------------------------------------------------------------
	// 出力変数の宣言(ピクセルカラー)
	// ------------------------------------------------------------
	output pixel4 dst;

	// ------------------------------------------------------------
	// パラメータ変数の宣言
	// ------------------------------------------------------------
	// 透明度
	parameter float alpha
	<
		minValue     : 0.0;
		maxValue     : 1.0;
		stepInterval : 0.01;
		defaultValue : 0.5;
	>;

	// スクロール値
	parameter float2 scroll
	<
		minValue     : float2( -2096.0 , -2096.0 );
		maxValue     : float2(  2096.0 ,  2096.0 );
		stepInterval : float2( 0.01 , 0.01 );
		defaultValue : float2( 0.0  , 0.0  );
	>;

	// ビットマップのサイズ(入力変数 "src1" )
	parameter float2 src1Size
	<
		parameterType : "inputSize";
		inputSizeName : "src1";
	>;

	// ------------------------------------------------------------
	// ピクセルごとに実行される関数
	// ------------------------------------------------------------
	void evaluatePixel() {

		// 現在のピクセル位置を取得する
		float2 pos0 = outCoord();

		// スクロールを加算
		float2 pos1 = pos0 + scroll;

		// 画像のサイズ
		float w = src1Size.x;
		float h = src1Size.y;

		// 位置を画像のサイズ内に収める
		pos1.x -= floor(pos1.x / w) * w;
		pos1.y -= floor(pos1.y / h) * h;

		// 位置を指定して、画像からピクセルカラーを取得する(ニアレストネイバー)
		pixel4 color0 = sampleNearest(src0 , pos0);
		pixel4 color1 = sampleNearest(src1 , pos1);

		// ピクセルカラーを出力する
		dst = (color1 - color0) * alpha + color0;
	}
}
 
■使用例(Flash 側のコード)
 
シェーダーを渡して、フィルタを動作させる

import flash.utils.ByteArray;
import flash.display.Shader;
import flash.display.ShaderData;
import flash.display.ShaderParameter;
import flash.display.ShaderInput;
import flash.display.Shape;
import flash.display.Graphics;
import flash.display.BitmapData;
import flash.filters.ShaderFilter;

// ------------------------------------------------------------
// Shape オブジェクトを作成する
// ------------------------------------------------------------
var shape:Shape = (function():Shape{

	// Shape オブジェクトを作成
	var shape:Shape = new Shape();

	// Shape に矩形を描画
	var g:Graphics = shape.graphics;
	g.beginFill ( 0xFFE0E0 , 1.0 );
	g.drawRect( 0 , 0 , stage.stageWidth , stage.stageHeight );
	g.endFill();

	return shape;
})();

// ステージに配置する
stage.addChild(shape);

// ------------------------------------------------------------
// BitmapData オブジェクトを作成する
// ------------------------------------------------------------
var bmp_data:BitmapData = (function():BitmapData{

	// BitmapData オブジェクトを作成する
	var bmp_data:BitmapData = new BitmapData( 256 , 256 , true , 0x00000000 );

	// イメージにパーリンノイズを描画する
	bmp_data.perlinNoise(
		64,
		64 ,
		1,
		Math.floor(Math.random() * 0x7FFFFFFF),
		true,
		false,
		(0),
		true
	);

	return bmp_data;
})();

// ------------------------------------------------------------
// Shader オブジェクトを作成する
// ------------------------------------------------------------
var shader_obj:Shader = (function():Shader{

	// 埋め込みアセットクラス
	[Embed( source="./filter.pbj" , mimeType="application/octet-stream" )]
	var MyByteCode:Class;

	// 埋め込んだファイルから、ByteArray オブジェクトを作成する (自作クラス "MyByteCode" )
	var byte_code:ByteArray = new MyByteCode();

	return new Shader(byte_code);
})();

(function():void{

	// ------------------------------------------------------------
	// ShaderData オブジェクトを取得する
	// ------------------------------------------------------------
	var shader_data:ShaderData = shader_obj.data;

	// ------------------------------------------------------------
	// ShaderParameter オブジェクトを取得する
	// ------------------------------------------------------------
	var parameter_alpha:ShaderParameter    = (shader_data["alpha"]) as ShaderParameter;
	var parameter_scroll:ShaderParameter   = (shader_data["scroll"]) as ShaderParameter;
	var parameter_src1Size:ShaderParameter = (shader_data["src1Size"]) as ShaderParameter;

	// ------------------------------------------------------------
	// ShaderInput オブジェクトを取得する
	// ------------------------------------------------------------
	var input_src1:ShaderInput = (shader_data["src1"]) as ShaderInput;

	// ------------------------------------------------------------
	// サポートチェック
	// ------------------------------------------------------------
	if(!(parameter_src1Size)) return;
	if(!(parameter_alpha)) return;
	if(!(parameter_scroll)) return;
	if(!(input_src1)) return;


	// ------------------------------------------------------------
	// 入力変数 "src1" に、BitmapData オブジェクトを渡す
	// ------------------------------------------------------------
	input_src1.input = bmp_data;

	// ------------------------------------------------------------
	// パラメータ変数 "src1Size" に、ビットマップのサイズを渡す
	// ------------------------------------------------------------
	parameter_src1Size.value = [ bmp_data.width , bmp_data.height ];

	// ------------------------------------------------------------
	// ShaderFilter オブジェクトを作成する
	// ------------------------------------------------------------
	var shader_filter:ShaderFilter = new ShaderFilter(shader_obj);

	var alpha:Number;
	var rad:Number = 0.0;

	// ------------------------------------------------------------
	// 毎フレーム実行されるイベント
	// ------------------------------------------------------------
	stage.addEventListener(Event.ENTER_FRAME , function (e:Event):void{

		// ------------------------------------------------------------
		// 透明度を変更
		// ------------------------------------------------------------
		rad += 10 * Math.PI / 180;
		alpha = (Math.sin(rad) + 1.0) * 0.5;
		alpha = alpha * 0.5 + 0.25;

		// ------------------------------------------------------------
		// 入力変数 "alpha" に、透明値を渡す
		// ------------------------------------------------------------
		parameter_alpha.value = [alpha];

		// ------------------------------------------------------------
		// 入力変数 "scroll" に、マウス位置を渡す
		// ------------------------------------------------------------
		parameter_scroll.value = [ -stage.mouseX , -stage.mouseY ];

		// ------------------------------------------------------------
		// フィルタを更新する
		// ------------------------------------------------------------
		shape.filters = [shader_filter];

	});

})();
 


 

算術演算の精密度を指定する

 


■算術演算の精密度を指定する

 
precisionHint プロパティを使用します。
 
ShaderPrecision.* 定数を指定します。
 
詳しい仕様は、公式リファレンスが参考になります。
 
http://help.adobe.com/ja_JP/FlashPlatform/reference/actionscript/3/flash/display/Shader.html#precisionHint
 
定数文字列説明
ShaderPrecision.FULL"full"デフォルトのモードです。
すべての環境で同じ結果が得られます。
ShaderPrecision.FAST"fast"より高速で動作するモードです。
CPU 環境によって結果が異なる場合があります。
 
シェーダーの算術演算の精密度を指定する

import flash.utils.ByteArray;
import flash.display.DisplayObject;
import flash.display.Shader;
import flash.display.ShaderPrecision;
import flash.filters.ShaderFilter;

// ------------------------------------------------------------
// 埋め込みアセットクラス
// ------------------------------------------------------------
[Embed( source="./sample.pbj" , mimeType="application/octet-stream" )]
var MyByteCode:Class;

// ------------------------------------------------------------
// 埋め込んだファイルから、ByteArray オブジェクトを作成する (自作クラス "MyByteCode" )
// ------------------------------------------------------------
var byte_code:ByteArray = new MyByteCode();

// ------------------------------------------------------------
// Shader オブジェクトを作成する
// ------------------------------------------------------------
var shader_obj:Shader = new Shader(byte_code);

// ------------------------------------------------------------
// 算術演算の精密度を指定する
// ------------------------------------------------------------
shader_obj.precisionHint = ShaderPrecision.FAST;

// ------------------------------------------------------------
// ShaderFilter オブジェクトを作成する
// ------------------------------------------------------------
var shader_filter:ShaderFilter = new ShaderFilter(shader_obj);

// ------------------------------------------------------------
// Document 表示オブジェクトに、フィルタを適用する
// ------------------------------------------------------------
var document_obj:DisplayObject = stage.getChildAt(0);
document_obj.filters = [shader_filter];