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 システムで使用する固有の演算式ですのでそのまま作成してください。
「AnyPortrait v1.6.0」では、「マスク」機能が大幅に改善されました。
追加されたマスク機能をサポートするには、クリッピングシェーダーとアルファマスクシェーダーを変更する必要があります。
マスク機能については、関連ページを参照してください。
「AnyPortrait v1.6.0」以降、デフォルトで提供されている「v16マテリアルセット」は、以前のバージョンと比較してクリッピング関連のコードが大幅に変更されました。
「v16 マテリアルセット」の「クリッピングシェーダー」と「アルファマスクシェーダー」のコードを確認し、カスタムシェーダーを作成する際に活用してみてください。
Shader "AnyPortrait/Unlit (v16)/AlphaBlend Clipped"
{
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)
_MaskRatio ("Mask Ratio", Range(0, 1)) = 0
// 4つのマスクチャンネル
_MaskRatio_1 ("Mask Ratio Ch1", Range(0, 1)) = 0
_MaskTex_1 ("Mask Texture Ch1", 2D) = "black" {}
_MaskScreenSpaceOffset_1 ("Mask Screen Space Offset Ch1", Vector) = (0, 0, 0, 1)
_MaskOp_1 ("Mask Operation Ch1", Range(0, 3)) = 0
_MaskRatio_2 ("Mask Ratio Ch2", Range(0, 1)) = 0
_MaskTex_2 ("Mask Texture Ch2", 2D) = "black" {}
_MaskScreenSpaceOffset_2 ("Mask Screen Space Offset Ch2", Vector) = (0, 0, 0, 1)
_MaskOp_2 ("Mask Operation Ch2", Range(0, 3)) = 0
_MaskRatio_3 ("Mask Ratio Ch3", Range(0, 1)) = 0
_MaskTex_3 ("Mask Texture Ch3", 2D) = "black" {}
_MaskScreenSpaceOffset_3 ("Mask Screen Space Offset Ch3", Vector) = (0, 0, 0, 1)
_MaskOp_3 ("Mask Operation Ch3", Range(0, 3)) = 0
_MaskRatio_4 ("Mask Ratio Ch4", Range(0, 1)) = 0
_MaskTex_4 ("Mask Texture Ch4", 2D) = "black" {}
_MaskScreenSpaceOffset_4 ("Mask Screen Space Offset Ch4", Vector) = (0, 0, 0, 1)
_MaskOp_4 ("Mask Operation Ch4", Range(0, 3)) = 0
// See-Through効果
_SeeThroughRatio ("See-Through Ratio", Range(0, 1)) = 0.0
_SeeThroughTex ("See-Through Texture", 2D) = "black" {}
_SeeThroughScreenSpaceOffset ("See-Through Screen Space Offset (XY_Scale)", Vector) = (0, 0, 0, 1)
_SeeThroughAlpha ("See-Through Alpha", Range(0, 1)) = 0.0
}
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;
float _MaskRatio;
// 4つのマスクチャネルプロパティ変数
float _MaskRatio_1;
sampler2D _MaskTex_1;
float4 _MaskScreenSpaceOffset_1;
float _MaskOp_1;
float _MaskRatio_2;
sampler2D _MaskTex_2;
float4 _MaskScreenSpaceOffset_2;
float _MaskOp_2;
float _MaskRatio_3;
sampler2D _MaskTex_3;
float4 _MaskScreenSpaceOffset_3;
float _MaskOp_3;
float _MaskRatio_4;
sampler2D _MaskTex_4;
float4 _MaskScreenSpaceOffset_4;
float _MaskOp_4;
// 「See-Through」プロパティ変数
float _SeeThroughRatio;
sampler2D _SeeThroughTex;
float4 _SeeThroughScreenSpaceOffset;
float _SeeThroughAlpha;
// マスク関数:クリッピングマスクアルファを計算
half GetMaskAlpha (float alpha, float ratio)
{
return saturate((alpha * ratio) + (1.0f * (1.0f - ratio)));
}
// マスク関数: 演算子によるチャネル別マスクの計算
half GetMaskAlphaByOp (float prevMask, float alpha, float ratio, float op)
{
float opWeight_And = saturate(1.0f - abs(op - 0.0f));//AND
float opWeight_Or = saturate(1.0f - abs(op - 1.0f));//OR
float opWeight_InvAnd = saturate(1.0f - abs(op - 2.0f));//Inverse AND
float opWeight_InvOr = saturate(1.0f - abs(op - 3.0f));//Inverse OR
float inverseAlpha = 1.0f - alpha;
float nextAlpha_And = saturate(prevMask * alpha);//Multiply
float nextAlpha_Or = saturate(prevMask + (alpha * (1.0f - prevMask)));//Add Blended
float nextAlpha_InvAnd = saturate(prevMask * inverseAlpha);//Multiply (Inverse)
float nextAlpha_InvAOr = saturate(prevMask + (inverseAlpha * (1.0f - prevMask)));//Add Blended (Inverse)
float resultMask = (nextAlpha_And * opWeight_And) + (nextAlpha_Or * opWeight_Or) + (nextAlpha_InvAnd * opWeight_InvAnd) + (nextAlpha_InvAOr * opWeight_InvOr);
return saturate((resultMask * ratio) + (prevMask * (1.0f - ratio)));
}
// マスク関数:マスクレンダリングテクスチャのUVを計算
float2 GetMaskScreenUV (float2 screenUV, float4 offset)
{
float2 result = screenUV - float2(0.5f, 0.5f);
result.x *= offset.z;
result.y *= offset.w;
result.x += offset.x * offset.z;
result.y += offset.y * offset.w;
result += float2(0.5f, 0.5f);
return result;
}
// マスク関数:「See-Through」効果を計算
half3 GetSeeThroughColor (half3 mainColor, half4 seeThroughColor)
{
float stAlpha = saturate(seeThroughColor.a * _SeeThroughAlpha * _SeeThroughRatio);
return (mainColor * (1.0f - stAlpha)) + (seeThroughColor.rgb * stAlpha);
}
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);
// クリッピングマスクのための「Screen Position」
float2 screenUV = i.screenPos.xy / max(i.screenPos.w, 0.0001f);
// 「See-Through」効果
half4 seeThroughColor = tex2D(_SeeThroughTex, GetMaskScreenUV(screenUV, _SeeThroughScreenSpaceOffset));
col.rgb = GetSeeThroughColor(col.rgb, seeThroughColor);
#if UNITY_COLORSPACE_GAMMA
// 色演算 (Gamma Space)
col.rgb *= _Color.rgb * 2.0f;
#else
// 色演算 (Linear Space)
col.rgb *= _Color.rgb * 4.595f;
col.rgb = pow(col.rgb, 2.2f);
#endif
col.a *= _Color.a;
// アルファマスク計算コード
float maskResult = 1.0f;
// 1. クリッピングマスク
half maskClipped = tex2D(_MaskTex, GetMaskScreenUV(screenUV, _MaskScreenSpaceOffset)).r;
maskResult *= GetMaskAlpha(saturate(maskClipped), saturate(_MaskRatio));
// 2. 4つのチャンネル別マスク
float maskCh1 = tex2D(_MaskTex_1, GetMaskScreenUV(screenUV, _MaskScreenSpaceOffset_1)).r;
float maskCh2 = tex2D(_MaskTex_2, GetMaskScreenUV(screenUV, _MaskScreenSpaceOffset_2)).r;
float maskCh3 = tex2D(_MaskTex_3, GetMaskScreenUV(screenUV, _MaskScreenSpaceOffset_3)).r;
float maskCh4 = tex2D(_MaskTex_4, GetMaskScreenUV(screenUV, _MaskScreenSpaceOffset_4)).r;
maskResult = GetMaskAlphaByOp(maskResult, saturate(maskCh1), saturate(_MaskRatio_1), _MaskOp_1);
maskResult = GetMaskAlphaByOp(maskResult, saturate(maskCh2), saturate(_MaskRatio_2), _MaskOp_2);
maskResult = GetMaskAlphaByOp(maskResult, saturate(maskCh3), saturate(_MaskRatio_3), _MaskOp_3);
maskResult = GetMaskAlphaByOp(maskResult, saturate(maskCh4), saturate(_MaskRatio_4), _MaskOp_4);
col.a *= maskResult;
return col;
}
ENDCG
}
(Shadow Caster Passは省略)
}
}
上記のシェーダは、「v16 マテリアルセット」に含まれるクリッピングシェーダの基本コードです。
以前に比べてマスク関連のプロパティが多く追加されました。
また、影を生成する「パス」が追加されました。
(ただし、ここでは「シャドウ生成パス」の説明は省略します。)
1. プロパティ
_MaskTex ("Mask Texture (A)", 2D) = "white" {}
_MaskScreenSpaceOffset ("Mask Screen Space Offset (XY_Scale)", Vector) = (0, 0, 0, 1)
_MaskRatio ("Mask Ratio", Range(0, 1)) = 0
_MaskRatio_1 ("Mask Ratio Ch1", Range(0, 1)) = 0
_MaskTex_1 ("Mask Texture Ch1", 2D) = "black" {}
_MaskScreenSpaceOffset_1 ("Mask Screen Space Offset Ch1", Vector) = (0, 0, 0, 1)
_MaskOp_1 ("Mask Operation Ch1", Range(0, 3)) = 0
: アルファマスク操作のプロパティが以前のバージョンと比較して増加しました。
既存と同じ「_MaskTex」、「_MaskScreenSpaceOffset」に加えて、該当チャンネル(クリッピングと4つの追加チャンネル)のマスクを使用するかどうかを決定する「_MaskRatio」プロパティーが追加されました。
また、4つのチャンネルには演算オプションが追加され、「AND」、「OR」などのマスクの組み合わせが可能になりました。
このための「_MaskOp_n」プロパティに関連するコードも確認してください。
_SeeThroughRatio ("See-Through Ratio", Range(0, 1)) = 0.0
_SeeThroughTex ("See-Through Texture", 2D) = "black" {}
_SeeThroughScreenSpaceOffset ("See-Through Screen Space Offset (XY_Scale)", Vector) = (0, 0, 0, 1)
_SeeThroughAlpha ("See-Through Alpha", Range(0, 1)) = 0.0
: 「See-Through」機能が追加されました。
アルファマスクと似ていますが、RGBAチャネルをすべて使用し、RGBチャネル値に重なる特性があります。
2. マスク演算関数
half GetMaskAlpha (float alpha, float ratio)
: クリッピングマスクを操作する関数です。
「_MaskRatio」に従って「Alpha」の適用度合いを調整します。
half GetMaskAlphaByOp (float prevMask, float alpha, float ratio, float op)
: 4つのチャンネルごとのマスクを演算する関数です。
演算オプションによって異なる式が適用されます。
float2 GetMaskScreenUV (float2 screenUV, float4 offset)
: 「Screen Space」を活用するマスクを参照するためにUV演算を行う関数です。
マスクごとに個別のUV最適化値(MaskScreenSpaceOffset)があるため、この関数を使用してUVを変換する必要があります。
half3 GetSeeThroughColor (half3 mainColor, half4 seeThroughColor)
: 「See-Through」効果を計算する関数です。
Shader "AnyPortrait/Unlit (v16)/AlphaMask"
{
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)
_MaskRatio ("Mask Ratio", Range(0, 1)) = 0
// マスクチェーンによる4つのマスクチャンネル
_MaskRatio_1 ("Mask Ratio Ch1", Range(0, 1)) = 0
_MaskTex_1 ("Mask Texture Ch1", 2D) = "black" {}
_MaskScreenSpaceOffset_1 ("Mask Screen Space Offset Ch1", Vector) = (0, 0, 0, 1)
_MaskOp_1 ("Mask Operation Ch1", Range(0, 3)) = 0
_MaskRatio_2 ("Mask Ratio Ch2", Range(0, 1)) = 0
_MaskTex_2 ("Mask Texture Ch2", 2D) = "black" {}
_MaskScreenSpaceOffset_2 ("Mask Screen Space Offset Ch2", Vector) = (0, 0, 0, 1)
_MaskOp_2 ("Mask Operation Ch2", Range(0, 3)) = 0
_MaskRatio_3 ("Mask Ratio Ch3", Range(0, 1)) = 0
_MaskTex_3 ("Mask Texture Ch3", 2D) = "black" {}
_MaskScreenSpaceOffset_3 ("Mask Screen Space Offset Ch3", Vector) = (0, 0, 0, 1)
_MaskOp_3 ("Mask Operation Ch3", Range(0, 3)) = 0
_MaskRatio_4 ("Mask Ratio Ch4", Range(0, 1)) = 0
_MaskTex_4 ("Mask Texture Ch4", 2D) = "black" {}
_MaskScreenSpaceOffset_4 ("Mask Screen Space Offset Ch4", Vector) = (0, 0, 0, 1)
_MaskOp_4 ("Mask Operation Ch4", Range(0, 3)) = 0
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue" = "Transparent" "PreviewType" = "Plane" }
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
Cull 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;
float _MaskRatio;
// 4つのマスクチャネルプロパティ変数
float _MaskRatio_1;
sampler2D _MaskTex_1;
float4 _MaskScreenSpaceOffset_1;
float _MaskOp_1;
float _MaskRatio_2;
sampler2D _MaskTex_2;
float4 _MaskScreenSpaceOffset_2;
float _MaskOp_2;
float _MaskRatio_3;
sampler2D _MaskTex_3;
float4 _MaskScreenSpaceOffset_3;
float _MaskOp_3;
float _MaskRatio_4;
sampler2D _MaskTex_4;
float4 _MaskScreenSpaceOffset_4;
float _MaskOp_4;
// マスク関数:クリッピングマスクアルファ計算
half GetMaskAlpha (float alpha, float ratio)
{
return saturate((alpha * ratio) + (1.0f * (1.0f - ratio)));
}
// マスク関数:演算子によるチャネル別マスクの計算
half GetMaskAlphaByOp (float prevMask, float alpha, float ratio, float op)
{
float opWeight_And = saturate(1.0f - abs(op - 0.0f));//AND
float opWeight_Or = saturate(1.0f - abs(op - 1.0f));//OR
float opWeight_InvAnd = saturate(1.0f - abs(op - 2.0f));//Inverse AND
float opWeight_InvOr = saturate(1.0f - abs(op - 3.0f));//Inverse OR
float inverseAlpha = 1.0f - alpha;
float nextAlpha_And = saturate(prevMask * alpha);//Multiply
float nextAlpha_Or = saturate(prevMask + (alpha * (1.0f - prevMask)));//Add Blended
float nextAlpha_InvAnd = saturate(prevMask * inverseAlpha);//Multiply (Inverse)
float nextAlpha_InvAOr = saturate(prevMask + (inverseAlpha * (1.0f - prevMask)));//Add Blended (Inverse)
float resultMask = (nextAlpha_And * opWeight_And) + (nextAlpha_Or * opWeight_Or) + (nextAlpha_InvAnd * opWeight_InvAnd) + (nextAlpha_InvAOr * opWeight_InvOr);
return saturate((resultMask * ratio) + (prevMask * (1.0f - ratio)));
}
// マスク関数:マスクレンダリングテクスチャのUVを計算
float2 GetMaskScreenUV (float2 screenUV, float4 offset)
{
float2 result = screenUV - float2(0.5f, 0.5f);
result.x *= offset.z;
result.y *= offset.w;
result.x += offset.x * offset.z;
result.y += offset.y * offset.w;
result += float2(0.5f, 0.5f);
return result;
}
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 = fixed3(1.0f, 1.0f, 1.0f);
col.a = saturate(col.a * _Color.a);
// マスクチェーンのためのマスク操作
float2 screenUV = i.screenPos.xy / max(i.screenPos.w, 0.0001f);
float maskResult = 1.0f;
// 1. クリッピングマスク
half maskClipped = tex2D(_MaskTex, GetMaskScreenUV(screenUV, _MaskScreenSpaceOffset)).r;
maskResult *= GetMaskAlpha(saturate(maskClipped), saturate(_MaskRatio));
// 2. 4つのチャンネル別マスク
float maskCh1 = tex2D(_MaskTex_1, GetMaskScreenUV(screenUV, _MaskScreenSpaceOffset_1)).r;
float maskCh2 = tex2D(_MaskTex_2, GetMaskScreenUV(screenUV, _MaskScreenSpaceOffset_2)).r;
float maskCh3 = tex2D(_MaskTex_3, GetMaskScreenUV(screenUV, _MaskScreenSpaceOffset_3)).r;
float maskCh4 = tex2D(_MaskTex_4, GetMaskScreenUV(screenUV, _MaskScreenSpaceOffset_4)).r;
maskResult = GetMaskAlphaByOp(maskResult, saturate(maskCh1), saturate(_MaskRatio_1), _MaskOp_1);
maskResult = GetMaskAlphaByOp(maskResult, saturate(maskCh2), saturate(_MaskRatio_2), _MaskOp_2);
maskResult = GetMaskAlphaByOp(maskResult, saturate(maskCh3), saturate(_MaskRatio_3), _MaskOp_3);
maskResult = GetMaskAlphaByOp(maskResult, saturate(maskCh4), saturate(_MaskRatio_4), _MaskOp_4);
col.a *= maskResult;
return col;
}
ENDCG
}
}
}
上記のシェーダは、「v16マテリアルセット」のアルファマスクシェーダの基本コードです。
従来は単に白に「Alpha」チャンネルのみ演算する役割だけを行いましたが、「AnyPortrait v1.6.0」ではマスク関連コードが追加されました。
これは「マスクチェーン」機能をサポートするためです。 (関連ページ)
クリッピング関連コードはクリッピングシェーダと同じですが、「Alpha」チャンネルとは無関係の「See-Through」効果はこのシェーダから除外されます。
「キーワード」を使用して、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」をして実行すると、上記のようにキーワードが有効かどうかによってレンダリング結果が変わることがわかります。