あまがみ's diary

ゲーム & Webプログラミングな日常

【Unity】Sprite用アウトラインシェーダ

f:id:ina-amagami:20170423173042p:plain:w300

以下の記事で紹介されているシェーダを参考に、改良版を2種類作成してみました。
UnityのSprite用シェーダ(2本) · M.Ike

  • 線の色は固定、SpriteRendererでスプライトの色を変更できる(Sprites/Outline)
  • スプライトの色は固定、SpriteRendererで線の色を変更できる(Sprites/OutlineColor)

影とアウトラインの機能が両方含まれていたので、負荷を抑えるためアウトラインのみ残しています。
uGUIではOutlineコンポーネントを使えるので、SpriteRendererを使うケース専用です。

半透明や縦長、横長のスプライトだとアウトラインが綺麗に付かない場合があります。
アウトラインを別画像として作る方法が使えるケースでは使わない方が良いかもしれません。
 

準備:スプライト設定

Sprite Mode -> Mesh Typeを「Tight」にしている場合、
ワイヤフレームで見た時に上のUnityロゴはこんな感じになっています。

f:id:ina-amagami:20170423173138p:plain:w300

今回のシェーダでは線を描く範囲を確保するためにポリゴン内のテクスチャを縮小するため、
画像のようにポリゴンが分離している場合は変な方向にずれて縮小されてしまいます。
Mesh Typeを「Full Rect」に変更すると四角形のポリゴンになるため、アウトラインが綺麗に付くようになります。
ただし、余白部分まで描画処理が発生する分、負荷が高くなる点に注意して下さい。
 

Sprites/Outline

SpriteRendererで指定するColorは、スプライトの色に影響し、線の色には影響しません。

プロパティ

Outline Spread…線の太さ
Outline Color…線の色(アルファ値は無効)
Outline Smoothness…線のなめらかさ

実装(SpritesOutline.shader)
Shader "Sprites/Outline"
{
    Properties
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
        _OutLineSpread ("Outline Spread", Range(0, 0.1)) = 0
        _OutLineColor ("Outline Color", Color) = (1, 1, 1, 1)
        _Smoothness ("Outline Smoothness", Range(0, 0.5)) = 0.1
    }

    SubShader
    {
        Tags
        { 
            "Queue"="Transparent" 
            "IgnoreProjector"="True" 
            "RenderType"="Transparent" 
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

        Cull Off
        Lighting Off
        ZWrite Off
        Fog { Mode Off }
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile DUMMY PIXELSNAP_ON
            #include "UnityCG.cginc"
            
            struct appdata
            {
                float4 vertex   : POSITION;
                float4 color    : COLOR;
                float2 texcoord : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex   : SV_POSITION;
                fixed4 color    : COLOR;
                float2 texcoord : TEXCOORD0;
            };
            
            sampler2D _MainTex;
            half _OutLineSpread;
            fixed4 _OutLineColor;
            fixed _Smoothness;

            v2f vert(appdata IN)
            {
                fixed scale = 1 + _OutLineSpread * 2;

                float2 tex = IN.texcoord * scale;
                tex -= (scale - 1) / 2;

                v2f OUT;
                OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex);
                OUT.texcoord = tex;
                OUT.color = IN.color;
                #ifdef PIXELSNAP_ON
                OUT.vertex = UnityPixelSnap (OUT.vertex);
                #endif

                return OUT;
            }

            sampler2D _AlphaTex;
            float _AlphaSplitEnabled;

            fixed4 SampleSpriteTexture (float2 uv)
            {
                fixed4 color = tex2D (_MainTex, uv);

#if UNITY_TEXTURE_ALPHASPLIT_ALLOWED
                if (_AlphaSplitEnabled)
                {
                    color.a = tex2D (_AlphaTex, uv).r;
                }
#endif

                return color;
            }

            fixed4 frag(v2f IN) : SV_Target
            {
                fixed4 base = SampleSpriteTexture(IN.texcoord) * IN.color;

                fixed4 out_col = _OutLineColor;
                out_col.a = 1;
                half2 line_w = half2(_OutLineSpread, 0);
                fixed4 line_col = SampleSpriteTexture(IN.texcoord + line_w.xy)
                                + SampleSpriteTexture(IN.texcoord - line_w.xy)
                                + SampleSpriteTexture(IN.texcoord + line_w.yx)
                                + SampleSpriteTexture(IN.texcoord - line_w.yx);
                out_col *= line_col.a;
                out_col.rgb = _OutLineColor.rgb;
                out_col = lerp(base, out_col, max(0, sign(_OutLineSpread)));

                fixed4 main_col = base;
                main_col = lerp(main_col, out_col, (1 - main_col.a));
                main_col.a = IN.color.a * max(0, sign(main_col.a - _Smoothness));
                return main_col;
            }
        ENDCG
        }
    }
}

 

