Skiaでは、シェーダーを使用して様々なグラフィックス効果を実現できます。非常に奥の深い強力な機能ですが、ここではその基本について説明します。
シェーダーの種類
Skiaには、以下のようなシェーダーが用意されています。
- グラデーションシェーダー: 線形または放射状のグラデーションを描画するためのシェーダーです。
- 画像シェーダー: 画像をテクスチャとして使用し、描画するためのシェーダーです。
- ノイズシェーダー: Perlinノイズによるパターンを生成するためのシェーダーです。
- カスタムシェーダー: シェーダー記述言語(SkSL)を使用して独自のシェーダーを作成できます。
とりあえず描いてみる
とりあえず、一番簡単なグラデーションシェーダーで、線形グラデーションを描画してみましょう。
座標(0,0)から(200,0)までの範囲で、赤、緑、青の3色のグラデーションを描画します。
シェーダーはアンマネージドリソースです。使い終わったら適切に破棄してください。
// 線形グラデーションシェーダーを作成
using var shader = SKShader.CreateLinearGradient(
new SKPoint( 0, 0 ), // 開始座標
new SKPoint( 200, 0 ), // 終了座標
new SKColor[] { // 色経由点の色の配列
SKColors.Red,
SKColors.Lime,
SKColors.Blue
},
new float[] { // 色経由点の位置の配列(0.0~1.0)
0.1f, // 10%の位置にRed
0.5f, // 50%の位置にLime
0.9f // 90%の位置にBlue
},
SKShaderTileMode.Clamp // タイルモード
);
// ペイントを作成
using var paint = new SKPaint {
Style = SKPaintStyle.Fill,
Shader = shader
};
// 四角形を描画
canvas.DrawRect( new SKRect( 0, 0, 200, 50 ), paint );
実行すると、以下のように、左上にグラデーションが描画されます。
シェーダーと座標
先程のグラデーションの四角形を、真ん中にもう一つ描画してみましょう。
// 四角形を描画
canvas.DrawRect( new SKRect( 0, 0, 200, 50 ), paint );
// もう一つ描画
canvas.DrawRect( new SKRect( 50, 75, 250, 125 ), paint );
実行すると、以下のように、真ん中にもう一つ四角形が描画されますが、グラデーションの位置がずれていることがわかります。
これは、シェーダーがキャンバス全体の座標系に基づいてグラデーションを計算しているためです。
シェーダーの座標系を変更して、グラデーションの位置を調整するには、以下の二つの方法があります。
シェーダーのローカル座標系を使用する
シェーダーのローカル座標系を使用して、グラデーションの位置を調整することができます。これには、座標変換行列を渡してシェーダーを作成する方法と、既存のシェーダーを元に変換行列を適用する方法があります。
// シェーダーを作成する際に変換行列を渡す方法
using var shaderMoved = SKShader.CreateLinearGradient(
new SKPoint( 0, 0 ),
new SKPoint( 200, 0 ),
new SKColor[] {
SKColors.Red,
SKColors.Lime,
SKColors.Blue
},
new float[] {
0.1f,
0.5f,
0.9f
},
SKShaderTileMode.Clamp,
SKMatrix.CreateTranslation( 50, 75 ) // ローカル座標系のオフセットを設定
);
// 既存のシェーダーを元に変換行列を適用する方法
using var shaderMoved = shaderOrigin.WithLocalMatrix( SKMatrix.CreateTranslation( 50, 75 ) );
キャンバスの座標系を変更する
もう一つの方法は、キャンバスの座標系を変更することです。これには、キャンバスのTranslateメソッドを使用して、描画前にキャンバスの座標系を移動しておきます。
// 四角形を描画
canvas.DrawRect( new SKRect( 0, 0, 200, 50 ), paint );
// キャンバスの座標系を移動
canvas.Translate( 50, 75 );
// もう一つ描画
canvas.DrawRect( new SKRect( 0, 0, 200, 50 ), paint );
いずれの方法も、実行すると、以下のように、真ん中にもう一つ四角形が描画され、グラデーションの位置も一致します。
カスタムシェーダー
深淵の世界へようこそ。
Skiaでは、SkSL(Skia Shading Language)を使用してカスタムシェーダーを作成できます。SkSLは、GLSLに似たシェーダー記述言語で、Skiaのレンダリングパイプラインで使用されます。
あまりにも奥が深いので、ここでは簡単な例だけ紹介します。
// SKSLコード
string sksl = @"
// メイン関数
half4 main(float2 coord) {
float red = coord.x / 300.0; // 横方向に赤が強くなる
float green = 0.5; // 緑は一定
float blue = coord.y / 200.0; // 縦方向に青が強くなる
return half4( red, green, blue, 1.0 ); // 結果をRGBA形式にまとめて返す
}
";
// シェーダーをコンパイル
var effect = SKRuntimeEffect.CreateShader( sksl, out var errorText );
// コンパイルに失敗した場合、errorTextにエラーメッセージが入る
if ( effect == null ) {
Debug.WriteLine( errorText );
return;
}
// シェーダーを生成
using var shader = effect.ToShader();
// ペイントを作成
using var paint = new SKPaint {
Style = SKPaintStyle.Fill,
Shader = shader
};
// 四角形を描画
canvas.DrawRect( new SKRect( 0, 0, 300, 200 ), paint );
実行すると、以下のように、二次元のグラデーションが描画されます。
画像を使用して、座標を変化させるなど、様々な応用が可能です。
// SKSLコード
string sksl = @"
// 元画像のシェーダーを受け取るユニフォーム変数
uniform shader src;
// メイン関数
half4 main(float2 coord) {
// 横方向に波打つ関数
float s = 0.5 * ( 1 + sin( coord.x * 0.5 ) );
// Y座標を変位させる
coord.y += s * 5.0;
// 元画像の色をsrcシェーダーから取得
half4 c = src.eval( coord );
// 色を少し変える(緑を強調)
c += half4( 0, 0.2, 0, 0 );
// 結果を返す
return c;
}
";
// シェーダーをコンパイル
var effect = SKRuntimeEffect.CreateShader( sksl, out var errorText );
// コンパイルに失敗した場合、errorTextにエラーメッセージが入る
if ( effect == null ) {
Debug.WriteLine( errorText );
return;
}
// ユニフォーム変数(今回は特に使わない)
using var uniforms = new SKRuntimeEffectUniforms( effect );
// 子シェーダー設定
using var children = new SKRuntimeEffectChildren( effect );
using var srcShader = bmpSrc.ToShader();
children[ "src" ] = srcShader; // 元画像のシェーダー"src"
// シェーダー生成
using var shader = effect.ToShader( uniforms, children );
// ペイントを作成
using var paint = new SKPaint {
Style = SKPaintStyle.Fill,
Shader = shader
};
// 円を描画
canvas.DrawCircle( 150, 100, 80, paint );
実行すると、以下のように、画像が波打つように変形されて描画されます。
まとめ
Skiaのシェーダーは非常に強力で、多様なグラフィックス効果を実現できます。基本的なグラデーションシェーダーから始めて、カスタムシェーダーまで、段階的に学んでいくことをお勧めします。シェーダーの座標系や変換についても理解を深めることで、より高度な描画が可能になります。
参考資料
総合目次




