最初のキャンバスアイテム(CanvasItem)シェーダー

はじめに

シェーダーは、GPUで実行される特別なプログラムで、グラフィックのレンダリングに使用されます。 最新のレンダリングはすべてシェーダーで行われます。 シェーダーの詳細については、シェーダーとは を参照してください。

このチュートリアルでは、頂点関数とフラグメント関数の両方を使用してシェーダーを作成するプロセスを順を追って説明し、シェーダープログラムを作成する実用的な側面に焦点を当てます。 このチュートリアルは、シェーダーの初心者を対象にしています。

注釈

シェーダーの作成経験があり、Godotでのシェーダーの動作の概要を探しているだけの場合は、シェーディングリファレンス を参照してください。

セットアップ

CanvasItem シェーダーはGodotのすべての2Dオブジェクトを描画するために使用され、Spatial シェーダーはすべての3Dオブジェクトを描画するために使用されます。

シェーダーを使用するには、オブジェクトにアタッチする必要がある Material 内にアタッチする必要があります。 マテリアルは Resource の一種です。 同じマテリアルで複数のオブジェクトを描画するには、マテリアルを各オブジェクトにアタッチする必要があります。

CanvasItem から派生したすべてのオブジェクトには、マテリアルプロパティがあります。 これには、すべての GUI要素スプライトタイルマップMeshInstance2D などがあります。オプションもあります。 親のマテリアルを継承します。 これは、同じマテリアルを使用するノードが多数ある場合に役立ちます。

ではまず、スプライトノードを作成します。 任意のCanvasItemを使用できますが、このチュートリアルではSpriteを使用します。

インスペクタで、"Texture"の横にある[空]をクリックして[読込み]を選択し、”Icon.png"を選択します。 新しいプロジェクトの場合、これはGodotアイコンです。 ビューポートにアイコンが表示されます。

次に、インスペクタのCanvasItemセクションの下を見て、「Material」の横をクリックし、「新規 ShaderMaterial」を選択します。 これにより、新しいマテリアルリソースが作成されます。 表示される球をクリックします。 Godotは現在、CanvasItemシェーダーとSpatialシェーダーのどちらを記述しているかを認識せず、Spatialシェーダーの出力をプレビューします。 したがって、表示されているのは、デフォルトのSpatialシェーダーの出力です。

"Shader"の横をクリックし、「新規 Shader」を選択します。 最後に、新しいシェーダーリソースをクリックすると、シェーダーエディタが開きます。 これで、最初のシェーダーを書き始める準備ができました。

最初のキャンバスアイテム(CanvasItem)シェーダー

Godotでは、すべてのシェーダーは、シェーダーのタイプを指定する行で始まります。 次の形式を使用します:

shader_type canvas_item;

CanvasItemシェーダーを記述しているため、最初の行で canvas_item を指定します。 すべてのコードは、この宣言の下に配置されます。

この行は、どの組み込み変数と関数を提供するかをエンジンに伝えます。

Godotでは、3つの関数をオーバーライドして、シェーダーの動作を制御できます。vertexfragment、および light。 このチュートリアルでは、頂点(vertex)関数とフラグメント(fragment)関数の両方を使用してシェーダーを作成する方法を説明します。 ライト(light)関数は、頂点関数やフラグメント関数よりもかなり複雑なので、ここでは説明しません。

最初のフラグメント関数

フラグメント関数は、Sprite内のすべてのピクセルに対して実行され、そのピクセルの色を決定します。

これらはSpriteでカバーされるピクセルに制限されているため、Spriteの周囲にアウトラインを作成する場合などは使用できません。

最も基本的なフラグメント関数は、すべてのピクセルに単一の色を割り当てること以外は何もしません。

そのためには、組み込み変数 COLORvec4 を書き込みます。vec4 は、4つの数値でベクトルを構築するための省略表現です。 ベクトルの詳細については、ベクトル演算チュートリアル <doc_vector_math>`を参照してください。``COLOR` はフラグメント関数への入力変数であり、フラグメント関数からの最終出力です。

void fragment(){
  COLOR = vec4(0.4, 0.6, 0.9, 1.0);
}
../../../_images/blue-box.png