Sprites/OutlineColor

アウトラインの色はSpriteRendererのColorで指定します。

実装(SpritesOutlineColor.shader)
Shader "Sprites/OutlineColor"
{
    Properties
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)
        [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
        _OutLineSpread ("Outline Spread", Range(0, 0.1)) = 0
        _Smoothness ("Outline Smoothness", Range(0, 0.5)) = 0
    }

    SubShader
    {
        Tags
        { 
            "Queue"="Transparent" 
            "IgnoreProjector"="True" 
            "RenderType"="Transparent" 
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

        Cull Off
        Lighting Off
        ZWrite Off
        Fog { Mode Off }
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile DUMMY PIXELSNAP_ON
            #include "UnityCG.cginc"
            
            struct appdata
            {
                float4 vertex   : POSITION;
                float4 color    : COLOR;
                float2 texcoord : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex   : SV_POSITION;
                fixed4 color    : COLOR;
                float2 texcoord : TEXCOORD0;
            };
            
            sampler2D _MainTex;
            fixed4 _Color;
            half _OutLineSpread;
            fixed _Smoothness;

            v2f vert(appdata IN)
            {
                fixed scale = 1 + _OutLineSpread * 2;

                float2 tex = IN.texcoord * scale;
                tex -= (scale - 1) / 2;

                v2f OUT;
                OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex);
                OUT.texcoord = tex;
                OUT.color = IN.color;
                #ifdef PIXELSNAP_ON
                OUT.vertex = UnityPixelSnap (OUT.vertex);
                #endif

                return OUT;
            }

            sampler2D _AlphaTex;
            float _AlphaSplitEnabled;

            fixed4 SampleSpriteTexture (float2 uv)
            {
                fixed4 color = tex2D (_MainTex, uv);

#if UNITY_TEXTURE_ALPHASPLIT_ALLOWED
                if (_AlphaSplitEnabled)
                {
                    color.a = tex2D (_AlphaTex, uv).r;
                }
#endif

                return color;
            }

            fixed4 frag(v2f IN) : SV_Target
            {
                fixed4 base = SampleSpriteTexture(IN.texcoord);
                base.rgb *= _Color.rgb;

                fixed4 out_col = IN.color;
                out_col.a = 1;
                half2 line_w = half2(_OutLineSpread, 0);
                fixed4 line_col = SampleSpriteTexture(IN.texcoord + line_w.xy)
                                + SampleSpriteTexture(IN.texcoord - line_w.xy)
                                + SampleSpriteTexture(IN.texcoord + line_w.yx)
                                + SampleSpriteTexture(IN.texcoord - line_w.yx);
                out_col.a *= line_col.a;
                out_col = lerp(base, out_col, max(0, sign(_OutLineSpread)));

                fixed4 main_col = base;
                main_col = lerp(main_col, out_col, (1 - main_col.a));
                main_col.a = IN.color.a * max(0, sign(main_col.a - _Smoothness));
                return main_col;
            }
        ENDCG
        }
    }
}

 
 

