UnityでありもののMeshをワイヤフレームで描画する
はじめに
Unityには、glBegin(GL_LINE_LOOP)
的な、直接ラインを引く手法はありません。メッシュを食わせて、それらしいShaderでラインっぽい描画にする必要がああります。
以前の記事 UnityでAudio Responsive なVisualizerをつくる - 自習室 では、メッシュのすべての辺を細長い長方形として扱って、GLを直接叩いて描画する力業な対処を行っていました。
今回は別の手法として、MeshTopology.Lines
を使う手法についてまとめます。
ソースコードは一部 @_kzr 先生の keijiro/unity-emgen at test · GitHub 中の物を参考にさせていただいております。
メッシュをライン描画用に MeshTopology.Lines
の形式に組み替える
Meshはありものを使うことにします。デフォルトで用意されている、Cube, Capsule, Cylinder, Plane, Sphere をいつもやるように MeshFilter
コンポーネントに食わせて、いつもやるようにMeshRenderer
で描画すれば良いようにします。
MeshTopology
デフォルトのMeshは MeshTopology.Triangles
で作られています。以下のコードで、使われているメッシュのトポロジーを調べることが出来ます。
// MeshFilterには、CubeやCylinderなどのMeshを与えておく MeshFilter meshFilter = GetComponent<MeshFilter>(); MeshTopology topo = meshFilter.Mesh.GetTopology(0); Debug.Log(topo); //"triangles" と出力される
MeshTopologyについてはこちら
https://docs.unity3d.com/Documentation/ScriptReference/MeshTopology.html
これは、OpenGL で言うところの glBegin(GL_TRIANGLES)
などで指定する図形のタイプの事で、おなじみ床井先生のサイトGLUTによる「手抜き」OpenGL入門で言うと、
- GL_TRIANGLES
- GL_QUADS
- GL_LINES
- GL_LINE_STRIP
- GL_POINTS
が選べる、という事を意味しています。
Cubeの場合で検証
monodevelopでデバッグして、Mesh.vertices
の中身を見てみると、Cubeの場合、上の図の順番で頂点が定義されています。
さらに Mesh.triangles
の中身を見てみると、{0, 3, 1, 0, 2, 3 ...} となっているのですが、3つずつで三角形を作っています。この図で言うと、
Mesh.vertices[0]
Mesh.vertices[3]
Mesh.vertices[1]
の3点で一個目のポリゴンを形成している、と言うことです。この3頂点は外から見たとき時計回りの順で指定されているので、カリングしても描画されます。
Mesh.SetIndices()
頂点 mesh.vertices
はそのまま利用します。
ポリゴン mesh.triangles
は、三角形を作るべく3つずつ頂点のインデックスを格納している配列です。トポロジーをMeshTopology.Lines
にしたメッシュでは、このインデックス配列に2つの頂点で1つの組(線分)となるようにセットしていきます。
mesh.triangles
から順番にインデックスを取り出して、ライン用のインデックス配列を作り直しているコードがこちらです
public int[] MakeIndices() { int[] indices = new int[2 * triangles.Length]; int i = 0; for( int t = 0; t < triangles.Length; t+=3 ) { indices[i++] = triangles[t]; //start indices[i++] = triangles[t + 1]; //end indices[i++] = triangles[t + 1]; //start indices[i++] = triangles[t + 2]; //end indices[i++] = triangles[t + 2]; //start indices[i++] = triangles[t]; //end } return indices; }
こうして作成したインデックス配列を、もう一度meshにセットしてあげれば、トポロジーがLinesになったメッシュの完成です。
triangles = meshFilter.mesh.triangles;
meshFilter.mesh.SetIndices(MakeIndices(), MeshTopology.Lines, 0);
シェーダ
MeshTopology.Lines
で正しく組まれたMeshは、シェーダで2頂点間に一直線に引かれるフラグメントを作ります。従って、シェーダは非常にシンプルな物になります。
Shader "Custom/MakeLine" { Properties { _Color ("Color", Color) = (1, 1, 1, 1) } SubShader { Tags { "RenderType"="Transparent" } Blend SrcAlpha One Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct v2f { float4 pos : SV_POSITION; }; float4 _Color; v2f vert(appdata_base v) { v2f o; o.pos = mul (UNITY_MATRIX_MVP, v.vertex); return o; } half4 frag (v2f i) : COLOR { return _Color; } ENDCG } } FallBack Off }
Tags{}
や Blend
を正しく指定すると、ラインですが透過も可能です。ここでは重ねていくとサチって発光しているような表現になるBlendを施してみました。
また今回の手法でも、ちゃんと ProjectSetting > Quality > Anti Aliasing することで、アンチエイリアスがかかります。
最後に
サンプルを動かすプロジェクトをGitHubに上げました。
izmhr/DrawLineFromMesh · GitHub
今回の手法だと、カメラからの距離にかかわらずすべての辺が1pxで描かれることになり、遠くなると線が密集してつぶれてしまいます。またもちろん、ライティングの効果は反映されません。これへの対処としては、一番はじめにも挙げた UnityでAudio Responsive なVisualizerをつくる - 自習室 も良いと思います。