【UnityShader】モデルのポリゴンを点で出す #24
前回の成果
ステンシルバッファが理解できた。
今回やること
ポリゴンを点で表現しようと思います。
事前準備
Sceneにモデルを配置してください。
自分が使用しているモデルはおもちゃラボ様の下準備の項目にあるものです。
ソースコード
using UnityEngine; public class MeshTopologyPoints : MonoBehaviour { void Start() { MeshFilter meshFilter = GetComponent<MeshFilter>(); meshFilter.mesh.SetIndices(meshFilter.mesh.GetIndices(0), MeshTopology.Points, 0); } }
Meshのindexを色々変更しているように見えます。
Meshとは
ポリゴン(三角形)の集まりで、3Dモデルの表面の形を表しているものです。
メッシュが所有している情報は以下の通りです。
情報名 | 意味 |
---|---|
頂点 | ポリゴンを構成する三角形のVector3型のList |
三角形のリスト | 3つの頂点リストを保持、座標ではなく頂点の配列に対するインデックス |
テクスチャのUV座標 | テクスチャと頂点の位置関係を保持する配列 |
サブメッシュ | 1つのメッシュは複数のサブメッシュから構成されており、GetTrianglesで取得することができる。 マテリアルが複数の場合はサブメッシュも複数ある |
サブメッシュは、マテリアルのことです。
例えば、同じオブジェクトにマテリアルが3つアタッチされていたらサブメッシュの数は3つとなります。
今回は1つです。
オブジェクトのMeshを見てみる
UnityのSceneのタブの"Shaded"となっているところを"Wireframe"に変更してみてください。
そうするとこのようにワイヤーフレームで描画され、メッシュをみることができます。
MeshFilter
これはMeshのデータをMeshRendererに渡すクラスです。
MeshFilter.mesh.SetIndices()
Meshを再構築する関数です。
引数は、
// 第一引数 : indexの配列 // 第二引数 : Meshのトポロジー // 第三引数 : SubMeshのindex meshFilter.mesh.SetIndices(int[] indices, MeshTopology topology, int submesh)
となっています。
MeshFilter.mesh.GetIndices()
// 第一引数 : SubMeshのindex MeshFilter.mesh.GetIndices(int submesh)
これで各頂点のindexが取得できます。
その30 気になる頂点インデックスの意義:より引用
例えば上の図のような四角形があった場合、
// [0, 1, 2, 0, 2, 3]が入る int[] indices = MeshFilter.mesh.GetIndices(0);
となります。
MeshTopology
そもそもトポロジーとは、ポリゴンの分割の仕方という意味です。
今回はPointsを指定していますが、他にも何種類かあるので紹介します。
トポロジー名 | 意味 |
---|---|
Traiangles | 三角形から構成されるメッシュ |
Quads | 四角形から構成されるメッシュ |
Lines | 線から構成されるメッシュ |
LineStrip | 線分から構成されるメッシュ |
Points | 点から構成されるメッシュ |
それぞれの違いは下記の画像がわかりやすいです。
結果
ドラゴンが頂点で表示されれば成功です。
他のトポロジーを指定してみた
Traiangles
普段の描画方法と変わらないので変化なし。
Lines
線で描画されているのでスカスカ
囚われてるドラゴンみたいで少しかっこいい。
LineStrip
線同士が繋がっているのでLineより密度が高い。
Quads
Lineと同じような描画方法だけど、勝手に補完してくれてるからかLineより密度高い。
頂点の色を変える
次に頂点の色を変えるShaderを書きます。
Shader "Unlit/gradationPoints" { Properties { _StartColor ("StartColor", Color) = (1, 1, 1, 1) _EndColor ("EndColor", Color) = (1, 1, 1, 1) } SubShader { Tags { "RenderType" = "Transparent" } Cull Off Blend SrcAlpha OneMinusSrcAlpha Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct v2f { float4 pos : SV_POSITION; float3 texcoord : TEXCOORD0; }; float4 _StartColor; float4 _EndColor; v2f vert(appdata_base v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.texcoord = mul(unity_ObjectToWorld, v.vertex).xyz; return o; } half4 frag(v2f i) : COLOR { float4 startColor = float4(_StartColor.r, _StartColor.g, _StartColor.b, _StartColor.a); float4 endColor = float4(_EndColor.r, _EndColor.g, _EndColor.b, _EndColor.a); return lerp(startColor, endColor, i.texcoord.y * 0.2f); } ENDCG } } }
startColorとendColorを補完しているだけです。
propertyで値を設定するのを忘れないように気をつけてください。
復習
UnityObjectToClipPos
オブジェクト空間からカメラのクリップ座標へ頂点を変換しています。
o.texcoord = mul(unity_ObjectToWorld, v.vertex).xyz
オブジェクトの座標をワールド座標系に変換しています。
余談ですが、セマンティクスがTEXCOORD0なのはfloat3型なだけで、NORMALでも構いません。
結果
ドラゴンがカラフルになりました。
法線方向に飛ばす
これだけだと物足りないので法線方向に飛ばしてみようと思います。
Script
using UnityEngine; public class MeshTopologyPoints : MonoBehaviour { // 追加 private Material _material; private float _normalPow; void Start() { _material = GetComponent<Renderer>().material; // ここまで MeshFilter meshFilter = GetComponent<MeshFilter>(); meshFilter.mesh.SetIndices(meshFilter.mesh.GetIndices(0), MeshTopology.Points, 0); } // 追加 void Update() { _normalPow += Time.deltaTime * 3; _material.SetFloat("_NormalPow", _normalPow); } // ここまで }
こちらはShaderの_NormalPow変数を増やしているだけです。
Shader
Shader "Unlit/gradationPoints" { Properties { _StartColor ("StartColor", Color) = (1, 1, 1, 1) _EndColor ("EndColor", Color) = (1, 1, 1, 1) // 追加 _NormalPow("NormalPow", float) = 0 // ここまで } SubShader { Tags { "RenderType" = "Transparent" } Cull Off Blend SrcAlpha OneMinusSrcAlpha Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct v2f { float4 pos : SV_POSITION; float3 texcoord : TEXCOORD0; }; float4 _StartColor; float4 _EndColor; // 追加 float _NormalPow; // ここまで v2f vert(appdata_base v) { v2f o; // 追加 float3 normal = UnityObjectToWorldNormal(v.normal); o.pos = UnityObjectToClipPos(v.vertex) + float4(normal * _NormalPow, 0); // ここまで o.texcoord = mul(unity_ObjectToWorld, v.vertex).xyz; return o; } half4 frag(v2f i) : COLOR { float4 startColor = float4(_StartColor.r, _StartColor.g, _StartColor.b, _StartColor.a); float4 endColor = float4(_EndColor.r, _EndColor.g, _EndColor.b, _EndColor.a); return lerp(startColor, endColor, i.texcoord.y * 0.2f); } ENDCG } } }
法線の処理を追加しました。
UnityObjectToWorldNormal
ワールド空間の正規化された法線です。
ベクトルの正規化とは、ベクトルの方向は維持しつつ大きさを「1」にする事を指します。
float4(normal * _NormalPow, 0)
o.pos = UnityObjectToClipPos(v.vertex) + float4(normal * _NormalPow, 0);
上の式の
float4(normal * _NormalPow, 0)
この部分で少し詰まりました。
なんで0なの???
0の理由
少し考えれば当然で、
UnityObjectToClipPos(v.vertex)
上の式の部分でクリップ座標を出しているのですが、
※TはPosition
答えがこうなります。
0以外を足してしまうと1が変わってしまい、座標の計算が間違ってしまうので0を足しています。
結果
このように弾けるような演出ができれば成功です。
今回はここで終わりです。
ここまでご視聴ありがとうございました。