kidoOooOoooOOom

IT系で開発やってます

UnityのUI-Default.shader 内のUNITY_UI_ALPHACLIP について

Unity 5.3 に含まれる UI-Default.shader でまだ理解できてなかったところを1つずつ消化していく。
UNITY_UI_ALPHACLIP関連について。

Properties
    {
        (省略)
        [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
    }
    SubShader
    {
 (省略)
        Pass
        {
        CGPROGRAM
            #pragma multi_compile __ UNITY_UI_ALPHACLIP
   (省略)

            fixed4 frag(v2f IN) : SV_Target
            {
                half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;

                color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);

                #ifdef UNITY_UI_ALPHACLIP
                clip (color.a - 0.001);
                #endif

                return color;
            }

いくつか省略。まず最初の Properitesで定義している

[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0

トグルでON/OFF できるproperty 。ONの場合は UNITY_UI_ALPHACLIP 変数に 1 が入る。
[Toggle] _UseUIAlphaClip と書いた場合は _USEUIALPHACLIP_ON 変数に1が入る挙動らしい。
Unity - Scripting API: MaterialPropertyDrawer

#pragma multi_compile __ UNITY_UI_ALPHACLIP

multi_compile についての Unity manual を見てもよく分からなかった。
Unity - マニュアル: 複数のシェーダープログラムのバリアントを作る

こちらのページが分かりやすい。
qiita.com
#ifdef で分岐する/しない パターンのことをシェーダーのバリアントとして実現している感じ。
__ が UNITY_UI_ALPHACLIP =0 のときのバリアント、UNITY_UI_ALPHACLIPがUNITY_UI_ALPHACLIP=1のときのバリアント。

                #ifdef UNITY_UI_ALPHACLIP
                clip (color.a - 0.001);
                #endif

fragment shader内のここで、UNITY_UI_ALPHACLIP=1の場合のバリアントで HLSLのclip()を実行している。
clipは渡された値が0未満だったらそのピクセルを破棄する処理。つまり、アルファが0.001未満だったら描画しない(完全に透明扱い)
clip (DirectX HLSL)

UnityのUnlit/Texture shaderを理解

Unity内蔵の Unlit/Texture shader は非常にシンプル。
ライティングの反映無し、透過無しのテクスチャのみのシェーダー。

Shader "Unlit/Texture" {
Properties {
	_MainTex ("Base (RGB)", 2D) = "white" {}
}

SubShader {
	Tags { "RenderType"="Opaque" }
	LOD 100
	
	Pass {
		Lighting Off
		SetTexture [_MainTex] { combine texture } 
	}
}
}
LOD 100

LOD は描画負荷が高いものや高度なグラッフィク機能の要求が有るものほど高い値を設定する。Unity では 100 ~ 600 ぐらいの範囲。デフォルトでは無限大になっていて、端末に応じてLODのしきい値を調整して重い表現は表示しないような使い方。
docs.unity3d.com

Tags { "RenderType"="Opaque" }

RenderTypeはレンダリングの種類を示す。今回はOpaque(不透明)を設定する。
他にどんな値があるのかや、Replaced Shadersでどのように使われるかは下記リンク参考。(ちょっと難しかった)
docs.unity3d.com

tarowork.hatenablog.jp

Unityでアルファマスクを使うshaderについて理解

githubで公開されている下記shaderについて学んだのでメモる。
github.com


shader コードはここ
unity-alphamask/SpriteWithMask.shader at master · keijiro/unity-alphamask · GitHub

まずは

Blend SrcAlpha OneMinusSrcAlpha

Blendingに関する設定。Blending は透過のカラー作成に使用する。
Blend SrcAlpha OneMinusSrcAlpha は Traditional transparency とのこと。


グラフィックスがレンダリングされて、さらにすべてのシェーダーが実行されて、すべてのテクスチャが適用されると、画面にピクセルが描画されます。すでに存在しているピクセルに対して、どのように合成するかはブレンディングで制御されます。
Blend Off : ブレンディングを無効にします(デフォルト)。
SrcAlpha: このステージの値はソースα値を乗算する。
OneMinusSrcAlpha: このステージの値はフレームバッファの(1 - Source Alpha)を乗算します。

docs.unity3d.com

struct v2f {
                float4 pos : SV_POSITION;
                float2 uv1 : TEXCOORD0;
                float2 uv2 : TEXCOORD1;
            };

vertext shaderから fragment shader へ渡す値のstruct宣言。
TEXCOORD0 と TEXCOORD1 で、マルチパスレンダリングのターゲットを2つ設定している。
ameblo.jp

o.pos = mul (UNITY_MATRIX_MVP, v.vertex);

mul は行列乗算。vertex shader に渡されたローカル座量(v.vertex) を model*view*projection 変換が詰まったUNITY_MATRIX_MVPの座標変換行列と乗算して、カメラに映る位置に変換している。
esakun.hateblo.jp

www.dfx.co.jp

o.uv1 = TRANSFORM_TEX (v.texcoord, _MainTex);
o.uv2 = TRANSFORM_TEX (v.texcoord, _MaskTex);

TRANSFORM_TEXマクロはテクスチャに設定されたタイリングとオフセットを反映したUV座標(テクスチャ座標)を取得するためのマクロ。
alfa.hatenablog.jp

half4 frag (v2f i) : COLOR
            {
                half4 base = tex2D (_MainTex, i.uv1);
                half4 mask = tex2D (_MaskTex, i.uv2);
                base.w = mask.x * mask.x * mask.x;
                return base * (_Color * 2.0f);
            }

tex2D関数はUV座標(テクスチャ座標)を指定してテクスチャのピクセルカラーを取得することができる。
color でいうところの RGBA が half4 で XYZW となるので、base.w は アルファ値、mask.x はマスクtextureのR値を参照している。
最後に base * (_Color * 2.0f) しているのは、_Color のデフォルト値が 0.5 だからだと思われる。

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

UnityをAndroid実機につなげてデバッグした時のアレコレ

Unity アプリがAndroid実機でクラッシュする問題を調査した時のアレコレをメモる。

  • monitorでログを見る

android 実機と MacをUSBケーブルで繋げる。
android sdk をインストールし、tools/monitor コマンドを実行すると android 実機上で吐かれたログを観れるようになる。

  • android のメモリ使用状況をチェック

android sdk の adb コマンドを使う。

adb shell dumpsys meminfo

を実行すると、下記のようなメモリ使用状況確認できる。

Total RAM: 1850148 kB (status normal)
 Free RAM: 775573 kB (170057 cached pss + 465736 cached kernel + 139780 free)
 Used RAM: 1181385 kB (1049989 used pss + 131396 kernel)
  • crash report から crashしたプログラムの箇所を特定する

crash report には、どのファイルのどの番地でクラッシュしたかが出力されている。
.so ファイルの場合は、objdump, greadelf コマンド等を用いて解析する。
Macでは、binutils を installして gobjdump, greadelf コマンドを使う。

(ただ、自分の場合は .so ファイルを gobjdumpしてもファイルが曖昧ですというエラーが出たため解析諦めた)

Unity 公式ドキュメントに書かれているこの方法をとればok.
http://docs.unity3d.com/ja/current/Manual/AttachingMonoDevelopDebuggerToAnAndroidDevice.html

(ただし、リモートデバッガをONにしたために解析したい場所とはまた別のところでクラッシュして困った・・・)


参考にしたページ:
hiro99ma blog: logcatに出てくるSIGSEGVからソースファイルを割り出す

www.slideshare.net

KMC Staff Blog:Androidのデバッグダンプからソース上の問題発生箇所を調べる方法
docs.unity3d.com

sssslide.com
docs.unity3d.com
araking56.blog134.fc2.com

qiita.com

UnityでScriptableObjectを用いて設定管理

拡張エディタやImporterの設定ファイルとして、ScriptableObjectが使用できる。
tsubakit1.hateblo.jp


例えば今回は、画像ファイルをUnity上にインポートする際に、特定のフォルダ配下の場合はTrue color にしたりmip mapをOFFにするといった自動設定を行うため、下記のようなScriptableObjectを用意する。

    [CreateAssetMenu(fileName = "TextureImporterConfig")]
    public class TextureImporterConfig : ScriptableObject, ISerializationCallbackReceiver
    {
        public List<TextureSetting> textureSettings = new List<TextureSetting> ();

        public Dictionary<string, TextureSetting> textureSettingsDic;

        public void OnAfterDeserialize()
        {
            textureSettingsDic = new Dictionary<string, TextureSetting> ();

            foreach (var item in textureSettings)
            {
                if (textureSettingsDic.ContainsKey(item.targetPath) == false)
                {
                    textureSettingsDic.Add(item.targetPath, item);
                }
            }
        }

        public void OnBeforeSerialize()
        {
        }
    }

    [System.Serializable]
    public class TextureSetting
    {
        public string targetPath;
        public TextureImporterType importType;
        public bool mipmapEnabled;
        public TextureImporterFormat importFormat;
        public string packingTag;
    }


CreateAssetMenu属性を付随すると、Asset Menuでこのclassのオブジェクトを生成できるようになる。
ただし、ScriptableObjectはDictionary型を直接シリアライズできないので、List型で一旦保持しておき、ISerializationCallbackReceiverのOnAfterDeserialize()の中でDictionary型へ変換するようにした。

これでUnity Editor上でも設定を変更できるようになったのだが、Editor上での変更が一向にassetファイルに反映されなかった。
調べてみると、自分で SetDirty をして変更通知をしてから asset を 保存するのが必要だった。
docs.unity3d.com

というわけで下記クラスを追加し、Editor上で Save Asset ボタンを押すと変更内容がassetファイルに反映されるようにした。

    [CustomEditor(typeof(TextureImporterConfig))]
    public class TextureImporterConfigEditor : Editor
    {
        public override void OnInspectorGUI()
        {
            DrawDefaultInspector();

            if (GUILayout.Button("Save Asset"))
            {
                TextureImporterConfig configTarget = target as TextureImporterConfig;
                EditorUtility.SetDirty(configTarget);
                AssetDatabase.SaveAssets();
            }
        }
    }

UnityでFBXファイルをインポートした時のデフォルト設定を自動化

Maya等で作成した3Dモデルが含まれるFBXファイルはUnity上にドラッグ&ドロップするだけでインポートが可能だが、デフォルト設定では色々とまずいので設定を変える必要があったりする。
インポートするファイルの数がたいしたことなければ問題ないが、100を超えるような物量に毎回同じ設定変更を行うのは効率が悪いので、スクリプトで自動化する。

Unityでは、AssetPostprocessorというクラスを継承すれば、インポート時のパイプラインにフックして特定の処理を行うことができる。
docs.unity3d.com


例えば、UnityとMayaでスケールの設定が異なっているため Modelのscale sizeを1から100に変更したいような場合は OnPreprocessModel() で値を変更すればおk。

void OnPreprocessModel ()
{
	var modelImporter = assetImporter as ModelImporter;
	modelImporter.globalScale = 100;
}

Importで生成された Materials に対して、特定のshader 割り当ておよび、パラメータ設定を行いたい場合は、OnPostprocessModel で生成されたMaterial に対して変更すればおk。

void OnPostprocessModel (GameObject gameObject)
{
	foreach (Transform item in gameObject.transform) {
		if (item.gameObject.GetComponent<Renderer> () == null) {
			continue;
		}

		foreach (Material material in item.GetComponent<Renderer>().sharedMaterials) {
			// shader を変更
			material.shader = Shader.Find ("HogeHogeShader");

			// shader のパラメータを変更
			material.SetFloat ("_HogeHogeParam", 0.1f);
			material.SetTexture ("_SampleCube", Resources.Load ("sample_texture") as Texture);
		}
	}
}

Unity上ですでにimport済みのファイルをReimportした場合もこの処理は走るので、せっかく設定したカスタム値を上書きしないように制御する必要があるので注意。