おめでとう! できました。 Godotで最初のシェーダーの作成に成功しました。

それでは、もっと複雑にしましょう。

COLOR の計算に使用できるフラグメント関数には多くの入力があります。UV もその1つです。 UV座標は(知らないうちに)スプライトで指定され、メッシュの各部分のテクスチャから読み取る場所をシェーダーに伝えます。

フラグメント関数では UV からしか読み取ることができませんが、他の関数で使用したり、値を COLOR に直接割り当てることができます。

UV は、左から右、上から下で0~1の間で変化します。

../../../_images/iconuv.png
void fragment() {
  COLOR = vec4(UV, 0.5, 1.0);
}
../../../_images/UV.png

TEXTURE ビルトインを使用する

スプライトの色を調整する場合、以下のコードのようにテクスチャの色を手動で調整することはできません。

void fragment(){
  //this shader will result in an all white rectangle
  COLOR.b = 1.0;
}

デフォルトのフラグメント関数は、テクスチャから読み取り、それを表示します。デフォルトのフラグメント関数を上書きすると、その機能が失われるため、自分で実装する必要があります。 texture 関数を使用してテクスチャから読み取ります。スプライトのような特定のノードには、TEXTURE を使用してシェーダーでアクセスできる専用のテクスチャー変数があります。スプライトを描画するには、UV および texture を一緒に使用します。

void fragment(){
  COLOR = texture(TEXTURE, UV); //read from texture
  COLOR.b = 1.0; //set blue channel to 1.0
}
../../../_images/blue-tex.png

Uniform入力

Uniform入力は、シェーダー全体で同一のデータを、シェーダーに渡すために使用されます。

Uniformを使用するには、シェーダーの上部で次のように定義します:

uniform float size;

使用法の詳細については、シェーディング言語ドキュメント を参照してください。

uniformを追加して、スプライトの青の量を変更します。

uniform float blue = 1.0; // you can assign a default value to uniforms

void fragment(){
  COLOR = texture(TEXTURE, UV); //read from texture
  COLOR.b = blue;
}

これで、エディタからスプライトの青の量を変更できます。シェーダーを作成した場所のインスペクタを再確認すると、``Shader Param`` というセクションが表示されるはずです。そのセクションを展開すると、宣言したuniformが表示されます。エディタで値を変更すると、シェーダーで指定したデフォルト値が上書きされます。

コードからシェーダーを操作する

ノードのマテリアルリソースで呼び出される set_shader_param() 関数を使用して、コードからuniformを変更できます。 Spriteノードでは、次のコードを使用して blue uniformを設定できます。

var blue_value = 1.0
material.set_shader_param("blue", blue_value)

uniformの名前は文字列であることに注意してください。文字列は、スペルや大文字小文字を含め、シェーダーでの記述方法と正確に一致する必要があります。

最初の頂点関数

フラグメント関数ができたので、頂点関数を書きましょう。

頂点関数を使用して、各頂点が画面のどこに出現するかを計算します。

頂点関数の最も重要な変数は VERTEX です。最初はモデルの頂点座標を指定しますが、実際にそれらの頂点を描画する場所を決定するために書き込むこともできます。VERTEX は、ローカル空間で最初に表示される vec2 です(つまり、カメラ、ビューポート、または親ノードに関連しません)。

VERTEX に直接追加することにより、頂点をオフセットできます。

void vertex() {
  VERTEX += vec2(10.0, 0.0);
}

TIME 組み込み変数と組み合わせて、これを単純なアニメーションに使用できます。

void vertex() {
  // Animate Sprite moving in big circle around its location
  VERTEX += vec2(cos(TIME)*100.0, sin(TIME)*100.0);
}

結論

コアでは、シェーダーはこれまで見てきたことを実行し、VERTEXCOLOR を計算します。これらの変数に値を割り当てるためのより複雑な数学的戦略を考え出すのはあなた次第です。

インスピレーションを得るには、Shadertoy`The Book of Shaders <https://thebookofshaders.com>`_などのより高度なシェーダーチュートリアルをいくつか見てください。