AnyPortrait > スクリプト > カスタムシェーダー
AnyPortraitは一般的に使用されるさまざまなシェーダを提供しますが、プロジェクトによっては特殊な効果を加えたカスタムシェーダを作成する必要もあります。
AnyPortraitのわずかな規則に準拠している場合は、カスタムシェーダを簡単に作成して素晴らしいレンダリング結果を作成できます。
このページでは、AnyPortraitのシェーダ基本型がどのように設定されているかについて説明します。
この説明に基づいてカスタムシェーダを作成して適用してください。
作成したシェーダを適用したい場合は、「マテリアルライブラリ」を利用するか、メッシュのプロパティを変更するだけです。
シェーダを変更する方法については、次のマニュアルを確認してください。
- マテリアルライブラリ
- メッシュ用シェーダ(Shader)
メモ
このページでは、シェーダの作成方法については説明しません。
関連する内容については、Unityドキュメントやフォーラム、関連サイトをご覧ください!
AnyPortraitは、1つのシェーダを使用するのではなく、複数のシェーダのうちの状況に合ったシェーダを使用します。
シェーダが派生する3つの要因に応じて、合計17個のシェーダが1つのセットを成して適用されます。 (関連ページ)
その要因は次のとおりです。
1. カラースペース(Color Space)
プロジェクトの色空間によってシェーダが決まります。
「Gamma Space」に基づいて作成され、「Linear Space」の場合は色演算式が変更されます。
2. ブレンディング(Blending)
メッシュは、4種類のブレンドのいずれかの方法でレンダリングされます。
デフォルトの「Alpha Blend」に基づいて作成され、「Additive」、「Soft Additive」、「Multiplicative」では、ブレンド属性とアルファ値演算に関連するコードが少し変更されます。
3. クリッピング(Clipping)
クリッピングメッシュはマスクテクスチャ情報を受け取り、アルファ演算をさらに実行します。
マスクを生成する1つのシェーダも含まれていますが、これは固定された役割を果たす機能性シェーダです。
上記の要因が組み合わせられ、合計17個のシェーダがキャラクターに適用されます。
しかし、カスタムシェーダーを作成したい場合は、あえてすべてのシェーダーを作成する必要はありません。
次の点をチェックして、あなたのプロジェクトの状況に合ったシェーダーだけを作成します。
- プロジェクトの色空間に応じて、GammaやLinearのいずれかのタイプのシェーダーのみ作成してください。
- ブレンディングオプションを使用しない場合は、基本的な方法であるAlpha Blendシェーダーを作成するだけです。
- クリッピングメッシュがない場合は、クリッピングシェーダを作成する必要はありません。
- クリッピングマスクを作るAlpha Maskシェーダは固定された役割のみを行うので、カスタムシェーダで制作する必要はありません。
このページでは、デフォルトの「Gamma Space」で「Alpha Blend」シェーダを作成するための規則と方法について最初に説明します。
そして、シェーダを決定する要因に応じて修正する必要がある部分をそれぞれ説明します。
AnyPortraitパッケージに組み込まれているシェーダコードを確認しておくと便利です。
シェーダーアセットはデフォルトで「Assets/AnyPortrait/Assets/Shaders」にあります。
提供されるシェーダを複製して必要な部分を修正すると便利です。
AnyPortraitのデフォルトシェーダは、UnityのデフォルトのTransparentシェーダとは大きく変わりません。
次のコードは「Gamma Space」の「Alpha Blend」の基本シェーダであり、これを元にカスタムコードを追加すればよいでしょう。
メモ
AnyPortraitは「Surface Shader」と「Fragment Shader」と「シェーダーグラフ」の両方をサポートしています。
このページでは「Fragment Shader」のコードに基づいて説明し、ここで説明するルールを状況に合わせて適切に適用すればよいでしょう。
Shader "Sample Shader/Gamma Space - Normal - AlphaBlend"
{
Properties
{
_Color ("2X Color (RGBA Mul)", Color) = (0.5, 0.5, 0.5, 1.0)
_MainTex ("Main Texture (RGBA)", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType" = "Transparent" "Queue" = "Transparent" "PreviewType" = "Plane" }
Blend SrcAlpha OneMinusSrcAlpha
LOD 200
Pass
{
Tags { "LightMode" = "ForwardBase" }
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
half4 _Color;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
col.rgb *= _Color.rgb * 2.0f;
col.a *= _Color.a;
return col;
}
ENDCG
}
Pass
{
Tags { "LightMode" = "ShadowCaster" }
ZWrite On
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
V2F_SHADOW_CASTER;
};
sampler2D _MainTex;
float4 _MainTex_ST;
half4 _Color;
v2f vert(appdata_base v)
{
v2f o;
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
float4 frag(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
col.a *= _Color.a;
if(col.a < 0.05f)
{
discard;
}
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
}
シェーダコードの主要部分を見てみましょう。
1. プロパティ
_Color ("2X Color (RGBA Mul)", Color) = (0.5, 0.5, 0.5, 1.0)
_MainTex ("Main Texture (RGBA)", 2D) = "white" {}
Unityのデフォルトの「Transparent」シェーダで使用されるプロパティであり、AnyPortraitで使用されるプロパティです。
クリッピングされるメッシュではなく一般的な場合は、これらのプロパティのみを使用します。
したがって、カスタムシェーダでは、これら2つのプロパティに関連するすべてのコードをそのまま作成する必要があります。
また、AnyPortraitのシステムはこれらのプロパティを制御するため、他のスクリプトでこれらのプロパティを変更しないでください。
2. 基本パス
Pass
{
Tags { "LightMode" = "ForwardBase" }
ZWrite Off
...
: 基本レンダリングパスのコードです。
上記のようにパスを明示したのは、影生成のためのパスが別途存在するためです。
シャドウパスを作成しない場合は、タグ「{ "LightMode" = "ForwardBase"}」を省略してください。
「Z Write」をしないので、この値を「Off」に設定します。
3. ブレンディング
Blend SrcAlpha OneMinusSrcAlpha
: 「Alpha Blend」シェーダのブレンディングコードです。
4. 色演算
fixed4 col = tex2D(_MainTex, i.uv);
col.rgb *= _Color.rgb * 2.0f;
col.a *= _Color.a;
return col;
: AnyPortraitの色演算式は「2X Multiply」です。
したがって、RGBにはさらに2を掛ける必要があります。
このコードはブレンドによって変わります。
5. シャドウパス
Pass
{
Tags { "LightMode" = "ShadowCaster" }
ZWrite On
...
}
: 「Alpha Blend」メッシュの場合は、オプションに応じてシャドウを作成できるはずです。
したがって、影を生成する「ShadowCaster」パスを作成する必要があります。
このパスはシャドウを生成する役割のみを行うため、上記の例のように作成するだけです。
「Surface Shader」方式で作成する場合は、このパスを作成する必要はありません。
上記の例は「Gamma Space」のシェーダーコードです。
上記の基本コードで、基本パス(「ForwardBase」)の「frag」関数を次のように修正しましょう。
...
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
col.rgb *= _Color.rgb * 4.595f;
col.rgb = pow(col.rgb, 2.2f);
col.a *= _Color.a;
return col;
}
...
「Linear Space」では、「Gamma Space」と比較して、色に「2.2」の二乗を加えた値が適用されると、元の画像の色に近づいて表示されます。
そのため、上記のように「2X Multiply」演算規則であるにもかかわらず、「2」ではなく「4.595」を掛け、最終RGBも「2.2」の二乗をしてくれます。
この変更点はブレンディングの種類に関係なく適用する必要があります。
メッシュのオプションを変更すると、デフォルトのブレンド「Alpha Blend」に加えて、「Additive」、「Soft Additive」、「Multiplicative」の方法でレンダリングできます。
ブレンディング方式によってシェーダをそれぞれ作成する必要がありますが、上記の「Alpha Blend」のコードを少し修正して楽に作成できます。
「Alpha Blend」以外のブレンドオプションは主に「視覚効果」のためであるため、影を生成するパスを作成しないことをお勧めします。
3種類のブレンディングについては、シェーダ内の「Blend」と「frag」関数の一部のコードを修正すればよい。
また、「Alpha Blend」とは異なり、シャドウ生成パスを省略するので、「LightMode」コードを削除するだけです。
次の例には3つのブレンドのコードがすべて含まれているので、コメントを確認して必要なコードをまとめるだけです。
...
SubShader
{
Tags { "RenderType" = "Transparent" "Queue" = "Transparent" "PreviewType" = "Plane" }
// --- ブレンディングの種類に応じて選択して作成 ---
Blend One One // Additive
Blend OneMinusDstColor One // Soft Additive
Blend DstColor SrcColor // Multiplicative
LOD 200
Pass
{
// --- パス指定コードの削除 ---
// Tags { "LightMode" = "ForwardBase" }
ZWrite Off
CGPROGRAM
...
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
col.rgb *= _Color.rgb * 2.0f;
col.a *= _Color.a;
// --- Additive または Soft Additive ---
col.rgb *= col.a;
col.a = 1.0f;
// ---------------------------------
// --- Multiplicative ---
col.rgb = col.rgb * (col.a) + float4(0.5f, 0.5f, 0.5f, 1.0f) * (1.0f - col.a);
col.a = 1.0f;
// ----------------------
return col;
}
ENDCG
}
// シャドウ生成パスコードの削除
}
}
1. ブレンディングオプション
Blend One One // Additive
Blend OneMinusDstColor One // Soft Additive
Blend DstColor SrcColor // Multiplicative
: メッシュのブレンドオプションに応じて、上記のコードのいずれかを選択できます。
(シェーダグラフを使用している場合は、「Soft Additive」がサポートされない場合があります。)
2. マルチパスコードの削除
// Tags { "LightMode" = "ForwardBase" }
: 影生成パスがなくなるので、マルチパスに関連するコードは削除してもよい。
シャドウ生成パスコードも削除します。
3. 色演算式の修正
(1) Additive, Soft Additive
col.rgb *= col.a;
col.a = 1.0f;
(2) Multiplicative
col.rgb = col.rgb * (col.a) + float4(0.5f, 0.5f, 0.5f, 1.0f) * (1.0f - col.a);
col.a = 1.0f;
: ブレンドに合わせて最終的な色を変更し、Alphaの値を1に変更します。
AdditiveとSoft Additiveの色演算式は互いに同じです。
クリッピングされるメッシュは別のシェーダによってレンダリングされます。
クリッピング用シェーダには、クリッピングマスクを生成するメッシュのレンダリング結果をテクスチャ形式で受け取り、一部がレンダリングされないようにする演算が追加されます。
クリッピング用のプロパティとクリッピング操作を基本シェーダに追加するだけです。
また、クリッピングシェーダは「Alpha Blend」でもシャドウを生成する必要はありませんので、シングルパスでのみ作成してください。
マスクメッシュの影の中に含まれるからです。
以下の例は、「Gamma Space」の「Alpha Blend」の場合のシェーダであり、上記の説明に合わせて変更できます。
Shader "Sample Shader/Gamma Space - Clipped - AlphaBlend"
{
Properties
{
_Color ("2X Color (RGBA Mul)", Color) = (0.5, 0.5, 0.5, 1.0)
_MainTex ("Main Texture (RGBA)", 2D) = "white" {}
// --- クリッピングプロパティ ---
_MaskTex ("Mask Texture (A)", 2D) = "white" {}
_MaskScreenSpaceOffset ("Mask Screen Space Offset (XY_Scale)", Vector) = (0, 0, 0, 1)
}
SubShader
{
Tags { "RenderType" = "Transparent" "Queue" = "Transparent" "PreviewType" = "Plane" }
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
LOD 200
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
// --- クリッピング操作のための「Screen Position」 ---
float4 screenPos : TEXCOORD1;
};
sampler2D _MainTex;
float4 _MainTex_ST;
half4 _Color;
// --- クリッピング用のプロパティ変数 ---
sampler2D _MaskTex;
float4 _MaskScreenSpaceOffset;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
// --- クリッピング用「Screen Position」の計算 ---
o.screenPos = ComputeScreenPos(o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
col.rgb *= _Color.rgb * 2.0f;
col.a *= _Color.a;
// --- クリッピングマスクによるアルファチャンネル演算 ---
float2 screenUV = i.screenPos.xy / max(i.screenPos.w, 0.0001f);
screenUV -= float2(0.5f, 0.5f);
screenUV.x *= _MaskScreenSpaceOffset.z;
screenUV.y *= _MaskScreenSpaceOffset.w;
screenUV.x += _MaskScreenSpaceOffset.x * _MaskScreenSpaceOffset.z;
screenUV.y += _MaskScreenSpaceOffset.y * _MaskScreenSpaceOffset.w;
screenUV += float2(0.5f, 0.5f);
col.a *= tex2D(_MaskTex, screenUV).r;
return col;
}
ENDCG
}
}
}
1. プロパティ
_MaskTex ("Mask Texture (A)", 2D) = "white" {}
_MaskScreenSpaceOffset ("Mask Screen Space Offset (XY_Scale)", Vector) = (0, 0, 0, 1)
: クリッピングマスク処理のために、マスクテクスチャをプロパティに追加します。
上記のようにプロパティを追加すると、AnyPortraitは自動的にクリッピングレンダリングを処理します。
「_MaskScreenSpaceOffset」は、クリッピングマスクを「画面座標系(Screen Space)」を基準に適用するためのプロパティです。
sampler2D _MaskTex;
float4 _MaskScreenSpaceOffset;
: 追加されたプロパティに合わせて変数も追加する必要があります。
2. Screen Space Position
float4 screenPos : TEXCOORD1;
: クリッピングマスクは画面座標系で処理されます。
したがって、頂点の画面座標系の位置を頂点シェーダで計算し、フラグメントシェーダに渡す必要があります。
o.screenPos = ComputeScreenPos(o.vertex);
: 頂点シェーダで画面座標系の位置を計算するコードを追加します。
3. マスクによるアルファチャンネル値の計算
float2 screenUV = i.screenPos.xy / max(i.screenPos.w, 0.0001f);
screenUV -= float2(0.5f, 0.5f);
screenUV.x *= _MaskScreenSpaceOffset.z;
screenUV.y *= _MaskScreenSpaceOffset.w;
screenUV.x += _MaskScreenSpaceOffset.x * _MaskScreenSpaceOffset.z;
screenUV.y += _MaskScreenSpaceOffset.y * _MaskScreenSpaceOffset.w;
screenUV += float2(0.5f, 0.5f);
col.a *= tex2D(_MaskTex, screenUV).r;
: クリッピングマスクを計算してアルファチャンネルに適用する式です。
AnyPortrait システムで使用する固有の演算式ですのでそのまま作成してください。
「キーワード」を使用して、1つのシェーダコードを複数のバージョンにコンパイルするようにして利用できます。
「AnyPortrait v1.5.1」に追加された機能を利用して、シェーダのキーワードを利用できます。
キーワードについては、次の Unity 公式文書に関する情報をご覧いただけます。
- Unityのマニュアル
以下は、「SPECIAL_COLOR」というキーワードによって色演算が変わるシェーダの例です。
Shader "Shader Example/Gamma Space - Normal - AlphaBlend (Keyword)"
{
Properties
{
_Color ("2X Color (RGBA Mul)", Color) = (0.5, 0.5, 0.5, 1.0)
_MainTex ("Main Texture (RGBA)", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType" = "Transparent" "Queue" = "Transparent" "PreviewType" = "Plane" }
Blend SrcAlpha OneMinusSrcAlpha
LOD 200
Pass
{
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile __ SPECIAL_COLOR
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
half4 _Color;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
col.rgb *= _Color.rgb * 2.0f;
col.a *= _Color.a;
#ifdef SPECIAL_COLOR
col.rgb *= fixed3(0.0f, 1.0f, 0.0f);
#endif
return col;
}
ENDCG
}
}
}
1. キーワード宣言
#pragma multi_compile __ SPECIAL_COLOR
: 「#pragma multi_compile」構文を使用してキーワードを宣言します。
この例では、「SPECIAL_COLOR」というキーワードを新しく宣言しました。
Unityでシェーダバリアントを生成する「multi_compile」と「shader_feature」ディレクティブを使用するか、動的ブランチを生成する「dynamic_branch」ディレクティブを使用してキーワードを利用できます。
ただし、AnyPortraitはマテリアルを動的に生成するため、アセットベースでシェーダバリアントをコンパイルする「shader_feature」は利用できません。
2. コード分岐の作成
#ifdef SPECIAL_COLOR
col.rgb *= fixed3(0.0f, 1.0f, 0.0f);
#endif
: キーワードによって異なる動作をするように作成します。
「multi_compile」を使用した場合は「#ifdef、#else、#elif、#endif」ディレクティブを使用し、「dynamic_branch」を使用した場合は「if()」文を使用します。
この例では、キーワード「SPECIAL_COLOR」が有効な場合、緑色のチャンネルの色のみをレンダリングするように作成しました。
キーワードを使用するには、「マテリアルライブラリ」で設定する必要があります。
詳細は関連ページで確認できます。
(1) マテリアルライブラリを開きます。
(2) 「SPECIAL_COLOR」という名前のプロパティオプションを追加し、「Keyword」タイプに設定してから「Enable」に設定します。
「Bake」をして実行すると、上記のようにキーワードが有効かどうかによってレンダリング結果が変わることがわかります。