🌟

Unityシェーダー超入門⑩ グリッチアニメーション

2023/05/14に公開

今回はグリッチシェーダーを作ります


こんな感じのよくあるやつ
https://unity-chan.com/download/releaseNote.php?id=UnityChanImagePackVol1
素材のユニティちゃんは上記サイトからお借りしております。感謝。

では、始めていきましょう。コードはこんな感じです。

10_SimpleGlitch
Shader "Unlit/10_SimpleGlitch" {
    Properties {
        _MainTex ("Texture", 2D) = "white" {}
        _GlitchIntensity ("Glitch Intensity", Range(0,1)) = 0.1
        _BlockScale("Block Scale", Range(1,50)) = 10
        _NoiseSpeed("Noise Speed", Range(1,10)) = 10
    }   
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"

            struct appdata {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float _GlitchIntensity;
            float _BlockScale;
            float _NoiseSpeed;

            v2f vert (appdata v) {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            float random(float2 seeds)
            {
                return frac(sin(dot(seeds, float2(12.9898, 78.233))) * 43758.5453);
            }

            float blockNoise(float2 seeds)
            {
                return random(floor(seeds));
            }

            float noiserandom(float2 seeds)
            {
                return -1.0 + 2.0 * blockNoise(seeds);
            }

            fixed4 frag (v2f i) : SV_Target {
                float4 color;
                float2 gv = i.uv;
                float noise = blockNoise(i.uv.y * _BlockScale);
                noise += random(i.uv.x) * 0.3;
                float2 randomvalue = noiserandom(float2(i.uv.y, _Time.y * _NoiseSpeed));
                gv.x += randomvalue * sin(sin(_GlitchIntensity)*.5) * sin(-sin(noise)*.2) * frac(_Time.y);
                color.r = tex2D(_MainTex, gv + float2(0.006, 0)).r;
                color.g = tex2D(_MainTex, gv).g;
                color.b = tex2D(_MainTex, gv - float2(0.008, 0)).b;
                color.a = 1.0;

                return color;
            }
            ENDCG
        }
    }
}

処理全体の流れ

とりあえず、処理全体の流れを捉えていきましょう。
その前に、そもそもグリッチとは何なのかを理解した方がいいと思われます。
アドビさんの方で説明がなされていたのでリンクをご覧ください。
https://helpx.adobe.com/jp/photoshop/how-to/jp-1min-feature-ps-glitch.html#:~:text=グリッチとはテレビや,をご紹介します。
要約すると、画面の乱れを意図的に作るエフェクトの総称という感じでしょうか。
ですので、定義としてはかなり広い物と捉えてください。
今回はそんな中でもよくあるブロックタイプのグリッチノイズエフェクトを作ってみました。
では、処理の流れを説明します。

  1. ブロックノイズ関数
    ブロックノイズに関しては以前解説しましたね。
    ただ今回はy成分に倍数が設定されることがポイントです。

  2. UVのx成分に微量のノイズを加える
    無くても動作に影響はないです
    ただ、微量でも加えると仕上がりは良くなります。
    ここは作りたい物に合わせて判断でいいと思います。

  3. UV座標のアニメーション
    -1~1の範囲を取るようにし、グリッチのアニメーションをさせる。
    ここが結構大事な部分だと自分は思います。

  4. 各色チャネルの色をずらす
    色収差効果を生むためにやります。ここはすごく簡単です。

大きな流れは以上です。

ノイズの作成

まずこの二行から

10_SimpleGlitch
                float noise = blockNoise(i.uv.y * _BlockScale);
                noise += random(i.uv.x) * 0.3;

ブロックノイズに関しては以前やったので、特に説明する必要はないので省きます。
https://zenn.dev/umeyan/articles/0b2465ff399249
ブロックノイズ関数の戻り値に今回はランダム関数の処理を通していないだけです。
前回はアニメーションをさせるのに必要でしたが、今回は不要です。
そして、x成分にはブロックノイズ以外のノイズが欲しかったので
random関数を使い少し弱めて代入しています。

