渋谷ほととぎす通信

「Unityをわかりやすく」初心者のためのゲーム作りブログ

【Unity】画面がスクロールするポストエフェクトの作り方

【Unity】画面がスクロールするポストエフェクトの作り方

こんにちは、エンジニアのオオバです。

この記事では画面が縦にスクロールするポストエフェクトをUnityで作ります。具体的には以下のような表現です。

【Unity】画面がスクロールするポストエフェクトの作り方_0

このスクロール表現を作る上重要なのは 「 frac関数 」です。frac関数はシェーダーを作る上でよく出てくるテクニック。ぜひこの記事を通してスクロール表現、fracを覚えてみてください。

シェーダーが何かよくわからない方は、こちらの記事も合わせて読んでみることをおすすめします。。

👉DOTweenの教科書を読んでUnityアニメーションをプログラミングしてみよう!

fracとは小数だけ残す関数

最初に今回の肝である「frac関数」について解説します。 frac関数は渡されたパラメーターの「小数部分」だけを返却します。

float value = frac(1.119);  

例えばこのようなソースコードの場合、変数valueには「0.119」が代入されます。では、この「frac関数」をどのようにしてスクロール表現に活用するのか解説していきます。

ポストエフェクトでスクロール表現の作り方

ここからは具体的にスクロール表現を作っていきます。大きく2つの手順を踏みます。まずはシェーダーを作成し、その次にC#側からシェーダーに値を送る部分を作ります。

手順①スクロール表現のシェーダーを作成

スクロール表現のシェーダーのソースコードから見ていきます。次のコードはフラグメントシェーダーです。

sampler2D _MainTex;  
float _ScrollValue;  

fixed4 frag (v2f_img i) : SV_Target  
{
    // UV座標をUとVで分解  
    float u = i.uv.x;  
    float v = i.uv.y;  
    // fracを使って小数点を取り出し、小数点部分だけ「V座標」をオフセットする  
    v = frac(v + _ScrollValue);  
    return tex2D(_MainTex, float2(u, v));  
}

重要な部分は「 v = frac(v + _ScrollValue); 」この1行です。 UV座標をUとVに分解 し、Vだけ「_ScrollValue」を加算するのです。加算した値をfrac関数で小数点だけ取り出しています。

つまり、_ScrollValueの値分、縦に画面をずらすことができるのです。

📚 参考サイト : frac - Win32 apps | Microsoft Docs

UV座標についてはこちらの記事で詳しく解説していますので、あわせて読んでみてください。

手順②C#側からスクロールする値をシェーダーにセット

C#側の役割はシェーダーに対してスクロール値をセットすることです。シェーダー内では「_ScrollValue」と宣言しているので、その変数に対し毎フレームC#から値をセットしてみましょう。

以下のような感じです。

m_mat.SetFloat("_ScrollValue", (float)Time.frameCount * m_speed);  

Materialに対して、 SetFloat を使います。第1引数に変数名、第2引数に値を代入するのです。すると、C#からシェーダーに値が渡ります。

このように毎フレームスクロールする値を渡し、その値を使ってShader側でスクロール処理をしています。

今回はMonoBehaviourのイベント関数「OnRenderImageメソッド」を使ってポストエフェクトを作っています。お決まりの書き方ですが、OnRenderImageから渡される、2つのRenderTexture( srcdest )は以下のコードで最終的に画面に出力します。

Graphics.Blit(src, dest, m_mat);  

srcとは元の画面です。destは これから 画面に表示する画面。 Graphics.Blit はsrcをdestにデータをコピーします。その際、第3引数のMaterial(シェーダー)を通してdestにコピーされるイメージです。

👉 【Unityの基礎】MonoBehaviour徹底解説【初心者向け】

このように、ポストエフェクトは作られるのです。

【Unity】画面がスクロールするポストエフェクトの作り方_1

ちなみに最初にお見せした↑の成果物は、 m_speed の値を変化させてスクロールスピードを早めたり遅くしたりして表現しています。

