This translation is community contributed and may not be up to date. We only maintain the English version of the documentation. Read this tutorial in English
Shadertoy.com は、ユーザーが投稿した GL シェーダー(shader)を集めたサイトです。シェーダーコードやアイデアを探すのに役立ちます。このチュートリアルでは、Shadertoy のシェーダーを Defold で動作させます。シェーダーに関する基本的な知識があることを前提としています。予備知識が必要な場合は、シェーダーマニュアル から読み始めるとよいでしょう。
使用するシェーダーは、Pablo Andrioli 氏(Shadertoy でのユーザー名は「Kali」)の Star Nest です。数学の魔法のような手続き的な計算だけで、見事な星々のエフェクトを描画するフラグメントシェーダー(fragment shader)です。

このシェーダーは、わずか65行のかなり複雑な GLSL コードで構成されていますが、心配はいりません。いくつかの単純な入力に応じて処理するブラックボックスとして扱います。ここでの作業は、Shadertoy の代わりに Defold と連携するようシェーダーを変更することです。
Star Nest シェーダーはフラグメントシェーダーだけで構成されているので、シェーダーでテクスチャ(texture)を付ける対象さえあれば十分です。スプライト(sprite)、タイルマップ(tilemap)、GUI、モデル(model)など、いくつかの選択肢があります。このチュートリアルでは単純な 3D モデルを使用します。モデルの描画を簡単にフルスクリーンのエフェクトにできるためです。たとえば、画面にポストプロセスを適用する場合には、このような描画が必要になります。
空のプロジェクトから始められます。

builtins/assets/meshes にある組み込みの quad.gltf メッシュ(mesh)を使用できます。
必要に応じて、Blender などの 3D モデリングソフトで正方形の平面メッシュを作成することもできます。扱いやすいように、4つの頂点の座標は X 軸の -1 と 1、Y 軸の -1 と 1 に配置します。Blender ではデフォルトで Z 軸が上を向くので、メッシュを X 軸の周りに90°回転させる必要があります。また、メッシュの UV 座標が正しく生成されていることも確認してください。Blender でメッシュを選択した状態で Edit Mode に入り、Mesh ▸ UV unwrap... ▸ Unwrap を選択します。
Blender は無料のオープンソース 3D ソフトウェアで、blender.org からダウンロードできます。

quad.gltf を設定します。model.material を選択します。シーンエディターにモデルが表示されますが、全体が黒く描画されます。これは、まだテクスチャが設定されていないためです。

Assets ペインの main フォルダーを 右クリック し、New->Material を選択して star-nest と名付け、新しいマテリアルファイル star-nest.material を作成します。
star-nest.vp とフラグメントシェーダープログラム star-nest.fp を作成します。star-nest.vp を設定します。star-nest.fp を設定します。view_proj」と名付け、型を Viewproj(「ビュー投影」の意味)にします。
頂点シェーダープログラムのファイル star-nest.vp を開きます。次のコードが含まれているはずです。
#version 140
// positions are in world space
in vec4 position;
in vec2 texcoord0;
out vec2 var_texcoord0;
uniform vertex_inputs
{
mat4 view_proj;
};
void main()
{
gl_Position = view_proj * vec4(position.xyz, 1.0);
var_texcoord0 = texcoord0;
}
フラグメントシェーダープログラムのファイル star-nest.fp を開き、UV 座標(var_texcoord0)の X 座標と Y 座標に基づいてフラグメントの色を設定するようコードを変更します。これにより、モデルが正しく設定されていることを確認します。
#version 140
in vec2 var_texcoord0;
out vec4 out_fragColor;
void main()
{
out_fragColor = vec4(var_texcoord0.xy, 0.0, 1.0);
}
main.collection の star-nest ゲームオブジェクトにあるモデルコンポーネントの Material プロパティを、作成したばかりの star-nest マテリアルに設定します。
これでエディターは新しいシェーダーでモデルを描画するので、UV 座標が正しいかどうかを明確に確認できます。左下の角は黒 (0, 0, 0)、左上の角は緑 (0, 1, 0)、右上の角は黄 (1, 1, 0)、右下の角は赤 (1, 0, 0) になるはずです。

