🌈

そろそろShaderをやるパート39 グリッチによるホログラムっぽい表現

2021/08/10に公開

そろそろShaderをやります

そろそろShaderをやります。そろそろShaderをやりたいからです。
パート100までダラダラ頑張ります。10年かかってもいいのでやります。
100記事分くらい学べば私レベルの初心者でもまあまあ理解できるかなと思っています。

という感じでやってます。

※初心者がメモレベルで記録するので
 技術記事としてはお力になれないかもしれません。

下準備

下記参考
そろそろShaderをやるパート1 Unite 2017の動画を見る(基礎知識~フラグメントシェーダーで色を変える)

デモ

いろいろ組み合わせてホログラムのような表現を作ってみました。

Shaderサンプル

Shader "Custom/Holo"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _LineColor ("LineColor", Color) = (0,0,0,0)
        _LineSpeed("LineSpeed",Range(0,10)) = 5
        _LineSize("LineSize",Range(0,1)) = 0.01
        _ColorGap("ColorGap",Range(0,1.0)) = 0.01
        _Alpha ("Alpha", Range(0,1)) = 0.5
        _FrameRate ("FrameRate", Range(0,30)) = 15
        _Frequency  ("Frequency", Range(0,1)) = 0.1
        _GlitchScale  ("GlitchScale", Range(1,10)) = 1
    }
    SubShader
    {
        Tags
        {
            "RenderType"="Tranparent"
        }
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

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

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

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _LineColor;
            float _LineSpeed;
            float _LineSize;
            float _ColorGap;
            float _Alpha;
            float _FrameRate;
            float _Frequency;
            float _GlitchScale;

            //ランダムな値を返す
            float rand(float2 co) //引数はシード値と呼ばれる 同じ値を渡せば同じものを返す
            {
                return frac(sin(dot(co.xy, float2(12.9898, 78.233))) * 43758.5453);
            }

            //パーリンノイズ
            float perlinNoise(fixed2 st)
            {
                fixed2 p = floor(st);
                fixed2 f = frac(st);
                fixed2 u = f * f * (3.0 - 2.0 * f);

                float v00 = rand(p + fixed2(0, 0));
                float v10 = rand(p + fixed2(1, 0));
                float v01 = rand(p + fixed2(0, 1));
                float v11 = rand(p + fixed2(1, 1));

                return lerp(lerp(dot(v00, f - fixed2(0, 0)), dot(v10, f - fixed2(1, 0)), u.x),
                            lerp(dot(v01, f - fixed2(0, 1)), dot(v11, f - fixed2(1, 1)), u.x),
                            u.y) + 0.5f;
            }

            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                //UVスクロール
                o.line_uv.y = v.line_uv.y - _Time.z * _LineSpeed;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                float2 uv = i.uv;
                //RGBずらしてホログラムっぽく
                float r = tex2D(_MainTex, uv + _ColorGap * perlinNoise(_Time.z)).r;
                float b = tex2D(_MainTex, uv - _ColorGap * perlinNoise(_Time.z)).b;
                float2 ga = tex2D(_MainTex, uv).ga;
                float4 shiftColor = fixed4(r, ga.x, b, ga.y);
                //ノイズラインの補間値計算
                float interpolation = step(frac(i.line_uv.y * 15), _LineSize);
                //ノイズラインを含むピクセルカラー
                float4 noiseLineColor = lerp(shiftColor, _LineColor, interpolation);
                float posterize = floor(frac(perlinNoise(frac(_Time)) * 10) / (1 / _FrameRate)) * (1 / _FrameRate);
                //uv.y方向のノイズ計算 -1 < random < 1
                float noiseY = 2.0 * rand(posterize) - 0.5;
                //グリッチの高さの補間値計算 どの高さに出現するかは時間変化でランダム
                float glitchLine1 = step(uv.y - noiseY, rand(uv));
                float glitchLine2 = step(uv.y - noiseY, 0);
                float glitch = saturate(glitchLine1 - glitchLine2);
                //uv.x方向のノイズ計算 -0.1 < random < 0.1
                float noiseX = (2.0 * rand(posterize) - 0.5) * 0.1;
                float frequency = step(abs(noiseX),_Frequency);
                noiseX *= frequency;
                //グリッチ適用
                uv.x = lerp(uv.x, uv.x + noiseX * _GlitchScale, glitch);
                float4 noiseColor = tex2D(_MainTex, uv);
                float4 finalColor = noiseLineColor * noiseColor;
                //アルファ操作
                finalColor.a = _Alpha;
                return finalColor;
            }
            ENDCG
        }
    }
}

今回のShaderでは下記の要素を組み合わせてホログラムのような表現を実現しています。
・グリッチ
・スクロールする線
・RGBシフト
・透過

グリッチは過去記事で解説しています。
そろそろShaderをやるパート38 グリッチ表現

スクロールする線ですが、ホログラム特有の映像がスキャンしているようなイメージを加えたかったのが採用した理由です。(約束のネバーランドに出てくるホログラムのイメージ)

誇張したものが下記です。この線を細く、高速で動かすといい感じになります。
ゆっくり太い線を動かしてもいいかもしれません。この辺りはお好みで。

こちらも過去記事で解説済みです。
そろそろShaderをやるパート12 線を描画してスクロールさせる

透過はシンプルにアルファを操作するだけです。
適切なタグとBlendingの記述をしないと透過しないので要注意です。

    Tags
    {
        "RenderType"="Tranparent"
    }
    Blend SrcAlpha OneMinusSrcAlpha

そろそろShaderをやるパート4 Unite 2017の動画を見る(プロパティ、透明度)

RGBシフト

今回初めてRGBシフトなるものの存在を知って試しました。
RBだけをずらします。Gはそのままなので、いい感じに色味がずれてホログラムっぽい映像の不完全な表現になりました。今回はパーリンノイズでずらしました。

    //RGBずらしてホログラムっぽく
    float r = tex2D(_MainTex, uv + _ColorGap * perlinNoise(_Time.z)).r;
    float b = tex2D(_MainTex, uv - _ColorGap * perlinNoise(_Time.z)).b;
    float2 ga = tex2D(_MainTex, uv).ga;
    float4 shiftColor = fixed4(r, ga.x, b, ga.y);

大げさにずらすとこうなります。

参考リンク

かんたんエフェクト加工シェーダー
【Unity(C#)】PerlinNoiseで電球切れ表現
【Unityシェーダ入門】シェーダで作るノイズ5種盛り
【Unity】ShaderGraphでポスタライズやモザイクを表現する(Posterizeノード)
【シェーダーグラフメモ その45】シンプルなGlitchエフェクト

Discussion