本記事は サムザップ Advent Calendar 2025 11日目の記事です。
はじめに
Unity6.3からUIToolkitがShaderGraphに対応しました。
また、Filterと呼ばれるポストプロセスのような機能にも対応したので触ってみようと思います。
環境
Unity 6000.3.0b10
自作したShaderGraph対応
ShaderGraphの生成
Projectウィンドウで右クリックを押下し、
Create/Shader Graph/URP/UI Shader Graph から生成するだけで完了です。
もしくは既に生成したShaderGraphのGraphInspectorの、
Universl/Material を UI にしてください。
ShaderGraphの中身
ポスタライズするだけにしています。
Value 変数はfloat型を指定しているだけです。
DefaultTextureノードについて
UIToolkit専用のノードで、UI側で指定されているテクスチャを取得した結果を返すノードです。
Sample Texture 2D ノードと似た機能ですが、こちらはTextureを指定しなくてもUI側で指定しているものを自動で取得してくれます。
RenderTypeBranchノードについて
UIToolkit専用のノードで、テクスチャの種類を見て適した結果を返してくれるノードです。
このノードの出力を Fragment ノードに接続する必要があります。
生成されたコードは以下のようになっています。
生成されたコード
[branch] if (_UIE_RENDER_TYPE_TEXTURE || _UIE_RENDER_TYPE_ANY && round(IN.typeTexSettings.x) == k_FragTypeTexture)
{
TextureFragInput Unity_UIE_EvaluateTextureNode_Input;
Unity_UIE_EvaluateTextureNode_Input.tint = IN.color;
Unity_UIE_EvaluateTextureNode_Input.textureSlot = IN.typeTexSettings.y;
Unity_UIE_EvaluateTextureNode_Input.uv = (_Posterize_42247c5b519243acba20130591f25500_Out_2_Vector4.xy);
Unity_UIE_EvaluateTextureNode_Input.isArc = false;
Unity_UIE_EvaluateTextureNode_Input.outer = float2(-10000, -10000);
Unity_UIE_EvaluateTextureNode_Input.inner = float2(-10000, -10000);
CommonFragOutput Unity_UIE_EvaluateTextureNode_Output = uie_std_frag_texture(Unity_UIE_EvaluateTextureNode_Input);
_DefaultTexture_d275e0be703a447d99a4926b24d80a2c_Texture_2_Vector4 = Unity_UIE_EvaluateTextureNode_Output.color;
}
float3 _RenderTypeBranch_d1b1e877eeff451b928a39fae1df6953_Color_5_Vector3 = float3(0, 0, 0);
float _RenderTypeBranch_d1b1e877eeff451b928a39fae1df6953_Alpha_6_Float = 1.0;
[branch] if (_UIE_RENDER_TYPE_SOLID || _UIE_RENDER_TYPE_ANY && TestType(IN.typeTexSettings.x, k_FragTypeSolid))
{
SolidFragInput Unity_UIE_RenderTypeSwitchNode_Solid_Input;
Unity_UIE_RenderTypeSwitchNode_Solid_Input.tint = IN.color;
Unity_UIE_RenderTypeSwitchNode_Solid_Input.isArc = false;
Unity_UIE_RenderTypeSwitchNode_Solid_Input.outer = float2(-10000, -10000);
Unity_UIE_RenderTypeSwitchNode_Solid_Input.inner = float2(-10000, -10000);
CommonFragOutput Unity_UIE_RenderTypeSwitchNode_Output = uie_std_frag_solid(Unity_UIE_RenderTypeSwitchNode_Solid_Input);
_RenderTypeBranch_d1b1e877eeff451b928a39fae1df6953_Color_5_Vector3 = Unity_UIE_RenderTypeSwitchNode_Output.color.rgb;
_RenderTypeBranch_d1b1e877eeff451b928a39fae1df6953_Alpha_6_Float = Unity_UIE_RenderTypeSwitchNode_Output.color.a;
}
else [branch] if (_UIE_RENDER_TYPE_TEXTURE || _UIE_RENDER_TYPE_ANY && TestType(IN.typeTexSettings.x, k_FragTypeTexture))
{
_RenderTypeBranch_d1b1e877eeff451b928a39fae1df6953_Color_5_Vector3 = _DefaultTexture_d275e0be703a447d99a4926b24d80a2c_Texture_2_Vector4.rgb;
_RenderTypeBranch_d1b1e877eeff451b928a39fae1df6953_Alpha_6_Float = _DefaultTexture_d275e0be703a447d99a4926b24d80a2c_Texture_2_Vector4.a;
}
else [branch] if ((_UIE_RENDER_TYPE_TEXT || _UIE_RENDER_TYPE_ANY) && TestType(IN.typeTexSettings.x, k_FragTypeText))
{
[branch] if (GetTextureInfo(IN.typeTexSettings.y).sdfScale > 0.0)
{
SdfTextFragInput Unity_UIE_RenderTypeSwitchNode_SdfText_Input;
Unity_UIE_RenderTypeSwitchNode_SdfText_Input.tint = IN.color;
Unity_UIE_RenderTypeSwitchNode_SdfText_Input.textureSlot = IN.typeTexSettings.y;
Unity_UIE_RenderTypeSwitchNode_SdfText_Input.uv = IN.uvClip.xy;
Unity_UIE_RenderTypeSwitchNode_SdfText_Input.extraDilate = IN.circle.x;
Unity_UIE_RenderTypeSwitchNode_SdfText_Input.textCoreLoc = round(IN.textCoreLoc);
Unity_UIE_RenderTypeSwitchNode_SdfText_Input.opacity = IN.typeTexSettings.z;
CommonFragOutput Unity_UIE_RenderTypeSwitchNode_Output = uie_std_frag_sdf_text(Unity_UIE_RenderTypeSwitchNode_SdfText_Input);
_RenderTypeBranch_d1b1e877eeff451b928a39fae1df6953_Color_5_Vector3 = Unity_UIE_RenderTypeSwitchNode_Output.color.rgb;
_RenderTypeBranch_d1b1e877eeff451b928a39fae1df6953_Alpha_6_Float = Unity_UIE_RenderTypeSwitchNode_Output.color.a;
}
else
{
BitmapTextFragInput Unity_UIE_RenderTypeSwitchNode_BitmapText_Input;
Unity_UIE_RenderTypeSwitchNode_BitmapText_Input.tint = IN.color;
Unity_UIE_RenderTypeSwitchNode_BitmapText_Input.textureSlot = IN.typeTexSettings.y;
Unity_UIE_RenderTypeSwitchNode_BitmapText_Input.uv = IN.uvClip.xy;
Unity_UIE_RenderTypeSwitchNode_BitmapText_Input.opacity = IN.typeTexSettings.z;
CommonFragOutput Unity_UIE_RenderTypeSwitchNode_Output = uie_std_frag_bitmap_text(Unity_UIE_RenderTypeSwitchNode_BitmapText_Input);
_RenderTypeBranch_d1b1e877eeff451b928a39fae1df6953_Color_5_Vector3 = Unity_UIE_RenderTypeSwitchNode_Output.color.rgb;
_RenderTypeBranch_d1b1e877eeff451b928a39fae1df6953_Alpha_6_Float = Unity_UIE_RenderTypeSwitchNode_Output.color.a;
}
}
else
{
SvgGradientFragInput Unity_UIE_RenderTypeSwitchNode_SvgGradient_Input;
Unity_UIE_RenderTypeSwitchNode_SvgGradient_Input.settingIndex = round(IN.typeTexSettings.z);
Unity_UIE_RenderTypeSwitchNode_SvgGradient_Input.textureSlot = round(IN.typeTexSettings.y);
Unity_UIE_RenderTypeSwitchNode_SvgGradient_Input.uv = IN.uvClip.xy;
Unity_UIE_RenderTypeSwitchNode_SvgGradient_Input.isArc = false;
Unity_UIE_RenderTypeSwitchNode_SvgGradient_Input.outer = float2(-10000, -10000);
Unity_UIE_RenderTypeSwitchNode_SvgGradient_Input.inner = float2(-10000, -10000);
CommonFragOutput Unity_UIE_RenderTypeSwitchNode_Output = uie_std_frag_svg_gradient(Unity_UIE_RenderTypeSwitchNode_SvgGradient_Input);
_RenderTypeBranch_d1b1e877eeff451b928a39fae1df6953_Color_5_Vector3 = Unity_UIE_RenderTypeSwitchNode_Output.color.rgb * IN.color.rgb;
_RenderTypeBranch_d1b1e877eeff451b928a39fae1df6953_Alpha_6_Float = Unity_UIE_RenderTypeSwitchNode_Output.color.a * IN.color.a;
}
少し冗長に見えますが、
-
Universl/MaterialをUIにした時点でバリアントは生まれてしまう - 公式ドキュメントに、何も接続しなければ分岐をよしなにしてくれるとの記載
-
MainPreviewが恐らくこれを用いてプレビューしている
から、ボトルネックにならない限りはこのままでも良さそうです。
UIToolkitに反映させる
制作したShaderGraphをUIToolkitに反映させます。
右クリックから、
Create/UI Toolkit/UI Docment(UXML) を押下します。
生成されたファイルをダブルクリックで、UIBuilderを開きます。
そして、ImageをHierarchyにドラッグアンドドロップします。
ImageのInspectorを開き、
Sourceの項目にUIとして表示させたい画像を、Materialに今回制作したShaderGraphのマテリアルをアタッチします。
Materialの+ボタンを押下し、ShaderGraph側で設定したPropertyであるValueを選択します。
結果
添付動画のようにUIToolkitで自作したShaderGraphが動いていれば成功です。
Filterについて
ポストプロセスのようなことが出来るFilterもUnity6.3から追加されました。
Filterの追加方法
といっても追加方法は簡単で、UIBuilderを開き
Filter/Functionで任意のフィルターを選択するだけです。
組み合わせることもでき、このような形でFilterをかけることができます。
Filterをコードで書く
コードで書くことも出来るので、コードでも書いてみます。
まずはUIBuilderを開き、左上の+ボタンから
Create New USS を押します。
生成されたUSSをIDE等で開き、以下のように記載します。
.hover-transition {
filter: blur(0) invert(0%);
transition: filter 0.5s;
}
.hover-transition:hover {
filter: blur(10px) invert(100%);
}
UIBuilderに戻り、StyleClassListに
.hover-transition と入力し、エンターキーを押します。
以下のように入力されたものが追加されていれば成功です。
UnityからGameObjectを作り、
-
UI Documentをアタッチ - 制作したUXMLを
Source Assetにアタッチ
の操作を行います。
結果
コードでFilterを適応させることができました。
また、マウスカーソルを重ねたときにFilterがかかっています。
自作Filterの作成
自作したシェーダーを、Filterとして適応させてみます。
筆者が確認した限り、ShaderGraphの対応はしていなさそうでした。
なので今回は.shaderで制作します。
シェーダーの作成
テクスチャをサンプリングし、パラメータによって色を変えることの出来るシンプルなシェーダーを用意します。
UIToolkitには強制的にGammaに変換する Force Gammma Rendering というパラメータがあります。
Linearワークフローで作業している場合、UIE_OUTPUT_LINEAR というディレクティブを追加することで、このパラメータに対応することができます。
Shader "UI/Filters/Color"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags
{
"RenderType"="Transparent"
"Queue"="Transparent"
"RenderPipeline"="UniversalPipeline"
}
Cull Off
ZWrite Off
ZTest Always
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _ UIE_OUTPUT_LINEAR
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct appdata
{
float4 positionOS : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 positionCS : SV_POSITION;
};
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
float4 _MainTex_ST;
half3 _Color;
float _Intensity;
half3 GammaToLinearSpace (half3 sRGB)
{
return sRGB * (sRGB * (sRGB * 0.305306011h + 0.682171111h) + 0.012522878h);
}
v2f vert (appdata v)
{
v2f o;
o.positionCS = TransformObjectToHClip(v.positionOS.xyz);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
half4 frag (v2f i) : SV_Target
{
float2 uv = i.uv;
half4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv);
col.rgb *= _Color * _Intensity;
// UIToolkit
#ifdef UIE_OUTPUT_LINEAR
col.rgb = GammaToLinearSpace(col.rgb);
#endif
return col;
}
ENDHLSL
}
}
}
FilterFunctionDefinition の生成
シェーダーで制作したものをUIToolkitに反映させるため、FilterFunctionDefinitionを制作します。
これはUIToolkitで、どのマテリアルの何のパラメータを操作できるようにするかを明示するものです。
まずは、Projectウィンドウで右クリックを押下し、
Create/UI Toolkit/Filter Function Definition を押下します。
そうすると、FilterFunctionDefinitionが生成されるので以下のように入力します。
パラメータの詳細は以下です。
| 名前 | 意味 |
|---|---|
| Filter Name | フィルター名 |
| Parameters | |
| - Name | UIToolkit上で表示される名前 |
| - Type | このパラメータの型 float もしくは color |
| - Interpolation Default | デフォルト値 |
| Passes | |
| - Material | このFilterのシェーダーのマテリアル |
| - Pass Index | シェーダーのPassのIndex |
| - Parameter Bindings | |
| - - Index | Nameに紐付くParametersのIndex |
| - - Name | シェーダーのパラメータ名 |
FilterFunctionDefinition をScriptで生成する
詳細は割愛しますが、C#で生成出来たほうが便利なケースもあるかと思います。
ですので、サンプルコードを乗せます。
using UnityEngine;
using UnityEditor;
using UnityEngine.UIElements;
public class CreateFilter : Editor
{
[MenuItem("Assets/Create/UI Toolkit/Create Custom Filter")]
public static void CreateColorFilterAsset()
{
var shader = Shader.Find("UI/Filters/Color");
var material = new Material(shader)
{
name = "colorFilterMaterial"
};
AssetDatabase.CreateAsset(material, "Assets/colorFilterMaterial.mat");
var colorFilter = CreateInstance<FilterFunctionDefinition>();
colorFilter.name = "colorFilter";
colorFilter.parameters = new[]
{
// 操作できるパラメータ
new FilterParameterDeclaration
{
name = "color", // 名前
interpolationDefaultValue = new FilterParameter(Color.black) // デフォルト値
},
new FilterParameterDeclaration
{
name = "intensity",
interpolationDefaultValue = new FilterParameter(1f)
}
};
// レンダリングパス定義
colorFilter.passes = new[]
{
new PostProcessingPass
{
material = material,
passIndex = 0, // ShaderのPassのindex
parameterBindings = new[]
{
new ParameterBinding
{
index = 0, // parameters配列のインデックス
name = "_Color" // シェーダープロパティ名
},
new ParameterBinding
{
index = 0,
name = "_Intensity"
}
}
}
};
AssetDatabase.CreateAsset(colorFilter, "Assets/colorFilter.asset");
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Selection.activeObject = colorFilter;
}
}
UIBuilderの操作
UIBuilderに戻り、Filterを以下のように設定します。
- Function :
Custom - Definition : 生成したFilterFunctionDefinition
結果
動画のようにパラメータによって画像の色が変わっていれば成功です。
最後に
UI Toolkit × Shader Graph / Filter 対応のおかげで、UIToolkitも段々扱いやすくなってきたのではないでしょうか。
今回の記事が皆さんのプロジェクトでお役に立てば幸いです。
明日は、 @Haru184 さんの記事になります。お楽しみに!!

















