LoginSignup
4
1

More than 3 years have passed since last update.

【Godot】シェーダーの基本的な使い方

Last updated at Posted at 2021-02-12

概要

この記事では、Godot Engine でのシェーダーの基本的な使い方について説明します。

プロジェクトを作成

プロジェクトを作成して、2Dノードを作成し "Main" に名前を変更します。

スプライトを登録

"icon.png" をドラッグ&ドロップしてスプライトを作成します。
Godot_Engine_-_TestShader____.png

スプライト(ノード名は iconになっています)を選択して、インスペクタから CanvasItem > Material > [空] をクリックして、「新規 ShaderMaterial」を選びます。
Godot_Engine_-_TestShader____.png
次に、表示された Material をクリックして、Shader > [空] をクリックして、「新規Shader」を選びます。
Godot_Engine_-_TestShader_-_Main_tscn.png

最後に表示された「Shader」の文字をクリックすると、シェーダーエディタが開きます。
Godot_Engine_-_TestShader_-_Main_tscn____.png

シェーダーコードの入力

何もしないフラグメントシェーダーのコード

最低限のフラグメントシェーダーのコードは以下のとおりです。

// CanvasItemのシェーダーであることを宣言
shader_type canvas_item;

// フラグメントシェーダー
void fragment() {
    // テクスチャのUVに対応する色をピクセルに反映する
    COLOR = texture(TEXTURE, UV);
}

ここから簡単なシェーダーコードを書いていきます。

単色にするシェーダーコード

まずは単色に置き換えるシェーダーです。
Godot_Engine_-_TestShader_-_Main_tscn.png
このようにアルファ値以外を特定の色に置き換えます。

// CanvasItemのシェーダーであることを宣言
shader_type canvas_item;

// フラグメントシェーダー
void fragment() {
    // 色を取得
    vec4 color = texture(TEXTURE, UV);
    // アルファ値以外を置き換える
    color.rgb = vec3(1, 1, 1);
    // 反映
    COLOR = color;
}

色情報は vec4 で RGB成分は .rgb で取得できるので、その部分を vec3 で置き換えることで色を特定の色のみにすることができます。

2値化する

特定のしきい値を上回っているかそうでないかで色を白と黒にします。
Godot_Engine_-_TestShader_-_Main_tscn.png


// CanvasItemのシェーダーであることを宣言
shader_type canvas_item;

// フラグメントシェーダー
void fragment() {
    // 色を取得
    vec4 color = texture(TEXTURE, UV);

    // RGBの合計を求める
    float sum = color.r + color.g + color.b;
    if(sum > 1.0) {
        // しきい値を超えていたら色を白にする
        color.rgb = vec3(1, 1, 1);
    }
    else {
        // そうでない場合は黒にする
        color.rgb = vec3(0, 0, 0);
    }

    // 反映
    COLOR = color;
}

RGBの合計を求めて、一定の値(ここでは 1.0)を超えていれば白色、それ以下の場合は黒色としています。

変化がわかりにくいので素材を "icon.png" から以下の素材に変更します……。
escape.png

この画像を SpriteのTextureに割り当てると以下のようになります。
Godot_Engine_-_TestShader_-_Main_tscn____.png

エディタから値を変更できるようにする

このシェーダーに外部からパラメータを渡せるようにします。


// CanvasItemのシェーダーであることを宣言
shader_type canvas_item;

// しきい値
uniform float threshold;

// フラグメントシェーダー
void fragment() {
    // 色を取得
    vec4 color = texture(TEXTURE, UV);

    // RGBの合計を求める
    float sum = color.r + color.g + color.b;
    if(sum > threshold) {
        // しきい値を超えていたら色を白にする
        color.rgb = vec3(1, 1, 1);
    }
    else {
        // そうでない場合は黒にする
        color.rgb = vec3(0, 0, 0);
    }

    // 反映
    COLOR = color;
}

uniform float threshold; という値を宣言し、しきい値判定にはその値を使用するようにします。

次に、iconノードにスクリプトをアタッチして以下のように記述します。

icon.gd
# エディタでパラメータを反映できるようにする
tool

extends Sprite

# 値の範囲は 0.0〜3.0 とする
export(float, 0, 3) var threshold = 1.0

func _process(delta):
    # しきい値をシェーダーに渡す
    material.set_shader_param("threshold", threshold)

material.set_shader_param([パラメータ名], [値])でシェーダーにパラメータを渡すことができます。

またエディタ上で値を変更して反映できるように tool 宣言をしています。
おそらく シーンファイルを開き直さないと「tool」宣言は反映されない ので、シーンを保存して開き直します。
するとエディタ上で変更した値がシェーダーにリアルタイムで反映されるようになります。

