【Godot】ディザ抜きシェーダで半透明を表現する

f:id:ueshita:20201206133700g:plain

ディザ抜きと呼ばれる手法を使うと半透明の表現ができます。

半透明といえばアルファブレンドですが、最近のゲームではアルファブレンドの代わりとしてディザ抜きがよく用いられています。

ディザ抜き

ディザ抜きはアルファブレンドに比べて若干ジャギジャギ感はありますが、次のメリットがあります。

  • 描画コストが軽い(不透明レンダリングなので)
  • 描画順を考えなくていい
  • 重ねた時に破綻しない
  • 内部のポリゴンが透けて見えない

Godotでディザ抜きをする

Godotでディザ抜きする方法を解説します。

BayerMatrix画像を用意する

ディザ抜きにはBayerMatrixというものを使用します。

0/168/162/1610/16
12/164/1614/166/16
3/1611/161/169/16
15/167/1613/165/16

このパラメータをグレイスケール化した4x4の小さな画像テクスチャを用意します。

f:id:ueshita:20201206134015p:plain

テクスチャのインポート設定を次のように設定します。

  • Compress ModeをLosslessにする
  • FilterをOFFにする

f:id:ueshita:20201206193755p:plain

ディザ抜きシェーダを書く

次にディザ抜きするシェーダを書きます。

  • フラグメントシェーダでBayerMatrixテクスチャをFRAGCOORDで参照
  • dither値とAlpha値と比較して抜く場合はdiscard構文でフラグメント処理を打ち切る
shader_type spatial;

uniform sampler2D Texture;  // モデルのテクスチャ
uniform sampler2D Bayer;    // 4x4のBayerMatrix画像
uniform float Alpha = 1.0;  // アルファパラメータ (0.0~1.0)

void fragment() {
    float dither = texture(Bayer, FRAGCOORD.xy * 0.25).r;
    if (dither >= Alpha)
        discard;
    ALBEDO = texture(Texture, UV).rgb;
}

シェーダパラメータをスクリプトから設定する

AlphaパラメータをShaderMaterialに設定します。 (TextureとBayerは事前にセットしておきます。)

extends MeshInstance

const DURATION = 1.0
var alpha = 0.0
var fade_mode = 0

func _process(delta):
    # スペースキーを押すとフェードアニメーションを開始
    if Input.is_action_just_pressed("ui_accept"):
        if fade_mode == 0:
            if alpha < 0.5:
                fade_mode = 1
            else:
                fade_mode = -1

    if fade_mode < 0:
        alpha -= delta / DURATION
        if alpha <= 0.0:
            alpha = 0.0
            fade_mode = 0
            
    if fade_mode > 0:
        alpha += delta / DURATION
        if alpha >= 1.0:
            alpha = 1.0
            fade_mode = 0

    # ShaderMaterialに値をセット
    var mat = material_override as ShaderMaterial
    mat.set_shader_param("Alpha", alpha)

参考記事