質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%

Q&A

解決済

1回答

3997閲覧

【Unity 2D】マテリアルをMaskにすることは出来ないのでしょうか

mizko

総合スコア18

0グッド

0クリップ

投稿2018/03/18 03:53

前提・実現したいこと

ジャギーのない円マスクを作成したい

ジャギーの少ない円形しシェーダーを作成し、マテリアルに付加。
これを使ってイメージとして円表示ができる。

マスクとして利用すると、イメージが前面に出てきて裏のイメージが表示できない

発生している問題・エラーメッセージ

エラーメッセージ

該当のソースコード

イメージ説明
イメージ説明
赤丸がマスク。
マスクをオフにすると裏のイメージが表示されます。

シェーダーのソース

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

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

マスク側のマテリアルを変更して動的に描いた図形でマスキングすることも可能なはずですが、たとえマスク図形の縁にアンチエイリアス処理を施してなめらかにしても、切り抜かれる側の縁はギザギザになってしまうかと思います。

Maskコンポーネントはステンシルバッファを利用してマスキング効果を実現しているようです。マスク側の描画時に同時に各ピクセルにステンシル値を書き込み、切り抜かれる側はそのピクセルのステンシル値に応じて描画する・しないを決定する仕掛けになっており、切り抜かれる側が縁のピクセルを描画する際に透明度をどうするべきか判断する手掛かりがないため、ジャギーが生じてしまうことになります。

ご提示のAliasing When Using UI Mask - Unity Answersは、切り抜きにMaskコンポーネントは使用せず、その代替として切り抜かれる側のイメージのマテリアルにマスキング用テクスチャを持たせる方法を提案しています。同様の手法がUnity uGUIで綺麗なマスク表現 - 渋谷ほととぎす通信にも紹介されておりましたので、ご参考になるかと思います。

サイトの例ではマスクをテクスチャとして与える方式ですが、これを動的に描いた円に置き換えるとこんな感じでしょうか。

HLSL

1Shader "UI/OvalMasked" 2{ 3 Properties 4 { 5 [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} 6 _Color ("Tint", Color) = (1,1,1,1) 7 8 _MaskCenterAndHalfExtents ("Mask Center / Half Extents", Vector) = (0.0,0.0,128.0,128.0) // 楕円の位置と水平・垂直半径のプロパティを追加 9 10 _StencilComp ("Stencil Comparison", Float) = 8 11 _Stencil ("Stencil ID", Float) = 0 12 _StencilOp ("Stencil Operation", Float) = 0 13 _StencilWriteMask ("Stencil Write Mask", Float) = 255 14 _StencilReadMask ("Stencil Read Mask", Float) = 255 15 16 _ColorMask ("Color Mask", Float) = 15 17 18 [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0 19 } 20 21 SubShader 22 { 23 Tags 24 { 25 "Queue"="Transparent" 26 "IgnoreProjector"="True" 27 "RenderType"="Transparent" 28 "PreviewType"="Plane" 29 "CanUseSpriteAtlas"="True" 30 } 31 32 Stencil 33 { 34 Ref [_Stencil] 35 Comp [_StencilComp] 36 Pass [_StencilOp] 37 ReadMask [_StencilReadMask] 38 WriteMask [_StencilWriteMask] 39 } 40 41 Cull Off 42 Lighting Off 43 ZWrite Off 44 ZTest [unity_GUIZTestMode] 45 Blend SrcAlpha OneMinusSrcAlpha 46 ColorMask [_ColorMask] 47 48 Pass 49 { 50 CGPROGRAM 51 #pragma vertex vert 52 #pragma fragment frag 53 #pragma target 2.0 54 55 #include "UnityCG.cginc" 56 #include "UnityUI.cginc" 57 58 #pragma multi_compile __ UNITY_UI_ALPHACLIP 59 60 struct appdata_t 61 { 62 float4 vertex : POSITION; 63 float4 color : COLOR; 64 float2 texcoord : TEXCOORD0; 65 UNITY_VERTEX_INPUT_INSTANCE_ID 66 }; 67 68 struct v2f 69 { 70 float4 vertex : SV_POSITION; 71 fixed4 color : COLOR; 72 float2 texcoord : TEXCOORD0; 73 float4 worldPosition : TEXCOORD1; 74 UNITY_VERTEX_OUTPUT_STEREO 75 }; 76 77 fixed4 _Color; 78 fixed4 _TextureSampleAdd; 79 float4 _ClipRect; 80 81 v2f vert(appdata_t v) 82 { 83 v2f OUT; 84 UNITY_SETUP_INSTANCE_ID(v); 85 UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT); 86 OUT.worldPosition = v.vertex; 87 OUT.vertex = UnityObjectToClipPos(OUT.worldPosition); 88 89 OUT.texcoord = v.texcoord; 90 91 OUT.color = v.color * _Color; 92 return OUT; 93 } 94 95 sampler2D _MainTex; 96 float4 _MaskCenterAndHalfExtents; 97 98 fixed4 frag(v2f IN) : SV_Target 99 { 100 float2 center = _MaskCenterAndHalfExtents.xy; // 円の中心座標 101 float2 halfExtents = _MaskCenterAndHalfExtents.zw; // 円の水平方向半径、垂直方向半径 102 float2 relativePosition = IN.worldPosition.xy - center; // ピクセルの円の中心からの相対位置 103 float fragDistance = length(relativePosition); // ピクセルの円の中心からの距離 104 float2 edgePosition = normalize(relativePosition / halfExtents) * halfExtents; // ピクセルの方角におけるエッジの位置 105 float edgeDistance = length(edgePosition); // エッジの円の中心からの距離 106 float maskAlpha = 1.0 - smoothstep(edgeDistance - 0.5, edgeDistance + 0.5, fragDistance); // ピクセルがエッジよりも半ピクセル以上内側なら1、半ピクセル以上外側なら0、中間なら位置に応じた半透明 107 108 half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color; 109 110 color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect); 111 color.a *= maskAlpha; // イメージのアルファにマスクをかける 112 113 #ifdef UNITY_UI_ALPHACLIP 114 clip (color.a - 0.001); 115 #endif 116 117 return color; 118 } 119 ENDCG 120 } 121 } 122}

このようなマテリアルを、Maskコンポーネントではなくイメージの方のマテリアルに使用します。すると下図のように、縁の部分がなめらかに切り抜かれました。

切り抜き部分の拡大図
プレビュー

渋谷ほととぎす通信さんでも言及されているように、この方式だと個々のイメージに個別にマスキングを行わなければならないのが弱点ですが、代わりにサイトでの例のように、グラデーションのかかったような任意のアルファでも切り抜ける利点があるかと思います。

投稿2018/03/18 21:56

Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mizko

2018/03/19 02:10

ご回答ありがとうございます。 マスクに滑らかな円を出せばいいと思っていました。 言われてみると確かに、マスク自体を滑らかにしたところで意味がないですね。そもそも質問が的外れでした。 マスクコンポーネントは透明度を渡さないのならば、渋谷ほととぎす通信さんのようにシェーダー自体をマスクとして利用させる方法がスマート(他に方法がない?)のようです。 ご回答いただいたコードを勉強させて頂きます。 ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問