これでプロジェクトを実行できます(Project->Build、またはショートカットの Ctrl/Cmd + B)。しかし、黒い画面が表示されます(左下の角に小さな1ピクセルが見えるかもしれませんが、ほぼ真っ黒です)。これはカメラ(camera)がなく、デフォルトのレンダースクリプト(render script)が単純なフォールバックとして広大な 2D 空間を表示する一方、モデルは位置 (0,0,0) にあり、幅がわずか1しかないためです。
カメラコンポーネントを持つゲームオブジェクトを追加し、ゲームで見える範囲を定めます。
camera という名前のゲームオブジェクトを位置 (0,0,1) に追加します。(デフォルトの 2D 設定では Z 軸が手前を向いているので、このゲームオブジェクトがモデルの手前に来るよう、Z 座標を1に設定することが重要です)。Camera コンポーネントを追加すると、四角形が映ったカメラのプレビューが表示されます。この構成では幸い、デフォルトのプロパティを変更しなくても正しい結果が見えるはずです。ただし、カメラの視錐台をそこまで大きくする必要はないので、Far Z を 2 に減らせます。
必要に応じて、Orthographic Projection を true に設定してカメラの種類を変更し、Orthographic Zoom も600程度に調整できます。ただし、この場合はアスペクト比が自動で調整されないため、モデルが画面全体を埋めることはありません。
準備が整ったので、実際のシェーダーコードに取りかかります。まず、元のコードを見てみましょう。いくつかの部分で構成されています。

