使用するTEXCOORDをマテリアルから指定できるようにする方法です。
Unity2018.3.1
作るもの
例えば今Scaleという変数をTEXCOORDに入れて渡してシェーダでこの値を使うことを考えます。
ScaleはTEXCOORD0かTEXCOORD1のxyzwのいずれかに入れて渡されるとします。
こんな時には「何番目のTEXCOORDのどのSwizzle(x/y/z/w)を使うか」をマテリアルから指定できると便利です。
特にParticle SystemのCustom Dataなどを扱うときに便利です。
シェーダ(ShaderGUIを使う場合)
シェーダはこんな感じに書いておきます。
Shader "Example" { Properties { // プロパティを定義 [HideInInspector]_ScaleCoordIndex ("__scalecoordindex", int) = 3 [HideInInspector]_ScaleCoordSwizzle ("__scalecoordswizzle", int) = 3 } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" // マクロ定義。cgincファイルに移してもOK #define SELECTABLE_COORD_DEFINE(name) uniform int name##CoordIndex; uniform int name##CoordSwizzle; #define SELECTABLE_COORD_VERTEX_INPUT(index1, index2) half4 selectableCoord1: TEXCOORD##index1; half4 selectableCoord2: TEXCOORD##index2; #define SELECTABLE_COORD_VERTEX_INITIALIZATION(appdata, v2f) half4x4 selectableCoords = { appdata.selectableCoord1, appdata.selectableCoord2, half4(0.0, 0.0, 0.0, 0.0), half4(0.0, 0.0, 0.0, 0.0)}; #define SELECTABLE_COORD_VERTEX_GET(name) selectableCoords[name##CoordIndex][name##CoordSwizzle] // 変数を定義 SELECTABLE_COORD_DEFINE(_Scale) struct appdata { float4 vertex : POSITION; // 使いたいTEXCOORDのインデックスを入力 SELECTABLE_COORD_VERTEX_INPUT(1, 2) }; struct v2f { float4 vertex : SV_POSITION; }; v2f vert (appdata v) { v2f o = (v2f)0; // 頂点シェーダでSelectableCoordを使うための初期化 SELECTABLE_COORD_VERTEX_INITIALIZATION(v, o) // SelectableCoordによる値の取得 half4 scale = SELECTABLE_COORD_VERTEX_GET(_Scale); o.vertex = UnityObjectToClipPos(v.vertex * scale); return o; } fixed4 frag (v2f i) : SV_Target { return 1; } ENDCG } } CustomEditor "ExampleShaderGUI" }
説明はコメントの通りです。
マクロの部分は汎用的なものなのでcgincファイルに切り出しておくと便利です。
ShaderGUI(ShaderGUIを使う場合)
インスペクタは下記のようにして作ります。
using UnityEngine; using UnityEditor; public class ExampleShaderGUI : ShaderGUI { // TEXCOORDのインデックス public enum Coord { Unused = 3, Coord1 = 0, Coord2 = 1, } // TEXCOORDのSwizzle public enum Swizzle { X = 0, Y = 1, Z = 2, W = 3, } public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties) { DrawSelectableCoord("_Scale", properties); } private void DrawSelectableCoord(string prefix, MaterialProperty[] properties) { var coordIndexName = prefix + "CoordIndex"; var coordSwizzleName = prefix + "CoordSwizzle"; var coordIndexProp = FindProperty(coordIndexName, properties); var coordSwizzleProp = FindProperty(coordIndexName, properties); // TEXCOORDのインデックスをEnumで入力 var coordIndex = (Coord)coordIndexProp.floatValue; using (var scope = new EditorGUI.ChangeCheckScope()) { coordIndex = (Coord)EditorGUILayout.EnumPopup(ObjectNames.NicifyVariableName(coordIndexName), coordIndex); if (scope.changed) { coordIndexProp.floatValue = (float)coordIndex; } } // TEXCOORDのSwizzleをEnumで入力 var coordSwizzle = (Swizzle)coordIndexProp.floatValue; using (var scope = new EditorGUI.ChangeCheckScope()) { coordSwizzle = (Swizzle)EditorGUILayout.EnumPopup(ObjectNames.NicifyVariableName(coordSwizzleName), coordSwizzle); if (scope.changed) { coordSwizzleProp.floatValue = (float)coordSwizzle; } } } }
これは単純にシェーダのプロパティを列挙型で入力できるようにしているだけです。
結果
ここまでの結果はこんな感じになります。
アトリビュート(MaterialPropertyDrawer)を使って書き直したほうがいい気がしてきた
ここまで書いたところで、ShaderGUIで書くよりもMaterialPropertyDrawerを使ったほうが汎用性が高い気がしてきました。
ので書きました。
MaterialPropertyDrawer(アトリビュートを使う場合)
まずMaterialPropertyDrawerはこんな感じに書きます。
using UnityEngine; using UnityEditor; public class SelectableCoordDrawer : MaterialPropertyDrawer { // TEXCOORDのインデックス public enum Coord { Unused = 3, Coord1 = 0, Coord2 = 1, } // TEXCOORDのSwizzle public enum Swizzle { X = 0, Y = 1, Z = 2, W = 3, } private string _propertyName; public SelectableCoordDrawer(string propertyName) { _propertyName = propertyName; } public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor) { var changed = false; position = EditorGUI.PrefixLabel(position, new GUIContent(ObjectNames.NicifyVariableName(_propertyName + "Coord"))); // 描画領域が狭すぎるので少しずらす position.width += 30; position.x -= 30; // Coordを描画 var coordPosition = position; coordPosition.xMax -= 30; var coordIndex = (Coord)prop.vectorValue.x; using (var scope = new EditorGUI.ChangeCheckScope()) { coordIndex = (Coord)EditorGUI.EnumPopup(coordPosition, coordIndex); if (scope.changed) { changed = true; } } // Swizzleを描画 var swizzlePosition = position; swizzlePosition.x += coordPosition.width + 4; swizzlePosition.width -= coordPosition.width + 4; var coordSwizzle = (Swizzle)prop.vectorValue.y; using (var scope = new EditorGUI.ChangeCheckScope()) { coordSwizzle = (Swizzle)EditorGUI.EnumPopup(swizzlePosition, coordSwizzle); if (scope.changed) { changed = true; } } if (changed) { prop.vectorValue = new Vector4((int)coordIndex, (int)coordSwizzle, prop.vectorValue.z, prop.vectorValue.w); } } }
シェーダ(アトリビュートを使う場合)
シェーダはこんな感じです。
Vector型のプロパティにSelectableCoordアトリビュートを付けるだけで使えます。
アトリビュートにしたのでCustomEditorの指定は削除します。
Shader "Example" { Properties { // プロパティを定義 [SelectableCoord(_Scale)] _ScaleCoord ("__scalecoord", Vector) = (0, 0, 0, 0) } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" // マクロ定義。cgincファイルに移してもOK #define SELECTABLE_COORD_DEFINE(name) uniform int2 name##Coord; #define SELECTABLE_COORD_VERTEX_INPUT(index1, index2) half4 selectableCoord1: TEXCOORD##index1; half4 selectableCoord2: TEXCOORD##index2; #define SELECTABLE_COORD_VERTEX_INITIALIZATION(appdata, v2f) half4x4 selectableCoords = { appdata.selectableCoord1, appdata.selectableCoord2, half4(0.0, 0.0, 0.0, 0.0), half4(0.0, 0.0, 0.0, 0.0)}; #define SELECTABLE_COORD_VERTEX_GET(name) selectableCoords[name##Coord.x][name##Coord.y] // 変数を定義 SELECTABLE_COORD_DEFINE(_Scale) struct appdata { float4 vertex : POSITION; // 使いたいTEXCOORDのインデックスを入力 SELECTABLE_COORD_VERTEX_INPUT(1, 2) }; struct v2f { float4 vertex : SV_POSITION; }; v2f vert (appdata v) { v2f o = (v2f)0; // 頂点シェーダでSelectableCoordを使うための初期化 SELECTABLE_COORD_VERTEX_INITIALIZATION(v, o) // SelectableCoordによる値の取得 half4 scale = SELECTABLE_COORD_VERTEX_GET(_Scale); o.vertex = UnityObjectToClipPos(v.vertex * scale); return o; } fixed4 frag (v2f i) : SV_Target { return 1; } ENDCG } } }
結果
結果はこんな感じになります。1行にしてみました。