UV座標をずらす

まず、グリッチ効果の作られ方ですが
なんとなくイメージはついていると思いますが、テクスチャのUV座標が動く事で効果が生まれています。
それを行っているのは以下のコード

10_SimpleGlitch
float2 randomvalue = noiserandom(float2(i.uv.y, _Time.y * _NoiseSpeed));
                gv.x += randomvalue * sin(sin(_GlitchIntensity)*.5) * sin(-sin(noise)*.2) * frac(_Time.y);

ここで重要なのはnoiserandom関数の存在です!
noiserandom関数は以前紹介した、疑似乱数を作るrandom関数をカスタムしたものです。

10_SimpleGlitch
            float noiserandom(float2 seeds)
            {
                return -1.0 + 2.0 * blockNoise(seeds);
            }

一見すると凄く謎なことをしてると思いますよね、なんか無駄っていうか…
難しく考える必要はないです。
まず、random関数は[0~1]の値を返すと説明しましたね。
つまり、[-1~1]に乱数の範囲が変わっているだけなんです。
これによって左右へのアニメーションが可能となったわけです。
試しに、[0~1]の範囲で乱数を返すと露骨に結果が変わるので試してみることをお勧めします。

そして次にここ

10_SimpleGlitch
gv.x += randomvalue * sin(sin(_GlitchIntensity)*.5) * sin(-sin(noise)*.2) * frac(_Time.y);

正直な話、ここはもう個人の好みの領域の話になると思います。
シンプルにこれでも問題はないと思っていますし、パフォーマンス面で見ればこちらの方が良いです。

10_SimpleGlitch
gv.x += randomvalue * _GlitchIntensity) * noise frac(_Time.y);

使っている理由としては細かい内容ではありますが
周期性を微妙にずらしたり、貯め感を作ったりとよりグリッチぽさを出すために行っています。
グリッチの映像なんかを見たりして真似してみるのも面白いと思います!
正解がない故に、目的に応じて使い分けするのが良いと思いますね。
前に紹介したこちらで自分好みの周期を探したりするのも楽しいですよ。
https://fordhurley.com/glsl-grapher/

色収差

いよいよ最後です。特に難しいことはないです!
そもそも色収差とは…みたいな話をするとかなり長くなるのと、自分も語れる程の知識がありません。
ようはRGBが微妙にずれた表現と捉えて置くのが最初は良いと思います。
っで、何の意味があるの?って言われるとカッコよくなるから…って答えますかね…。
そしてグリッチの表現でもよく使われるからというのもありますね。
微妙にずらすかずらさないかで、明らかにルックの違いは出るのでやることをお勧めします!
さてコードの中身です

10_SimpleGlitch
                color.r = tex2D(_MainTex, gv + float2(0.006, 0)).r;
                color.g = tex2D(_MainTex, gv).g;
                color.b = tex2D(_MainTex, gv - float2(0.008, 0)).b;
                color.a = 1.0;

rとbの成分をずらしただけというのが良くわかりますね。
以上です!

グリッチは作ってる時はとても楽しいです。
これ以外にもいろんな種類のグリッチエフェクトは存在していますので、是非調べてみてください。
自分もまだ表現したいグリッチエフェクトがあるのでそれにも挑戦したい…!

10回目を終えて

思えば10回も記事を書けていた、最初は正直5,6回書いたら辞めるんじゃないかなと思っていた。
なんだかんだ面白くて続けれたのは良い傾向だと思う。当面の学習方法はまた写経と改変をメインにしつつ知識を増やしていきたいです。今月からwebGLのスクールにも参加したのでGLSLもやっていきたい。
Jsも触ったことなかったので、基礎中の基礎でもいいから持ち帰ってレベルを上げていきたい。
webGLの記事を書くのも楽しそう。

今後も出来る限り週1で更新しています!では!

Discussion