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
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
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 にする。
次は #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 がのちに出てくる。
- #pragma fragment frag
fragmentシェーダーを表す。frag 関数をとりあえずエントリしておき、frag関数をその後で実装する。
uniform sampler2D _MainTex;
sample2D型の uniform変数である _MainTex 変数を宣言している。
uniform変数というのは、頂点シェーダからもフラグメントシェーダからも参照可能な、
constなグローバル変数。
そして本命の処理
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 セマンティクスを持つということしか現状では分からず・・・。
- fixed4 original = tex2D(_MainTex, i.uv);
今から処理をかける textureの fixed4型の色情報を保持しておく。
- fixed Y = dot (fixed3(0.299, 0.587, 0.114), original.rgb);
dot は2つのベクトルの内積を得る関数。
この式は、まずグレイスケール変換を行っている内積だった。
この係数は、NTSC 系加重平均法と呼ばれるグレイスケール変換に使われる手法に則ったもの
- fixed4 sepiaConvert = float4 (0.191, -0.054, -0.221, 0.0);
グレースケール化した画素に特定の処理を行うことでセピア調化できる。
ぐぐってみると乗算で求める方式と加算で求める方式など見られた。
Unityは上記定数で加算する方式でImageEffectsのSepiatone shaderを実装している様子。
このshader を cameraにアタッチすることでセピアトーン化できる。
UnityをAndroid実機につなげてデバッグした時のアレコレ
Unity アプリがAndroid実機でクラッシュする問題を調査した時のアレコレをメモる。
- monitorでログを見る
android 実機と MacをUSBケーブルで繋げる。
android sdk をインストールし、tools/monitor コマンドを実行すると android 実機上で吐かれたログを観れるようになる。
- android のメモリ使用状況をチェック
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からソースファイルを割り出す
KMC Staff Blog:Androidのデバッグダンプからソース上の問題発生箇所を調べる方法
docs.unity3d.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した場合もこの処理は走るので、せっかく設定したカスタム値を上書きしないように制御する必要があるので注意。