Shader モードの記述には OpenGL GLSL シェーダ言語をベースとした macOS/iOS のカスタムフィルターを記述するための言語 - Core Image Kernel Language - を使用します.ここでは Shader モードでの記述の要点と,本言語の概要を説明いたします.
Shader 欄には C 言語に似た文法で関数を最低1つ記述します. この関数は kernel キーワードから始まり,return で色や位置情報を返します. 元画像の1画素ごとにこの関数が呼ばれ,その結果,新たな画像が生成されます. 戻り値,引数の形式で,色変換,変形,汎用の3つのタイプの関数が定義できます. KansuImager ではそれぞれ,color,warp,convert という名称で定義します.
kernel vec4 color(__sample s){ return s; }
引数の型は __sample で(sampleの前は '_' 2つ), 処理位置での元画像の画素の色情報が渡されます. 戻り値の型は vec4(4元のベクトル)で,変換後の色情報を返します. この例では s をそのまま vec4 に変換して返します. すなわち,この例では元画像がコピーされるだけです.
kernel vec2 warp(){ vec2 p=destCoord(); return p; }
引数はありません. 返り値は元画像の位置です. この位置の画素値が,着目する変換後の位置にコピーされます. 通常,destCoord 関数で着目する変換後の画素の位置を取得し, これに従って変換前の画像の位置を計算し,返します. この例では変換前後の位置は一致するのでなにも変化は生じません.
kernel vec4 convert(sampler s){ vec2 p=destCoord(); vec4 q=samplerTransform(s,p); vec4 c=sample(s,q); return c; }
色変換と変形を同時行う場合に定義します. 引数は sampler 型で入力画像の任意の位置の画素値や画像サイズなどの情報が引き出せます. 返り値は着目位置での画素値です.
この例では,destCoord 関数で着目する変換後の画素位置を取得し(p), これを sampler の座標に変換して(q)さらに画素値を得ます(c). そのまま c を返すので結果的に何も生じません.
Core Image Kernel Language では,上記の3タイプの関数より多くの引数が指定可能ですが, 本 Shader モードでの記述では上記3タイプに限定します.
OpenGL GLSL では引数なしの関数 main を記述し,情報の入出力はグローバル変数の形式で行いますが, Core Image Kernel Language では引数あり任意名称の関数でシェーダを定義します.
KansuImager の独自の機能として, Pre タイミングで定義されたパラメータの値を参照することができます. 下記のように,パラメータの名称の前に $ または @ を付けます. $ は実数値,@ は整数値の定数としてコードに埋め込まれます.
// A, N をパラメータ名とする float r=$A; int n=@N; for(int i=0; i<@N; i++) { ...
int - 整数 float - 実数
数値の型には,整数の int,実数の float があります.
定数とし数値を記述する場合,小数点の有無で実数と整数が厳密に区別されることに注意して下さい.
float x=0.0; // OK float y=0; // エラー
異なる型の値を代入する場合には,型キャストを用います.
int i=1; float x=float(i);
const を用いて定数として定義もできます. for などの繰り返し処理はコンパイル時に繰り返し回数が決定している必要があるので, 繰り返し数を指定する変数の宣言に const を使用します.
const int n=5; for(int i=0; i<n; i++){...
bool - true または false.
論理値の型として bool が使用できます.
bool fg=true; bool judge=(x>1.0);
vec2 - x, y の2成分 vec3 - x, y, z の3成分 vec4 - x, y, z, w の4成分
ベクトル型(構造体)としては float 型の成分の数によって vec2,vec3,vec4 が定義されています. vec2 は画像の位置座標,vec3,vec4 は色情報など格納に用います.
なお,画像の座標系の原点は左下すみで,右がX軸正,上がY軸正方向です.
vec3,vec4 で色を格納する場合,x,y,z,w はそれぞれ,赤,緑,青,アルファ成分に対応します.
ベクトルの初期化や個々の成分へのアクセスは以下のようにします.
//xを1.0,yを2.0で初期化 vec2 v=vec2(1.0, 2.0); v.x=1.0; //x成分への代入 v.y=2.0; //y成分への代入 vec3 u=vec3(0.0); //x,y,z全て0.0で初期化 //x,y,zをuの値で,wを1.0で初期化 vec4 c=vec4(u,1.0);
ベクトル同士の演算が行えます.
vec2 p=destCoord(); // 現在の位置取得 vec2 q=p+vec2(1.0, 0.0); // 右隣の位置 q+=vec2(0.0, 1.0); // さらに上の位置
__sample - r, g, b, a の4成分
色変換関数の引数の型(構造体)として __sample 型が用いられます(sampleの前は '_' が2つ). float 型の r,g,b,a の4成分を持ち,それぞれ色の赤,緑,青,アルファ成分を表し,通常 0.0 から 1.0 までの範囲の値を格納します.なお a は不透明度を表し,0.0 で透明,1.0 は不透明,中間の値は半透明を表現します.
__sample の値は vec4 の変数に代入することができます.
// s を __sample 型変数とする. vec4 c=s; // vec4 へ代入 c.x=s.r; // r 成分を代入 c=s.rgba; // vec4 へ代入 c=s.bgra; // r と b の成分を交換して代入
sampler 型は画像情報を格納する型で,入力画像の情報の取得に用います. 汎用変換関数の引数の型として使用されます.
sampler 型変数から画素値(色情報)を取り出すには, 画像の位置座標を sampler のローカル座標に変換し, sampler 型変数と共にこれを sample 関数に渡す必要があります.
// s を sampler 型変数とする. vec2 p=destCoord(); // 現在の位置を取得 vec4 q=samplerCoord(s,p); // sampler の座標に変換 vec4 c=sample(s,q); // 画素値の取得
Core Image Shader Language では GLSLでは使用できる mat2, mat3, mat4, struct, arrays が使用できません. 詳しくは Core Image Kernel Language マニュアル を参照して下さい.
C言語と同様に if 文によって処理を分枝します.
if(x<0.0){ ... } else if(x<1.0){ ... } else { ... }
C言語と同様な構文で for, while による繰り返し処理が実行できます. ただし,繰り返し回数はコンパイル時に推定可能である必要があります. そこで,終了判定には定数または const 宣言された変数を用います.
また,continue,break はサポートされていません.
for(int i=0; i<5; i++){ ... または const int n=5; for(int i=0; i<n; i++){ ...
while(x<5.0){ ... または const float x1=5.0; while(x<x1){ ...
ここでは Core Image Kernel Language で定義された関数から抜粋して紹介します. 詳細については Core Image Kernel Language のマニュアル を参照して下さい.
destCoord() | 現在の位置 |
samplerTransform(s,p) | sampler座標系に変換 |
samplerCoord(s) | samplerの現在の位置 |
samplerSize(s) | 画像サイズ |
sample(s,p) | 位置 p の画素値 |
処理対象となっている画素の出力画像上の位置を返します.
vec2 p=destCoord();
位置を sampler 座標系に変換します.
// sampler s; vec2 p=destCoord(); vec2 q=samplerTransform(s,p);
現在の sampler の sampler 座標系での画素位置を取得します.
// sampler s; vec2 p=samplerCoord(s);
入力画像のサイズを取得します.
// sampler s; vec2 size=samplerSize(s);
なお,変形変換関数や色変換関数では sampler 変数が取得できないので, samplerSize 関数は使用できません. そこで,画像サイズを得るには,以下のように Pre タイミングのパラメータを定義し, $ または @ を用いて shader コードに埋め込んで下さい.
#テキストエディタでの記述例 $Pre width W #Wは定義済み変数 $Pre height H #Hは定義済み変数 $Shader kernel vec2 warp(){ vec2 size=vec2($width, $height); ...
位置 p の入力画像の画素値を取得します.
// sampler s; vec2 p=samplerCoord(s); vec4 c=sample(s,p);
ここでは OpenGL Shader Language (GLSL) で定義された関数から抜粋して紹介します. 詳細については Core Image Kernel Language のマニュアル からのリンクを参照するか, GLSL リファレンス(外部ページ) を参照して下さい.なお,Core Image Kernnel Language ではサポートしていない GLSL 関数や機能があることに注意して下さい.
GLSL には C 言語と同様に sin や cos といった豊富な数値演算関数が用意されています. これらについては上記マニュアルを参照して下さい. ここでは便利なベクトル関係の関数について紹介します.
length(v) | ベクトルの長さ |
distance(p,q) | 2点間の距離 |
normalize(v) | ベクトルの正規化 |
dot(u,v) | ベクトルの内積 |
equal(u,v) | ベクトルが等しいか |
ベクトルの長さを返します.
vec2 v=vec2(1.0,2.0); float L=length(v);
2点間の距離を返します.
vec2 p=destCoord(); float L=distance(p, vec2(100.0, 200.0));
ベクトルの長さを1にします.
vec2 p=destCoord(); vec2 n=normalize(p);
ベクトルの内積をとります.
vec2 p=destCoord(); const float d=1.0/1.41421356; float L=dot(p, vec2(d,d));
ベクトルが等しいか判定します.
if(equal(u,v)) { ...
Shader モードでの記述例を示します. 記述例はテキスト・フォーマットで,コードの一部を抜粋する形式で示します.
画像中央で左半面の画像を右半面に反転コピーさせます.
1:$Pre width W 2:$Mode Shader 3:$Shader 4:kernel vec2 warp(){ 5: vec2 p=destCoord(); 6: const float x0=$width/2.0; 7: if(p.x>x0){ 8: p.x=$width-p.x; 9: } 10: return p; 11:}
1行目
Pre 指定でパラメータ width を定義します.
値は定義済み変数 W(画像幅)を指定します.
2行目
Mode を Shader に指定します.
4行目
変形関数 warp を記述します.
5行目
着目している変換後の座標位置を取得し p に格納します.
6行目
Pre パラメータ width の値 $width を取得し,
2.0 で除して定数 x0 に格納します.
7-9行目
変換後のX位置 p.x と x0 を比較し,
p.x>x0,すなわち右半面の位置にある場合,
p.x を画像右端からの距離に置き換えます.
10行目
ベクトル p を返します.
変形関数の返り値は参照する入力画像の位置を指定します. 上記の処理では p が左半面にある場合, p をそのまま返すので出力画像は入力画像と一致します. p が右半面にある場合,p を対称位置に変更するので, 入力画像の左側の対称位置の値がコピーされます.