以下の内容はhttps://redhologerbera.hatenablog.com/entry/2024/11/10/225424より取得しました。


Unityでメッシュの頂点座標に別のオブジェクトを生成する

本日はUnity枠です。

 今回はメッシュの表現としてメッシュを構成する頂点を可視化していきます。

 筆者は現在大学で3DCGを教えていますが、3Dのことを2Dの画面越しに教えることに対して思うことがあり、3DのことはARなどのXR技術を駆使して3Dで教えたいと感じており、その一環で頂点を可視化していきます。

〇環境

・Windows11PC

・Unity6000.0.2f1

〇メッシュの頂点座標を取得して任意のオブジェクトを配置する

今回は以下のようなQuadを用意しました。

最終的にはビルボードを適応して頂点をイメージする球体を表現します。

Unity プリミティブキューブを作成し、PlaceObjectAtVertexと呼ばれるC#スクリプトを作成、アタッチしました。

スクリプトの中身

スクリプトは以下です。

using UnityEngine;

public class PlaceObjectAtVertex : MonoBehaviour
{
    // 配置したいオブジェクトをインスペクターで設定
    [SerializeField] private GameObject targetObject;

    private void Start()
    {
        // メッシュを取得
        MeshFilter meshFilter = GetComponent<MeshFilter>();
        if (meshFilter == null || targetObject == null)
        {
            Debug.LogWarning("メッシュまたはターゲットオブジェクトが設定されていません。");
            return;
        }

        // メッシュの頂点情報を取得
        Mesh mesh = meshFilter.mesh;
        Vector3[] vertices = mesh.vertices;

        // 頂点[0]のワールド座標に変換
        Vector3 worldPosition = transform.TransformPoint(vertices[0]);

        // オブジェクトを頂点[0]の位置に配置
        targetObject.transform.position = worldPosition;
    }
}

内容はメッシュを取得して最初の頂点(vertices[0])の座標を求め、TargetOjcectの座標を移動させます。

これによってメッシュの頂点リストの最初の頂点座標にオブジェクトが配置されるようになります。

 頂点数だけ配置する場合はfor文でInstantiateでオブジェクトを配置することで可能です。

        // メッシュの頂点情報を取得
        Mesh mesh = meshFilter.mesh;
        Vector3[] vertices = mesh.vertices;

        for (int i = 0; i < vertices.Length; i++)
        {
            Vector3 worldPosition = transform.TransformPoint(vertices[i]);
            Instantiate(targetObject, worldPosition, Quaternion.identity);   
        }

〇重複頂点の処理

 メッシュはUVやサブメッシュなどの関係で重複頂点が作られることがあります。

Unityのプリミティブキューブの場合は24つの頂点が存在します。

この問題を解消するためにはListやDictionary形に全頂点座標を格納し、同じ座標の場合を除外するといったアプローチができます。

using UnityEngine;
using System.Collections.Generic;

public class PlaceObjectAtUniqueVertexWithTolerance : MonoBehaviour
{
    [SerializeField] private GameObject targetObject;
    [SerializeField] private float tolerance = 0.01f;  // 判定する距離のしきい値

    private void Start()
    {
        MeshFilter meshFilter = GetComponent<MeshFilter>();
        if (meshFilter == null || targetObject == null)
        {
            Debug.LogWarning("メッシュまたはターゲットオブジェクトが設定されていません。");
            return;
        }

        Mesh mesh = meshFilter.mesh;
        Vector3[] vertices = mesh.vertices;

        List<Vector3> placedPositions = new List<Vector3>();

        foreach (var vertex in vertices)
        {
            Vector3 worldPosition = transform.TransformPoint(vertex);

            // 判定距離内に既存の配置がないかチェック
            bool isDuplicate = false;
            foreach (var pos in placedPositions)
            {
                if (Vector3.Distance(worldPosition, pos) < tolerance)
                {
                    isDuplicate = true;
                    break;
                }
            }

            // 近い座標がない場合のみ配置
            if (!isDuplicate)
            {
                Instantiate(targetObject, worldPosition, Quaternion.identity);
                placedPositions.Add(worldPosition);
            }
        }
    }
}

このように実装することで重複なしにメッシュの頂点を可視化することができます。

これを使用することで様々な表現ができそうです。

本日は以上です。




以上の内容はhttps://redhologerbera.hatenablog.com/entry/2024/11/10/225424より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14