コードの各部分を円A用と円B用に二重化してやるという発想は妥当かと思うのですが、frag
末尾の...
ShaderLab
1 return col1;
2 return col2;
というのはまずいでしょうね。このように記述してもfrag
から出力される色はcol1
だけで、col2
は無視されてしまうかと思います。
結局、円Bについては上の方の...
ShaderLab
1 if(dist2 < _HoleSize2)
2 {
3 clip(-1.0);
4 }
という部分だけが結果に影響することになるでしょう。円B中心の半径_HoleSize2
未満の小さい部分だけがclip
によってくり抜かれ、周りのグラデーションがない小さな円になってしまったものと思われます。
円Bにも周りにグラデーション領域ができるようにするとなると、一案としては「中心点が一番近い円の色を返す」というのはどうでしょう。つまりdist1
とdist2
を比べ、dist1
の方が近かったらcol1
を、dist2
の方が近かったらcol2
を返すというわけです。
ShaderLab
1 return lerp(col1, col2, step(dist2, dist1));
穴をマウスポインタに向かって伸ばす件について
下記のような穴のスクリプトと...
C#
1 using System ;
2 using UnityEngine ;
3 using UnityEngine . Rendering ;
4
5 [ RequireComponent ( typeof ( Renderer ) ) ]
6 public class Hole : MonoBehaviour
7 {
8 private const int SpokeCount = 32 ;
9 private const float SmoothingThreshold = 0.001f ;
10 private static readonly int CenterProperty = Shader . PropertyToID ( "_Center" ) ;
11 private static readonly int InverseMaxSpokeLengthProperty = Shader . PropertyToID ( "_InverseMaxSpokeLength" ) ;
12 private static readonly int SpokeLengthsProperty = Shader . PropertyToID ( "_SpokeLengths" ) ;
13 private static readonly int SmoothingFactorProperty = Shader . PropertyToID ( "_SmoothingFactor" ) ;
14
15 [ SerializeField ] [ Min ( 0.0f ) ] private float maxSpokeLength = 0.4f ; // ローカル空間におけるスポークの最大長さ
16 [ SerializeField ] [ Min ( 0.0f ) ] private float mouseAngleHalfWidth = 15.0f ; // スポークの長さが半減するマウスポインタとスポークの角度差
17
18 public Vector2 center ; // 穴の中心のローカルXY座標
19 [ Min ( 0.0f ) ] public float smoothTime = 1.0f ; // スポークが目標長さに伸縮するおよその時間
20 [ Range ( 0.0f , 0.05f ) ] public float outlineSmoothness = 0.025f ; // 穴の輪郭のなめらかさ
21 public bool showDebugGizmos ; // スポークとマウスポインタ位置をシーンビュー上に表示するか
22
23 private readonly Vector2 [ ] spokeDirections = new Vector2 [ SpokeCount ] ;
24 private readonly float [ ] spokeLengths = new float [ SpokeCount ] ;
25 private readonly float [ ] spokeTargetLengths = new float [ SpokeCount ] ;
26 private readonly float [ ] spokeVelocities = new float [ SpokeCount ] ;
27 private LocalKeyword enableSmoothing ;
28 private Camera mainCamera ;
29 private Material material ;
30 private float spokeLengthFactor ;
31
32 private void Start ( )
33 {
34 this . material = this . GetComponent < Renderer > ( ) . material ;
35 this . enableSmoothing = new LocalKeyword ( this . material . shader , "ENABLE_SMOOTHING" ) ;
36 this . mainCamera = Camera . main ;
37 for ( var i = 0 ; i < SpokeCount ; i ++ )
38 {
39 var spokeAngle = ( 2.0f * Mathf . PI * i ) / SpokeCount ;
40 this . spokeDirections [ i ] = new Vector2 ( Mathf . Cos ( spokeAngle ) , Mathf . Sin ( spokeAngle ) ) ;
41 }
42 this . spokeLengthFactor = ( Mathf . Log ( 0.5f ) * Mathf . Rad2Deg * Mathf . Rad2Deg ) / ( this . mouseAngleHalfWidth * this . mouseAngleHalfWidth ) ;
43 }
44
45 private void Update ( )
46 {
47 // まず、マウスポインタの位置をもとに各スポークの目標長さを決める
48 // スポークの向きがマウスポインタの方角からずれるほど、スポークの
49 // 長さを釣り鐘形に割り引くことでなめらかな外形を作る
50 var mouseRay = this . mainCamera . ScreenPointToRay ( Input . mousePosition ) ;
51 if ( ! new Plane ( this . transform . forward , this . transform . position ) . Raycast ( mouseRay , out var enter ) )
52 {
53 return ;
54 }
55 var mouseWorldPosition = mouseRay . GetPoint ( enter ) ;
56 var mousePosition = ( Vector2 ) this . transform . InverseTransformPoint ( mouseWorldPosition ) ;
57 var relativeMousePosition = mousePosition - this . center ;
58 var mouseLength = Mathf . Min ( relativeMousePosition . magnitude , this . maxSpokeLength ) ;
59 if ( mouseLength > 0.0f )
60 {
61 var mouseDirection = relativeMousePosition . normalized ;
62 for ( var i = 0 ; i < SpokeCount ; i ++ )
63 {
64 var angle = Mathf . Acos ( Mathf . Clamp ( Vector2 . Dot ( mouseDirection , this . spokeDirections [ i ] ) , - 1.0f , 1.0f ) ) ;
65 this . spokeTargetLengths [ i ] = Mathf . Exp ( this . spokeLengthFactor * angle * angle ) * mouseLength ;
66 }
67 }
68 else
69 {
70 Array . Fill ( this . spokeTargetLengths , 0.0f ) ;
71 }
72
73 // 各スポークの長さを目標値に向かってなめらかに伸縮させる
74 for ( var i = 0 ; i < SpokeCount ; i ++ )
75 {
76 this . spokeLengths [ i ] = Mathf . SmoothDamp (
77 this . spokeLengths [ i ] ,
78 this . spokeTargetLengths [ i ] ,
79 ref this . spokeVelocities [ i ] ,
80 this . smoothTime ) ;
81 if ( this . showDebugGizmos )
82 {
83 Debug . DrawLine (
84 this . transform . TransformPoint ( this . center ) ,
85 this . transform . TransformPoint ( ( this . spokeDirections [ i ] * this . spokeLengths [ i ] ) + this . center ) ,
86 Color . green ) ;
87 }
88 }
89
90 // マウスポインタの位置に十字を表示する
91 if ( this . showDebugGizmos )
92 {
93 const float crossRadius = 8.0f ;
94 var mouseScreenPosition = this . mainCamera . WorldToScreenPoint ( mouseWorldPosition ) ;
95 Debug . DrawLine (
96 this . mainCamera . ScreenToWorldPoint ( mouseScreenPosition + ( Vector3 . left * crossRadius ) ) ,
97 this . mainCamera . ScreenToWorldPoint ( mouseScreenPosition + ( Vector3 . right * crossRadius ) ) ,
98 Color . yellow ) ;
99 Debug . DrawLine (
100 this . mainCamera . ScreenToWorldPoint ( mouseScreenPosition + ( Vector3 . down * crossRadius ) ) ,
101 this . mainCamera . ScreenToWorldPoint ( mouseScreenPosition + ( Vector3 . up * crossRadius ) ) ,
102 Color . yellow ) ;
103 }
104
105 // 穴の中心、スポークの長さ、その他マテリアルに必要なデータをセットする
106 this . material . SetVector ( CenterProperty , this . center ) ;
107 this . material . SetFloat ( InverseMaxSpokeLengthProperty , 1.0f / this . maxSpokeLength ) ;
108 this . material . SetFloatArray ( SpokeLengthsProperty , this . spokeLengths ) ;
109 this . material . SetKeyword ( this . enableSmoothing , this . outlineSmoothness > SmoothingThreshold ) ;
110 this . material . SetFloat ( SmoothingFactorProperty , 1.0f / this . outlineSmoothness ) ;
111 }
112
113 private void OnDestroy ( )
114 {
115 Destroy ( this . material ) ;
116 }
117 }
板のマテリアル用に下記のようなシェーダーを用意しました。
ShaderLab
1 Shader "Unlit/Hole"
2 {
3 Properties
4 {
5 _Color ("Base Color", Color) = (0.0, 0.0, 0.0, 0.5) // 背景の色
6 _InnerRadius ("Inner Radius", Range(0.0, 1.0)) = 0.1 // 縁のグラデーションが始まる半径
7 _OuterRadius ("Outer Radius", Range(0.0, 1.0)) = 0.2 // 縁のグラデーションが終わる半径
8 }
9 SubShader
10 {
11 Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
12
13 ZWrite Off
14 Blend SrcAlpha OneMinusSrcAlpha
15
16 Pass
17 {
18 CGPROGRAM
19 #pragma multi_compile _ ENABLE_SMOOTHING
20 #pragma vertex vert
21 #pragma fragment frag
22 #include "UnityCG.cginc"
23 #define SPOKE_COUNT 32
24
25 struct v2f
26 {
27 float2 position : TEXCOORD0;
28 float4 vertex : SV_POSITION;
29 };
30
31 v2f vert(float4 vertex : POSITION)
32 {
33 v2f o;
34 o.vertex = UnityObjectToClipPos(vertex);
35 o.position = vertex.xy;
36 return o;
37 }
38
39 float2 _Center;
40 float _InverseMaxSpokeLength;
41 float _SpokeLengths[SPOKE_COUNT];
42 float _SmoothingFactor;
43 fixed4 _Color;
44 float _InnerRadius;
45 float _OuterRadius;
46
47 // 線分abと点pの距離を得る
48 float distance(float2 a, float2 b, float2 p)
49 {
50 float2 s = b - a;
51 float l2 = dot(s, s);
52 if (l2 > 0.0)
53 {
54 float2 v = p - a;
55 float f = dot(v, s) / l2;
56 return f <= 0.0 ? distance(a, p) : f >= 1.0 ? distance(b, p) : abs((v.x * s.y) - (v.y * s.x)) / sqrt(l2);
57 }
58 return distance(a, p);
59 }
60
61 fixed4 frag(v2f i) : SV_Target
62 {
63 float minimumDistance = 1.#INF;
64
65 #ifdef ENABLE_SMOOTHING
66 float nearness = exp2(-distance(_Center, i.position) * _SmoothingFactor);
67 float weightSum = 1.0;
68 #endif
69
70 for (int j = 0; j < SPOKE_COUNT; j++)
71 {
72 // ピクセルと各スポークの距離を調べて...
73 float2 direction;
74 sincos(2.0 * UNITY_PI * j / SPOKE_COUNT, direction.y, direction.x);
75 float d = distance(_Center, _Center + direction * _SpokeLengths[j], i.position);
76
77 #ifdef ENABLE_SMOOTHING
78 // 外形をスムージングする場合、まずピクセルと各スポークの近さを積算する
79 // このとき、スポークの長さに応じて積算量に重みをつける
80 float weight = _SpokeLengths[j] * _InverseMaxSpokeLength;
81 nearness += exp2(-d * _SmoothingFactor) * weight;
82 weightSum += weight;
83 #else
84 // 外形をスムージングしない場合、単純にピクセルと各スポークの距離を調べて最短のものを選ぶ
85 minimumDistance = min(minimumDistance, d);
86 #endif
87 }
88
89 // 外形をスムージングする場合、minimumDistanceを積算した近さから逆算する
90 #ifdef ENABLE_SMOOTHING
91 minimumDistance = -log2(nearness / weightSum) / _SmoothingFactor;
92 #endif
93
94 // 完全透明部分はクリッピングする
95 float2 radii = float2(min(_InnerRadius, _OuterRadius), max(_InnerRadius, _OuterRadius));
96 clip(minimumDistance - radii.x);
97
98 // 縁の部分のグラデーションを決める
99 fixed4 color = _Color;
100 color.a *= smoothstep(radii.x, radii.y, minimumDistance);
101 return color;
102 }
103 ENDCG
104 }
105 }
106 }
穴を中心に仮想的なスポークを放射状に配置し、それらがマウスポインタの位置に応じて伸縮するようにしてみました。シェーダー上ではこれらスポークとの距離をもとに穴を開けるという作戦です。
下記のような回答は推奨されていません。
このような回答には修正を依頼しましょう。
2022/04/08 04:05
2022/04/08 23:36
2022/04/11 02:43
2022/04/13 06:04