てくメモ

trivial な notes

【Godot Shader】2D Doodle エフェクトの作成

横着しようと AIに Unity 用シェーダーの Godot ポートをお願いしてみたところ、上手くいかなかった。

なので、手を動かして学習する。ここでは、Doodle エフェクト1を、Sprite2D にかけてみる。(Godot 4.x)


最初に成果物。以下の画像のような感じ。(外部サイト投稿の apng なので表示・再生されないことがあるかもしれない)

https://i.imgur.com/rnY5NVh.png

書いたものは以下。

// 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;

初期値を代入できるほか、エディタ用のヒントを付与することができる。

エディタからUI編集できる

乱数

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 との間には違いがある。(fracfractfloat2vec2

頂点シェーダー

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);
}

頂点シェーダーと同様に、各種ビルトインを用いる。ここでは、TEXTUREUVCOLORを利用している。

なお、スクリーンを取得するSCREEN_TEXTUREが Godot 4.x で廃止されており、Web上(例えば Godot Shaders など)にある例がコピペで動かない原因のひとつになっている。(エディタ上でどうしたらよいかの専用エラーメッセージが出てはくれる)


というわけで、冒頭のような 2D Doodle エフェクトシェーダーを作成することができた。

最初戸惑ったが(HLSLとの差異、ver. 3.x ⇨ 4.x による変更でWeb上のものを参考にするのにひと手間が必要になる場合がある、など)、手を動かすことで理解は進んだ。シェーダーはシェーダーそのものの沼が深いので、手前の部分はできれば戸惑わないようにしたい。



  1. 同様の効果を表す違う名前がいくつかあるみたいなので、馴染みのある名前に読み替えてください。