SublimeText3をUnityシェーダのエディタにする方法

Unityシェーダを書く時、MonoDevelopはハイライトはしてくれるけどインテリセンスは効かない。
Xamarinはハイライトすらしてくれない。
SublimeText3がお手軽に神エディタ化してくれたのでご紹介します。

ダウンロードはこちら
Sublime Text - Download

Package Controlが必要です。まだ入れていない方はこちらの記事が参考になります
[tips][Sublime Text] Sublime Text 3をインストールしたらまずやること

パッケージのインストール

MacならCommand+Shift+PでInstall Packageを立ち上げます

f:id:ina-amagami:20170407224710p:plain:w400

Unity Shaderで検索したら、
『Unity Shader』と『Unity3D Shader Highlighter and Snippets』の2種類が見つかりました。
後者があれば十分だと感じたので、こちらを紹介します!

Unity3D Shader Highlighter and Snippets

f:id:ina-amagami:20170407225627p:plain:w400
View->Syntaxで『Unity Compute Shader』と『Unity Shader』が選択可能

Compute Shaderの方はコンピュートシェーダ用だと思われるので試していませんが、
Unity Shaderはこんな感じにインテリセンスが効きます

f:id:ina-amagami:20170407225819p:plain:w400

ビルトイン関数なら右の方にbuiltin-functionと表示してくれたりなど、便利!

 

【Unity5】これだけは押さえておきたいAudioClip設定まとめ

Unityでは細かいことを気にしなくても音を再生できてしまうので、
AudioClipの設定は特に変更せず使っているという方も多いのでは?

デフォルトのままではマズいかも?という項目に絞って解説します!

詳しくはUnityのリファレンスから
参考:Unity - マニュアル: Audio Clip

f:id:ina-amagami:20161204212414p:plain:w400

Force To Mono

Monoはモノラルのことです。ステレオのファイルをモノラルに変換します。
大抵の場合はファイルサイズがおよそ半分になるので、
ステレオである必要がない効果音などはチェックを入れておくと良いでしょう。

オプションのNormalizeはざっくり書くとモノラル化した時の音量に影響します。
Normalizeは基本的にチェックを付けておけば問題ないと思います。

Load Type

AudioClipをロードする時の方法を指定します。

- Decompress On Load

圧縮されているものを展開してメモリにロードします。
他の2つと異なり再生時に展開処理をしなくて済むので、CPUへの負荷が軽くなるのがメリットです。
デフォルトで指定されていますが、BGMなどファイルサイズの大きいもので使うと
メモリを大幅に使ってしまうので注意して下さい。

- Compressed In Memory

圧縮されたままの状態でメモリにロードします。再生時に展開するのでCPU負荷が大きいです。
ファイルサイズが大きく、メモリに展開されては困る場合に使いましょう。

- Streaming

ロードしながら再生を行うので、メモリをほんの少ししか使いません。
その代わりに処理負荷がかかります。
他の2つと異なりロード完了を待たなくても再生を開始できるので、遅延がありません。
BGMで使うのがオススメです。

Compression Format

圧縮の方法を指定します。

- PCM

品質を求める場合だけ使います。

- ADPCM

展開速度が早いので、ゲーム中で大量に再生する効果音などに使うとCPUの負荷を抑えられます。

- Vorbis

Unity5から使えるようになりました。拡張子で.oggのファイルはこの圧縮形式が使われています。
品質を下げずにファイルサイズを大幅に落とせるため、一番よく使う形式かと思います。
Qualityオプションで圧縮率を調整できるので、音質変化の許容範囲内で圧縮率を上げましょう。

その他モバイルでは上書き設定でMP3も選択できますが、
MP3を選ぶならVorbisのままで問題ないかと思います。

まとめ

デフォルト設定でも再生できますが、
メモリとCPU負荷を考慮して目的に合わせて設定しましょう!