回答編集履歴

1

ジオメトリーシェーダーを使わない案を追記

2022/07/31 21:34

投稿

Bongo
Bongo

スコア10807

test CHANGED
@@ -100,3 +100,136 @@
100
100
  ![図](https://ddjkaamml8q8x.cloudfront.net/questions/2022-07-31/e05de968-9481-4234-b54f-cabb06cea00e.gif)
101
101
 
102
102
  ただしこの方法ですと、「[ジオメトリシェーダ・コンピュートシェーダのモバイル対応状況【Unity】|アマガミナブログ](https://amagamina.jp/blog/geometry-compute-mobile/)」で挙げられているような一部環境(たとえばグラフィックスシステムにMetalが使われているような)では動かないかと思います。その場合は別の手を考えてみようと思いますが、おそらくメッシュの作り方にも手を加える必要があるように予想されますので、メッシュを作る部分のスクリプトもご提示いただけると参考になりそうです。
103
+
104
+ ## 追記
105
+
106
+ まず、メッシュ生成部分を下記のようにしてみました。
107
+
108
+ ```C#
109
+ // CreateLineMeshの引数のthicknessは廃止し、マテリアル側で設定することにする
110
+ public static Mesh CreateLineMesh(Vector3 center, float length)
111
+ {
112
+ var mesh = new Mesh();
113
+ var triangles = new int[6];
114
+ triangles[0] = 0;
115
+ triangles[1] = 1;
116
+ triangles[2] = 2;
117
+ triangles[3] = 2;
118
+ triangles[4] = 3;
119
+ triangles[5] = 0;
120
+ var vertices = new Vector3[4];
121
+
122
+ // bottomとtopはcenter.yとし、現時点では太さのない潰れた長方形の状態にしておく
123
+ var left = center.x - length / 2.0f;
124
+ var right = center.x + length / 2.0f;
125
+ var bottom = center.y;
126
+ var top = center.y;
127
+
128
+ vertices[0] = new Vector3(left, bottom, center.z);
129
+ vertices[1] = new Vector3(left, top, center.z);
130
+ vertices[2] = new Vector3(right, top, center.z);
131
+ vertices[3] = new Vector3(right, bottom, center.z);
132
+
133
+ var nornals = vertices.ToArray();
134
+
135
+ // 各頂点に追加情報を埋め込む
136
+ // まずXYZには線分の逆末端の座標をいれておく
137
+ // (left側頂点にはright側の、right側頂点にはleft側の座標)
138
+ var otherVertices = new Vector4[vertices.Length];
139
+ for (var i = 0; i < otherVertices.Length; i++)
140
+ {
141
+ otherVertices[i] = vertices[vertices.Length - i - 1];
142
+ }
143
+
144
+ // そしてWには0~3の数値を入れておく
145
+ // これは各頂点が4隅のうちどれであるかを特定する目的のもので、さしあたり
146
+ // 1ビット目がbottomかtopかを、2ビット目がleftかrightかを示すことにした
147
+ otherVertices[0].w = 0;
148
+ otherVertices[1].w = 1;
149
+ otherVertices[2].w = 3;
150
+ otherVertices[3].w = 2;
151
+
152
+ mesh.SetVertices(vertices);
153
+ mesh.SetTriangles(triangles, 0);
154
+ mesh.SetNormals(nornals);
155
+
156
+ // 追加情報の埋め込み先はUVを使わせてもらうことにした
157
+ mesh.SetUVs(0, otherVertices);
158
+
159
+ mesh.RecalculateBounds();
160
+
161
+ return mesh;
162
+ }
163
+ ```
164
+
165
+ そしてマテリアルは下記のようにしました。
166
+
167
+ ```ShaderLab
168
+ Shader "Unlit/Line"
169
+ {
170
+ Properties
171
+ {
172
+ _Color ("Color", Color) = (1.0, 1.0, 1.0, 1.0)
173
+ _Thickness ("Thickness", Range(0.0, 128.0)) = 4.0
174
+ }
175
+ SubShader
176
+ {
177
+ Tags { "RenderType" = "Opaque" }
178
+
179
+ Pass
180
+ {
181
+ CGPROGRAM
182
+ #pragma vertex vert
183
+ #pragma fragment frag
184
+ #include "UnityCG.cginc"
185
+
186
+ struct appdata
187
+ {
188
+ float4 vertex : POSITION;
189
+ float4 otherVertex : TEXCOORD0;
190
+ };
191
+
192
+ float _Thickness;
193
+
194
+ float4 vert(appdata v) : SV_POSITION
195
+ {
196
+ float4 p0 = UnityObjectToClipPos(v.vertex);
197
+ float4 p1 = UnityObjectToClipPos(float4(v.otherVertex.xyz, 1.0));
198
+
199
+ // 線分の両端の座標をスクリーン座標に変換する
200
+ float2 sp0 = (p0.xy * _ScreenParams.xy) * (0.5 / p0.w);
201
+ float2 sp1 = (p1.xy * _ScreenParams.xy) * (0.5 / p1.w);
202
+
203
+ // 線分に平行なベクトルを求めておく
204
+ // このとき、頂点がleftかrightかによって向きを調整する
205
+ float2 tangent = (sp0 - sp1) * (((int)v.otherVertex.w & 2) - 1);
206
+ float sqrLength = dot(tangent, tangent);
207
+ if (sqrLength > 0.0)
208
+ {
209
+ // tangentを90°回転してnormalとする
210
+ float2 normal = tangent.yx * float2(-1.0, 1.0);
211
+
212
+ // normalの長さを線幅の半分にして...
213
+ normal *= (_Thickness * 0.5) / sqrt(sqrLength);
214
+
215
+ // 頂点をnormalの方向にずらす
216
+ // このとき、頂点がbottomかtopかによって向きを調整する
217
+ p0.xy = (sp0 - normal * (((int)v.otherVertex.w & 1) * 2 - 1)) * p0.w * (_ScreenParams.zw - 1.0) * 2.0;
218
+ }
219
+
220
+ return p0;
221
+ }
222
+
223
+ fixed4 _Color;
224
+
225
+ fixed4 frag() : SV_Target
226
+ {
227
+ return _Color;
228
+ }
229
+ ENDCG
230
+ }
231
+ }
232
+ }
233
+ ```
234
+
235
+ ![図2](https://ddjkaamml8q8x.cloudfront.net/questions/2022-08-01/979da514-16f5-4760-8a6e-036dc2543d16.gif)