【Unity 2D】マテリアルをMaskにすることは出来ないのでしょうか
解決済
回答 1
投稿
- 評価
- クリップ 0
- VIEW 5,984
前提・実現したいこと
ジャギーのない円マスクを作成したい
ジャギーの少ない円形しシェーダーを作成し、マテリアルに付加。
これを使ってイメージとして円表示ができる。
マスクとして利用すると、イメージが前面に出てきて裏のイメージが表示できない
発生している問題・エラーメッセージ
エラーメッセージ
該当のソースコード
赤丸がマスク。
マスクをオフにすると裏のイメージが表示されます。
シェーダーのソース
Shader "Unlit/Circle"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
ColorMask RGB
ZWrite Off
Cull Off
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
// Quality level
// 2 == high quality
// 1 == medium quality
// 0 == low quality
#define QUALITY_LEVEL 1
struct appdata
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord - 0.5;
return o;
}
fixed4 _Color;
fixed4 frag (v2f i) : SV_Target
{
float dist = length(i.uv);
#if QUALITY_LEVEL == 2
// length derivative, 1.5 pixel smoothstep edge
float pwidth = length(float2(ddx(dist), ddy(dist)));
float alpha = smoothstep(0.5, 0.5 - pwidth * 1.5, dist);
#elif QUALITY_LEVEL == 1
// fwidth, 1.5 pixel smoothstep edge
float pwidth = fwidth(dist);
float alpha = smoothstep(0.5, 0.5 - pwidth * 1.5, dist);
#else // Low
// fwidth, 1 pixel linear edge
float pwidth = fwidth(dist);
float alpha = saturate((0.5 - dist) / pwidth);
#endif
return fixed4(_Color.rgb, _Color.a * alpha);
}
ENDCG
}
}
}
試したこと
描画順の問題かと思い、stack over flowを参考に以下のように編集しました。
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
// required for UI.Mask
_StencilComp ("Stencil Comparison", Float) = 3
_Stencil ("Stencil ID", Float) = 1
_StencilOp ("Stencil Operation", Float) = 2
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_ColorMask ("Color Mask", Float) = 15
}
SubShader
{
Tags
{
"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
が、マテリアル自体が描画されなくなりました
補足情報(FW/ツールのバージョンなど)
Unity 20171.1.p3
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+1
マスク側のマテリアルを変更して動的に描いた図形でマスキングすることも可能なはずですが、たとえマスク図形の縁にアンチエイリアス処理を施してなめらかにしても、切り抜かれる側の縁はギザギザになってしまうかと思います。
Maskコンポーネントはステンシルバッファを利用してマスキング効果を実現しているようです。マスク側の描画時に同時に各ピクセルにステンシル値を書き込み、切り抜かれる側はそのピクセルのステンシル値に応じて描画する・しないを決定する仕掛けになっており、切り抜かれる側が縁のピクセルを描画する際に透明度をどうするべきか判断する手掛かりがないため、ジャギーが生じてしまうことになります。
ご提示のAliasing When Using UI Mask - Unity Answersは、切り抜きにMaskコンポーネントは使用せず、その代替として切り抜かれる側のイメージのマテリアルにマスキング用テクスチャを持たせる方法を提案しています。同様の手法がUnity uGUIで綺麗なマスク表現 - 渋谷ほととぎす通信にも紹介されておりましたので、ご参考になるかと思います。
サイトの例ではマスクをテクスチャとして与える方式ですが、これを動的に描いた円に置き換えるとこんな感じでしょうか。
Shader "UI/OvalMasked"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
_MaskCenterAndHalfExtents ("Mask Center / Half Extents", Vector) = (0.0,0.0,128.0,128.0) // 楕円の位置と水平・垂直半径のプロパティを追加
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_ColorMask ("Color Mask", Float) = 15
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
Cull Off
Lighting Off
ZWrite Off
ZTest [unity_GUIZTestMode]
Blend SrcAlpha OneMinusSrcAlpha
ColorMask [_ColorMask]
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#pragma multi_compile __ UNITY_UI_ALPHACLIP
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
float4 worldPosition : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
fixed4 _Color;
fixed4 _TextureSampleAdd;
float4 _ClipRect;
v2f vert(appdata_t v)
{
v2f OUT;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
OUT.worldPosition = v.vertex;
OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
OUT.texcoord = v.texcoord;
OUT.color = v.color * _Color;
return OUT;
}
sampler2D _MainTex;
float4 _MaskCenterAndHalfExtents;
fixed4 frag(v2f IN) : SV_Target
{
float2 center = _MaskCenterAndHalfExtents.xy; // 円の中心座標
float2 halfExtents = _MaskCenterAndHalfExtents.zw; // 円の水平方向半径、垂直方向半径
float2 relativePosition = IN.worldPosition.xy - center; // ピクセルの円の中心からの相対位置
float fragDistance = length(relativePosition); // ピクセルの円の中心からの距離
float2 edgePosition = normalize(relativePosition / halfExtents) * halfExtents; // ピクセルの方角におけるエッジの位置
float edgeDistance = length(edgePosition); // エッジの円の中心からの距離
float maskAlpha = 1.0 - smoothstep(edgeDistance - 0.5, edgeDistance + 0.5, fragDistance); // ピクセルがエッジよりも半ピクセル以上内側なら1、半ピクセル以上外側なら0、中間なら位置に応じた半透明
half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
color.a *= maskAlpha; // イメージのアルファにマスクをかける
#ifdef UNITY_UI_ALPHACLIP
clip (color.a - 0.001);
#endif
return color;
}
ENDCG
}
}
}
このようなマテリアルを、Maskコンポーネントではなくイメージの方のマテリアルに使用します。すると下図のように、縁の部分がなめらかに切り抜かれました。
渋谷ほととぎす通信さんでも言及されているように、この方式だと個々のイメージに個別にマスキングを行わなければならないのが弱点ですが、代わりにサイトでの例のように、グラデーションのかかったような任意のアルファでも切り抜ける利点があるかと思います。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.34%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2018/03/19 11:10
マスクに滑らかな円を出せばいいと思っていました。
言われてみると確かに、マスク自体を滑らかにしたところで意味がないですね。そもそも質問が的外れでした。
マスクコンポーネントは透明度を渡さないのならば、渋谷ほととぎす通信さんのようにシェーダー自体をマスクとして利用させる方法がスマート(他に方法がない?)のようです。
ご回答いただいたコードを勉強させて頂きます。
ありがとうございました!