横着しようと AIに Unity 用シェーダーの Godot ポートをお願いしてみたところ、上手くいかなかった。
なので、手を動かして学習する。ここでは、Doodle エフェクト1を、Sprite2D
にかけてみる。(Godot 4.x)
最初に成果物。以下の画像のような感じ。(外部サイト投稿の apng なので表示・再生されないことがあるかもしれない)
書いたものは以下。
// I referred to the following article. // https://www.alanzucconi.com/2019/04/16/sprite-doodle-shader-effect/ shader_type canvas_item; uniform float doodle_factor = 0; uniform float doodle_scale : hint_range(1, 20, 0.05) = 5; uniform float doodle_snap : hint_range(0, 1, 0.05) = 0.25; uniform float noise_radius : hint_range(0, 0.1, 0.0005) = 0.0050; vec2 random2(vec2 p) { return fract(sin(vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3)))) * 43758.5453); } float snap(float x, float snap) { return snap * round(x / snap); } void vertex() { vec2 time = vec2(snap(TIME, doodle_snap), doodle_factor); vec2 noise = random2(VERTEX + time) * doodle_scale; VERTEX += noise; } void fragment() { vec2 noise = random2(UV) * noise_radius; COLOR = texture(TEXTURE, UV + noise); }
学習
ドキュメントは以下。日本語は翻訳が疎らなので英語にリンクする。
GLSL
ドキュメントによれば、シェーダー言語は GLSL ES 3.0。
Unity が HLSL なので、ポートしようとすると独自機能以外にここで引っかかる。
以下のような記事がある。
また、以下のような GLSL に慣れ親しんでいる人向けのガイドがあった。
CanvasItem shaders
CanvasItem shaders(2D シェーダー)は、シェーダー先頭でshader_type canvas_item;
を宣言する。
ドキュメントは以下。
uniform 変数
シェーダーパラメータとしてuniform
変数を宣言できる。
uniform float doodle_factor = 0; uniform float doodle_scale : hint_range(1, 20, 0.05) = 5; uniform float doodle_snap : hint_range(0, 1, 0.05) = 0.25; uniform float noise_radius : hint_range(0, 0.1, 0.0005) = 0.0050;
初期値を代入できるほか、エディタ用のヒントを付与することができる。
乱数
vec2 random2(vec2 p) { return fract(sin(vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3)))) * 43758.5453); }
例えばこの短いメソッドでも HLSL との間には違いがある。(frac
⇔ fract
、float2
⇔vec2
)
頂点シェーダー
float snap(float x, float snap) { return snap * round(x / snap); } void vertex() { vec2 time = vec2(snap(TIME, doodle_snap), doodle_factor); vec2 noise = random2(VERTEX + time) * doodle_scale; VERTEX += noise; }
(引数で渡される型ではなく、)各種のビルトインについて操作を行う。ここでは、VERTEX
にノイズを足した。
時間経過を利用した表現には、ビルトイン変数であるTIME
が利用できる。この効果では適度に間引いた間隔としたいので、一定間隔にスナップした値を使用した。
フラグメントシェーダー
void fragment() { vec2 noise = random2(UV) * noise_radius; COLOR = texture(TEXTURE, UV + noise); }
頂点シェーダーと同様に、各種ビルトインを用いる。ここでは、TEXTURE
・UV
・COLOR
を利用している。
なお、スクリーンを取得するSCREEN_TEXTURE
が Godot 4.x で廃止されており、Web上(例えば Godot Shaders など)にある例がコピペで動かない原因のひとつになっている。(エディタ上でどうしたらよいかの専用エラーメッセージが出てはくれる)
というわけで、冒頭のような 2D Doodle エフェクトシェーダーを作成することができた。
最初戸惑ったが(HLSLとの差異、ver. 3.x ⇨ 4.x による変更でWeb上のものを参考にするのにひと手間が必要になる場合がある、など)、手を動かすことで理解は進んだ。シェーダーはシェーダーそのものの沼が深いので、手前の部分はできれば戸惑わないようにしたい。
- 同様の効果を表す違う名前がいくつかあるみたいなので、馴染みのある名前に読み替えてください。↩