shot.gif

UVスクロールするシェーダー

最後にUVスクロールを行うシェーダーです。
シェーダーに以下の記述をします。

// CanvasItemのシェーダーであることを宣言
shader_type canvas_item;

// UV値のオフセット値
uniform vec2 ofs_uv;

// フラグメントシェーダー
void fragment() {
    // 色を取得
    vec4 color = texture(TEXTURE, UV+ofs_uv);

    // 反映
    COLOR = color;
}

スクリプト側は以下のように記述します。

icon.gd
# エディタでパラメータを反映できるようにする
tool

extends Sprite

# 値の範囲は 0.0〜1.0 とする
export(float, 0, 1) var ofs_u = 0
export(float, 0, 1) var ofs_v = 0

func _ready():
    # テクスチャリピートを有効にする
    texture.flags = Texture.FLAG_REPEAT

func _process(delta):
    # UVオフセット値をシェーダーに渡す
    material.set_shader_param("ofs_uv", Vector2(ofs_u, ofs_v))

_ready() でテクスチャにリピート設定をしました。
この設定はシーンを再読み込みする必要があるので、シーンを保存して閉じ、読み込みし直します。

すると、エディタからUVスクロールを行うことができるようになります。
shot.gif

追記(2021.2.15):ラスタースクロール

ラスタースクロールのシェーダーを書いてみたので備忘録として残しておきます。
shot.gif

まずはスクリプト側

tool
extends Sprite

export(float, 0, 30) var time := 0.0

func _process(delta):
    # シェーダにパラメータを設定
    material.set_shader_param("time", time)

シェーダー側のコードです。

shader_type canvas_item;

uniform float time;

// ラスタースクロールする
void fragment() {
    float u = UV.x + 0.5 * sin(UV.y*time);
    vec2 uv = vec2(u, UV.y);
    COLOR = texture(TEXTURE, uv);
}

追記(2021.2.17):フォグシェーダー

フォグシェーダーの作り方を紹介している動画を見つけたので試しに作ってみました。

shot.gif

処理は今ひとつ理解できていませんが、以下のシェーダーでプロシージャルなフォグが実装できます。

shader_type canvas_item;

// フォグの色
//uniform vec3 color = vec3(0.33, 0.15, 0.82); // 紫
uniform vec3 color = vec3(0.33, 0.48, 0.95); // 青

// ズームインの回数
uniform int OCTAVES = 4;

// フォグの移動速度
uniform float speed = 0.5;

// アルファの倍率
uniform float alpha_rate = 0.5;

// 疑似乱数
float rand(vec2 coord) {
    // UVとの内積を求める
    float fdot = dot(coord, vec2(56, 78));

    float fsin = sin(fdot) * 1000.0;

    return fract(fsin * 1000.0);
}

// パーリンノイズ
float noise(vec2 coord) {
    // 端数切り捨て
    vec2 i = floor(coord);

    // 小数部を取り出す
    vec2 f = fract(coord);

    float a = rand(i);                  // 左上
    float b = rand(i + vec2(1.0, 0.0)); // 右上
    float c = rand(i + vec2(0.0, 1.0)); // 左下
    float d = rand(i + vec2(1.0, 1.0)); // 右下

    // CubeOut.
    vec2 cubic = f * f * (3.0 - 2.0 * f);

    // 小数部の4点から補間する
    return mix(a, b, cubic.x) + (c - a) * cubic.y * (1.0 - cubic.x) + (d - b) * cubic.x * cubic.y;
}

// フラクタル
float fbm(vec2 coord) {
    float value = 0.0;
    float scale = 0.5;

    // ズームイン
    for(int i = 0; i < OCTAVES; i++) {
        value += noise(coord) * scale;
        coord *= 2.0;
        scale *= 0.5;
    }

    return value;
}

void fragment() {
    vec2 coord = UV * 20.0;

    // 移動処理
    vec2 motion = vec2( fbm(coord + vec2(TIME * -speed, TIME * speed)) );

    // 最終的な値
    float final = fbm(coord + motion);

    COLOR = vec4(color, final * alpha_rate);
}

追記(2021.2.20):太らせるシェーダー

公式のサンプルを見ていたら面白いシェーダーを見つけたので紹介です。

shot.gif

shader_type canvas_item;

render_mode blend_mix;
uniform float fattyness : hint_range(0, 10) ;

void fragment() {
    vec2 ruv = UV - vec2(0.5, 0.5);
    vec2 dir = normalize(ruv);
    float len = length(ruv);

    len = pow(len * 2.0, fattyness) * 0.5;
    ruv = len * dir;

    vec4 col = texture(TEXTURE, ruv + vec2(0.5, 0.5));

    COLOR = col;
}

参考

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1