kidoOooOoooOOom

IT系で開発やってます

UnityのSepiatone shaderを理解する

Shaderを勉強しはじめたので、まずはシンプルな Sepiatone shaderの中身を写経しながら基本的なところを学んでいく。

Unity の ImageEffectsで使われているセピアトーン化のshaderコードがこれ

Shader "Hidden/Sepiatone Effect" {
Properties {
    _MainTex ("Base (RGB)", 2D) = "white" {}
}

SubShader {
    Pass {
        ZTest Always Cull Off ZWrite Off

CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#include "UnityCG.cginc"

uniform sampler2D _MainTex;

fixed4 frag (v2f_img i) : SV_Target
{
    fixed4 original = tex2D(_MainTex, i.uv);

    // get intensity value (Y part of YIQ color space)
    fixed Y = dot (fixed3(0.299, 0.587, 0.114), original.rgb);

    // Convert to Sepia Tone by adding constant
    fixed4 sepiaConvert = float4 (0.191, -0.054, -0.221, 0.0);
    fixed4 output = sepiaConvert + Y;
    output.a = original.a;

    return output;
}
ENDCG

    }
}

Fallback off

}

かなりシンプルだが、初心者には1行1行が重い意味を持つ。

まず Pass の付近

SubShader {
    Pass {
        ZTest Always Cull Off ZWrite Off

シンタックス
Pass { [Name and Tags] [RenderSetup] }
であり、ここでは ZTest, Cull, ZWrite の設定をしている。

  • ZTest Always

これは Ztestを常に合格させる、つまり奥行きがどうであれ必ずレンダリングさせたいという意味。
セピアトーンの色で画面を塗るために必要。

  • Cull Off

ポリゴンのどちら側をカリングする(描画しない)か制御。
Off は、「カリングを無効にして、すべての面を描画します。特殊なエフェクトで使用します」という意味。

  • ZWrite Off

このオブジェクトのピクセルをデプスバッファに書き込みするか制御(デフォルトは On )
透過のエフェクトになるため、Off にする。

docs.unity3d.com



次は #pragma のところ

CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#include "UnityCG.cginc"

~~~

ENDCG
  • CGPROGRAM と ENDCG

は、この中に Cg/HLSL コードを書いてますよというおまじない。

  • #pragma vertex vert_img

#pragma ステートメントで、どんなシェーダー関数なのかを示す。
vertex は頂点シェーダを表す。
また、UnityCG.cginc で定義されている vert_img をエントリ関数として使用することを宣言しており、その関数から渡ってくる v2f_img がのちに出てくる。

tips.hecomi.com

  • #pragma fragment frag

fragmentシェーダーを表す。frag 関数をとりあえずエントリしておき、frag関数をその後で実装する。

uniform sampler2D _MainTex;

sample2D型の uniform変数である _MainTex 変数を宣言している。
uniform変数というのは、頂点シェーダからもフラグメントシェーダからも参照可能な、
constグローバル変数

ユニフォーム変数 - code snippets

そして本命の処理

fixed4 frag (v2f_img i) : SV_Target
{
    fixed4 original = tex2D(_MainTex, i.uv);

    // get intensity value (Y part of YIQ color space)
    fixed Y = dot (fixed3(0.299, 0.587, 0.114), original.rgb);

    // Convert to Sepia Tone by adding constant
    fixed4 sepiaConvert = float4 (0.191, -0.054, -0.221, 0.0);
    fixed4 output = sepiaConvert + Y;
    output.a = original.a;

    return output;
}
  • fixed4 frag (v2f_img i) : SV_Target

frag関数で戻り値の方が fixed4 , 引数が v2f_img 型の i 変数というのがわかる。: SV_Target というのはセマンティクスというもので、変数の意味がGPUに伝えられる。
fragmentシェーダーでは慣例として カラーを出力し SV_Target セマンティクスを持つということしか現状では分からず・・・。

docs.unity3d.com

セマンティクス (DirectX HLSL)

  • fixed4 original = tex2D(_MainTex, i.uv);

今から処理をかける textureの fixed4型の色情報を保持しておく。

  • fixed Y = dot (fixed3(0.299, 0.587, 0.114), original.rgb);

dot は2つのベクトルの内積を得る関数。
この式は、まずグレイスケール変換を行っている内積だった。
この係数は、NTSC 系加重平均法と呼ばれるグレイスケール変換に使われる手法に則ったもの

wgld.org

  • fixed4 sepiaConvert = float4 (0.191, -0.054, -0.221, 0.0);

グレースケール化した画素に特定の処理を行うことでセピア調化できる。
ぐぐってみると乗算で求める方式と加算で求める方式など見られた。
Unityは上記定数で加算する方式でImageEffectsのSepiatone shaderを実装している様子。

このshader を cameraにアタッチすることでセピアトーン化できる。
f:id:gidooom:20160512201352p:plain