GLSL バージョン140を使用する新しいパイプラインを使います。そのため、ファイルの先頭で #version 140 によりバージョンを宣言します。
5–18行目では、いくつかの定数を定義しています。これらはそのままでかまいません。通常の GLSL 定数であり、Shadertoy や Defold に特有のものではないためです。
21行目と63行目には、入力フラグメントのスクリーン空間における X、Y テクスチャ座標(in vec2 fragCoord)と、出力フラグメントの色(out vec4 fragColor)があります。
Defold は、補間される変数を通じて、テクスチャ座標を頂点シェーダーからフラグメントシェーダーへ UV 座標(0–1の範囲)として渡します。今回の頂点シェーダーでは、これを out 修飾子で宣言しています。
// in star-nest.vp
out vec2 var_texcoord0;
フラグメントシェーダーでは、同じ値を in 修飾子で受け取ります。
// in star-nest.fp
in vec2 var_texcoord0;
次に、GLSL 140 では、out 修飾子を使ってフラグメント出力を明示的に宣言します。
// in star-nest.fp
out vec4 out_fragColor;
つまり、元の Shadertoy コードが fragColor に書き込むところを、Defold のシェーダーでは out_fragColor に書き込みます。
23–27行目では、テクスチャの寸法、移動方向、スケールを適用した時間を設定しています。Shadertoy では、シェーダーは fragCoord を通じてピクセル位置を受け取り、ビューポートまたはテクスチャの解像度は uniform vec3 iResolution として渡されます。シェーダーは、フラグメント座標と解像度から、正しいアスペクト比を持つ UV 形式の座標を計算します。見栄えのよい構図にするために、解像度に基づくオフセットも適用します。
Defold ではピクセル座標から計算を始めません。代わりに、頂点シェーダーから var_texcoord0 を通じて正規化された UV 座標を受け取っています。この座標は、描画する四角形全体で 0.0 から 1.0 の範囲になります。
Defold 版では、var_texcoord0 の UV 座標を使うようにこれらの計算を変更する必要があります。
一般的な変換は次のようになります。
vec2 uv = var_texcoord0.xy;
uv = uv * 2.0 - 1.0;
uv.x *= aspect;
aspect の具体的な値は、サンプルの構成によって異なります。既知の画面サイズでフルスクリーンの四角形にエフェクトを描画する場合は、このチュートリアルではアスペクト比を固定値としてコードに記述できます。任意のウィンドウサイズに対応する必要がある場合は、解像度をフラグメント定数として渡し、GLSL 140 のユニフォーム(uniform)ブロック内に配置します。
ここでは時間も設定します。時間は uniform float iGlobalTime としてシェーダーに渡されます。Defold は1.12.3以降、特別な Time 定数を通じてシェーダーに時間を提供しているので、これを使用します。
現在の Defold では、不透明型以外のユニフォームをユニフォームブロック内で宣言します。 フラグメントシェーダーでは、次のように宣言します。
uniform fragment_inputs
{
vec4 time;
};
次に、star-nest.material に time という名前の Fragment Constant を追加し、型を Time に設定します。
すると、その値を次のように使用できます。
float iGlobalTime = time.x;
float dt = time.y;
ここで、time.x はエンジン起動からの時間、time.y は前フレームからの経過時間です。
29–39行目では、ボリュームレンダリングの回転を設定し、マウス位置が回転に影響するようにしています。マウス座標は uniform vec4 iMouse としてシェーダーに渡されます。
このチュートリアルではマウス入力を省略します。
41–62行目がシェーダーの中心部分です。このコードはそのままにしておけます。
上記の各部分に必要な変更を加えると、次のシェーダーコードになります。読みやすくするために少し整理しています。Defold 版と Shadertoy 版の違いに注釈を付けています。
#version 140 // <1>
// Star Nest by Pablo Román Andrioli
// This content is under the MIT License.
#define iterations 17
#define formuparam 0.53
#define volsteps 20
#define stepsize 0.1
#define zoom 0.800
#define tile 0.850
#define speed 0.010
#define brightness 0.0015
#define darkmatter 0.300
#define distfading 0.730
#define saturation 0.850
in vec2 var_texcoord0; // <2>
out vec4 out_fragColor; // <3>
uniform fragment_inputs // <4>
{
vec4 time;
};
void main() // <5>
{
// get coords and direction
vec2 res = vec2(1.0, 1.0); // <6>
vec2 uv = var_texcoord0.xy * res.xy - 0.5;
vec3 dir = vec3(uv * zoom, 1.0);
float iGlobalTime = time.x; // <7>
float shader_time = iGlobalTime * speed;
float a1 = 0.5; // <8>
float a2 = 0.8;
mat2 rot1 = mat2(cos(a1), sin(a1), -sin(a1), cos(a1));
mat2 rot2 = mat2(cos(a2), sin(a2), -sin(a2), cos(a2));
dir.xz *= rot1;
dir.xy *= rot2;
vec3 from = vec3(1.0, 0.5, 0.5);
from += vec3(shader_time * 2.0, shader_time, -2.0);
from.xz *= rot1;
from.xy *= rot2;
// volumetric rendering
float s = 0.1;
float fade = 1.0;
vec3 v = vec3(0.0);
for (int r = 0; r < volsteps; r++) {
vec3 p = from + s * dir * 0.5;
// tiling fold
p = abs(vec3(tile) - mod(p, vec3(tile * 2.0)));
float pa = 0.0;
float a = 0.0;
for (int i = 0; i < iterations; i++) {
// the magic formula
p = abs(p) / dot(p, p) - formuparam;
// absolute sum of average change
a += abs(length(p) - pa);
pa = length(p);
}
// dark matter
float dm = max(0.0, darkmatter - a * a * 0.001);
a *= a * a;
// dark matter, don't render near
if (r > 6) {
fade *= 1.0 - dm;
}
v += fade;
// coloring based on distance
v += vec3(s, s * s, s * s * s * s) * a * brightness * fade;
fade *= distfading;
s += stepsize;
}
// color adjust
v = mix(vec3(length(v)), v, saturation);
out_fragColor = vec4(v * 0.01, 1.0); // <9>
}
フラグメントシェーダープログラムを保存します。これでシーンエディターでも実行時でも、モデルに星々のテクスチャが美しく描画されるはずです。

最後の仕上げは、時間を導入して星々を動かすことです。Defold は1.12.3以降、Time 型のフラグメント定数を通じて、この時間を自動的に提供します。
Time に設定します。
これだけです!フラグメントシェーダーでは、この time をすでに処理しています。これで完成です!
続きの楽しい演習として、元のマウス移動入力をシェーダーに追加してみましょう。新しい Fragment Constant を作成し、今回は型を User にする必要があります。マウスの移動を検出するスクリプトの on_input 内で、go.set() 関数を使って新しい定数に入力座標を設定し、更新します。
Defold を楽しんでください!