OnRenderImageの「_MainTex」とは

ポストエフェクトをOnRenderImageで使う際はシェーダー内の「_MainTex変数」は重要です。なぜなら_MainTexにカメラが写した画面のRenderTextureが代入されるからです。動作するためには条件があります。それは、「Propertiesで_MainTexの宣言をしておくこと」です。また、_MainTexという名前を変更もできません。OnRenderImageでポストエフェクトを作る際には重要なポイントなので抑えておきましょう。

まとめ

この記事では画面をスクロールさせるポストエフェクトの作り方を解説してきました。最初は難しく感じたかもしれませんが、仕組みはシンプルだったと思います。

スクロールさせるポストエフェクトの作り方

①UV座標のUとVを分離

②V座標の値を変化

③frac関数で小数点だけ取り出す

④画面に出力

重要なのはUV座標を分離させて、frac関数を使うことです。 frac関数を使えばどんな大きな値も小数になります。

ポストエフェクトはクオリティを左右します。 なぜなら画面全体に関わるため、視認する面積が大きいからです。 だからこそ使いこなしたいですよね。使いこなすためには基礎学習と復習です。

今回紹介した表現はポストエフェクトの中ではかなり簡単で基礎的なテクニックでした。この記事を何度も読み直して「基礎」を理解してみましょう。 何事も基礎が大事です。 そして、同時に手を動かすことです。最初は写経でOK。写経したら値を変化させたりコードを追加してみるのです。そうした研究やテストが学びを深めます。

本ブログではシェーダーを分かりやすく解説した記事も扱っていますので、ぜひウォッチしておいてください。

最後に今回使用したソースコードを紹介して終わります。

今回作成したソースコード全文

今回作成したソースコードは2ファイルです。C#ファイル1、シェーダーファイル1です。

💻ソースコード : スクロールするポストエフェクトC#ファイル
using UnityEngine;  

// このコンポーネントをCameraにアタッチします  
public class VerticalScroll : MonoBehaviour  
{
    [SerializeField] float m_speed = 0.005f;  
    Material m_mat;  

    void OnRenderImage(RenderTexture src, RenderTexture dest)  
    {
        if (m_mat == null)  
        {
            m_mat = new Material(Shader.Find("Hidden/VerticalScroll"));  
            // Materialが再生が終了したら破棄されるようにHideFlagsを設定する  
            m_mat.hideFlags = HideFlags.DontSave;  
        }
        // Shaderの_ScrollValue変数に値を渡す  
        m_mat.SetFloat("_ScrollValue", (float)Time.frameCount * m_speed);  
        Graphics.Blit(src, dest, m_mat);  
    }
}
💻ソースコード : スクロールするポストエフェクトシェーダー
Shader "Hidden/VerticalScroll"  
{
    Properties  
    {
        _MainTex ("Texture", 2D) = "white" {}  
    }

    CGINCLUDE  
    #include "UnityCG.cginc"  

    sampler2D _MainTex;  
    float _ScrollValue;  

    fixed4 frag (v2f_img i) : SV_Target  
    {
        float u = i.uv.x;  
        float v = i.uv.y;  
        // 小数点部分だけをオフセットする  
        // fracは小数点だけを残す関数  
        v = frac(v + _ScrollValue);  
        return tex2D(_MainTex, float2(u, v));  
    }
    ENDCG  
    SubShader  
    {
        Pass  
        {
            CGPROGRAM  
            #pragma vertex vert_img  
            #pragma fragment frag  
            ENDCG  
        }
    }
}

この記事の内容は以上です。

この記事があなたのゲーム開発に少しでもお役に立てたら嬉しいです。

「Unity初心者大学」というUnity初心者向けのYouTube始めました!!
ぜひチャンネル登録をお願いします!

最後まで読んでいただきありがとうございました!
すばらしいポストエフェクトライフをお過ごしください。

オススメ記事
検証環境
  • Unity2020.3.31f1
  • ビルトインレンダーパイプライン