おもちゃラボ

Unityで遊びを作ってます

【Unityシェーダ入門】コントラストを調節できるポストエフェクトを作る

今回はゲーム画面のコントラストを調節するポストエフェクトの作り方を紹介します。Photoshopなどでは、スライダを動かすだけでコントラストを調整できますね。この機能をUnityのシェーダで作ってみましょう。

f:id:nn_hokuson:20170801154711j:plain

今回の記事の内容は次のとおりです。

コントラストの原理

まずは、コントラストを高くするというのは、どういう操作なのかを最初に説明します。コントラストを高くする、というのは「暗いところをより暗く、明るいところをより明るくする」ことです。

Photoshopのトーンカーブで説明すると、次の図のようになります。例えば輝度値200の明るいピクセルは、このカーブを通すと輝度値は230になります。逆に輝度値50の暗い部分はこのカーブを通すと輝度値は20になります。

f:id:nn_hokuson:20170801192118p:plain:w320

このようなS字カーブをプログラムで作る場合は、シグモイド曲線が便利です。シグモイド関数は次の数式で表せます。

f:id:nn_hokuson:20170801194003p:plain:w250

a=8の場合、次のような曲線になります。このあと説明しますが、aの値を変化させることでS字カーブを変形することができます。

f:id:nn_hokuson:20170801194140p:plain:w300

最近では、ディープラーニングでパーセプトロンが発火するかどうかの判定に、このシグモイド関数が使われていたりします。

ではシグモイド関数を使って、出力の輝度値を変化させるシェーダプログラムを作っていきましょう。

描画する画像をフックする

最終的に描画する画像にポストエフェクトをかけるために、画面をレンダリングする途中で画像をフックして画像処理します。

プロジェクトウインドウで「PostEffect.cs」というファイルを作成し、次のプログラムを入力してください。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PostEffect : MonoBehaviour {

    public Shader shader;
    public Material mat;

    void Start()
    {
        this.mat = new Material (this.shader);
    }

    void OnRenderImage(RenderTexture src, RenderTexture dest )
    {
        Graphics.Blit (src, dest, this.mat);
    }
}

Startメソッドではインスペクタでセットしたシェーダを使って、マテリアルを作成しています。

OnRenderImageメソッドはレンダリングが完了した後に呼び出されるメソッドで、この中でBlitメソッドを使ってポストエフェクトをかけています。Blitメソッドはsrc画像に第三引数で指定したポストエフェクトをかけてdest画像に書き込みます。

詳しくはこちらの記事も合わせて参照下さい。

nn-hokuson.hatenablog.com

スクリプトが保存できたら、PostEffect.csをカメラオブジェクトにアタッチしてください。

f:id:nn_hokuson:20170801192715j:plain

コントラストを高くするシェーダを作る

コントラストを高くするためのシェーダを作ります。プロジェクトウインドウで「右クリック」→「Create」→「Shader」→「Standard Surface Shader」を選択し、作成したファイル名をContrastに変更します。

contrast.shaderを開いて、次のスクリプトを入力して下さい。

Shader "PostEffect/contranst"
{
    Properties{
        _MainTex("MainTex", 2D)=""{}
        _Contrast("Contrast", Range(1, 20))=10
    }

    SubShader
    {
        Pass{
            CGPROGRAM
                #include "UnityCg.cginc"
                #pragma vertex vert_img
                #pragma fragment frag

                sampler2D _MainTex;
                float _Contrast;

                float4 frag(v2f_img i) : COLOR {
                    float4 c = tex2D(_MainTex, i.uv);
                    if( i.uv.x > 0.5 ){
                        c = 1/(1+exp(-_Contrast*(c-0.5)));
                    }
                    return c;
                }
            ENDCG
        }
    }
}

インスペクタからコントラストの強さを変更できるよう、Propertiesブロックに_Contrast変数を宣言しています。

また、フラグメントシェーダで各ピクセルに対してコントラストの計算をしています。コントラストの計算には上で紹介したシグモイド関数を使っています。

cの値が0から1の範囲でS字カーブになるように、expの係数cから0.5を引いていることに注意してください。

c = 1/(1+exp(-_Contrast*(c-0.5)));

_Contrastの値を変更することで、つぎのようにトーンカーブも変化します。macOSにはGrapherという便利なアプリがあるので実験してみてください。

f:id:nn_hokuson:20170801192806p:plain

カメラにコントラスト用のシェーダをセットする

作成したシェーダをを、カメラにアタッチしたPostEffectスクリプトのShader欄にドラッグ&ドロップします。これで、コントラストを調整するポストエフェクトをかける準備ができました。

f:id:nn_hokuson:20170801193110j:plain

実行結果は次のようになります。左半分がポストエフェクトをかけていない画面、右半分がポストエフェクトをかけた画面になります。

f:id:nn_hokuson:20170801154711j:plain

コントラストの強さを変更したい場合は、ゲーム実行中にヒエラルキーウインドウでMainCameraを選択し、インスペクタのPostEffectスクリプトからMatをダブルクリックして開き、スライダーで調節できます。

f:id:nn_hokuson:20170801193312p:plain:w300

_Contrastの値を10、12、15と変化させると次のように見栄えが変化します。

f:id:nn_hokuson:20170801161359j:plain:w500

まとめ

今回はコントラストを調整するポストエフェクトを作成しました。Unityではシェーダを使えば簡単にポストエフェクトが作れるので是非いろいろ試してみて下さい。