Mesh-Schneidealgorithmus/3.1 Detaillierte Erläuterung des Unity Mesh-Schneidealgorithmus

Vorwort

Wenn wir Spiele entwickeln, haben wir oft ein spezielles Gameplay usw., das Mesh-Cutting erfordert. Wenn Sie beispielsweise Obst in 3D schneiden oder ein Loch in die Stadtmauer auf der Karte graben, werde ich Ihnen heute einen Mesh-Schneidealgorithmus vorstellen, der Ihnen bei der Lösung der Mesh-Schneideprobleme hilft, die im Projekt verwendet werden müssen. In diesem Artikel wird das Mesh-Schneiden hauptsächlich unter folgenden Aspekten erläutert.

Ja, hier gibt es eine Austauschgruppe für Spieleentwicklung. Ich hoffe, dass jeder reinklicken kann, um gemeinsam Entwicklungserfahrungen auszutauschen!

(1) Wie man die Berührungsbedienung des Spielers akzeptiert und eine Schnittfläche erzeugt;

(2) Die Hauptdatenkomponenten im Mesh-Objekt des 3D-Modells;

(3) Detaillierte Erläuterung der Schritte des Modell-Mesh-Cut-Algorithmus;

animiertes Cover

So akzeptieren Sie die Touch-Bedienung des Spielers und erzeugen eine Schnittfläche

Beim Schneiden des Modells müssen wir zunächst eine Schnittebene basierend auf der Berührungsbedienung des Spielers generieren. Wie in Abbildung 1 gezeigt, müssen wir entsprechend der schwarzen Linie des Spielers eine Schnittebene basierend auf der schwarzen Linie generieren. Die Hauptschritte sind wie folgt:

(1) Ermitteln Sie die Bildschirmkoordinaten des Startpunkts und kombinieren Sie sie mit der 3D-Kamera, um die Bildschirmkoordinaten des Startpunkts in das Ansichtsfensterkoordinatensystem der Kamera zu übertragen.

if (!dragg
ing && Input.GetMouseButtonDown(0)) {
    start = cam.ScreenToViewportPoint(Input.mousePosition);
    dragging = true;

}

(2) Ermitteln Sie die Bildschirmkoordinaten des Endpunkts und kombinieren Sie sie mit der 3D-Kamera, um die Bildschirmkoordinaten des Fokus in das Ansichtsfensterkoordinatensystem der Kamera einzufügen.

if (dragging && Input.GetMouseButtonUp(0)) {
        // Finished dragging. We draw the line segment
        end = cam.ScreenToViewportPoint(Input.mousePosition);
        dragging = false;
}

(3) Erzeugen Sie basierend auf der Kamera zwei Strahlen vom Startpunkt und Endpunkt und berechnen Sie den Schnittpunkt des Strahls und der Nahebene der Kamera.

var startRay = cam.ViewportPointToRay(start);
var endRay = cam.ViewportPointToRay(end);
var startPoint = startRay.GetPoint(cam.nearClipPlane),
var endPoint = endRay.GetPoint(cam.nearClipPlane),

(4) Nachdem wir zwei Punkte haben, gibt es eine Linie, und eine Richtung wird benötigt, um eine Ebene zu bestimmen (die Linie bewegt sich entsprechend der Richtung und wird zu einer Oberfläche). In dieser Richtung nehmen wir die Richtung des endRay-Strahls als Vektor A.

(5) Erhalten Sie einen Vektor vom Endpunkt - dem Startpunkt, und kombinieren Sie ihn mit der Bildmenge in Richtung endRay, um ein Kreuzprodukt zu erstellen, sodass Sie den nach oben gedrehten Vektor der schiefen Ebene erhalten. Wie in der Abbildung gezeigt, verläuft die Fase gleichzeitig durch die Anfangs- und Endpunkte, sodass die Schnittfläche bestimmt werden kann.

var planeTangent = (end - start).normalized;
if (planeTangent == Vector3.zero)
    planeTangent = Vector3.right;
var normalVec = Vector3.Cross(depth, planeTangent);

Anhand von Start und normalVec können wir unsere Schnittebene bestimmen. Es ist die Ebene, die durch den Startpunkt verläuft, und der Aufwärtsrichtungsvektor ist normalVec.

Die wichtigsten Datenkomponenten im 3D-Modell Mesh-Objekt

Bevor ich den Mesh-Schneidalgorithmus im Detail erkläre, möchte ich Ihnen zunächst erklären, welche Teile der Mesh-Daten eines Modells hauptsächlich enthalten. Wenn wir ein neues Mesh generieren, wissen wir, was jeder Teil der Daten darstellt. Mesh-Daten in Unity werden in einem Mesh-Objekt generiert. Ein Mesh-Objekt enthält hauptsächlich die folgenden wichtigen Daten:

Scheitelpunktdaten: Alle Mesh-Scheitelpunkte im Mesh-Objekt;

Normale Daten: Normale Daten, die jedem Scheitelpunkt im Mesh-Objekt entsprechen;

Textur-UV-Daten: Textur-UV-Daten, die jedem Scheitelpunkt im Mesh-Objekt entsprechen;

Dreiecksscheitelpunktindexdaten: Die Dreiecksdaten, die jeder Fläche im Mesh-Objekt entsprechen, und das Dreieck basiert auf dem Scheitelpunktindex. Beispielsweise besteht Fläche A aus drei Scheitelpunkten A1, A2 und A3. Hier ist der Scheitelpunktindex. A1 wird in die Scheitelpunktdaten indiziert, um die Scheitelpunktdaten zu erhalten. A1 wird in die Normaldaten indiziert, um die Normale zu erhalten. In Unity können Sie die oben genannten Daten wie folgt über die Schnittstellenfunktion des Mesh-Objekts abrufen:

mesh.GetVertices(ogVertices);
mesh.GetTriangles(ogTriangles, 0);
mesh.GetNormals(ogNormals);
mesh.GetUVs(0, ogUvs);

Detaillierte Erläuterung der Schritte des Modell-Mesh-Cutting-Algorithmus

Nachdem wir die Schnittebene bestimmt haben, analysieren wir die Hauptschritte des Mesh-Schneidalgorithmus:

Schritt 1: Ermitteln Sie die Schnittebene im Modellkoordinatensystem

Übertragen Sie den Punkt „Schnittfläche“ (den Punkt, durch den die Ebene verläuft) + die Normale (den Aufwärtsvektor der Ebene) von den Ansichtsfensterkoordinaten in das zu schneidende Modellkoordinatensystem:

var localNormal = ((Vector3)(obj.transform.localToWorldMatrix.transpose * normal)).normalized;
var localPoint = obj.transform.InverseTransformPoint(point);

Dann ist gemäß localPoint der Normalenvektor localNormal, um ein Plane-Objekt zu generieren. Die ebenen Objekte sind hier relativ zum Koordinatensystem des Modells.

var slicePlane = new Plane();
slicePlane.SetNormalAndPosition(localNormal , localPoint);

Schritt2: Teilen Sie das Objekt entsprechend der Schnittebene in einen Vorwärtsteil und einen Rückwärtsteil. Nach dem Schneiden des Objekts ist der Teil über der Ebene der positive Teil (Positiv). ), und der Teil unterhalb der Ebene ist negativ

Schritt 3:  Bestimmen Sie, ob das folgende Mesh die Ebene schneidet. Wenn das Mesh und die Ebene überhaupt keinen Fokus haben, endet der Algorithmus, ohne das Mesh zu teilen;

if (!Intersections.BoundPlaneIntersect(mesh, ref slice))
    return false;

Schritt 4:  Rufen Sie die Netzdaten im ursprünglichen Netz ab und bereinigen Sie die nach dem Schneiden gespeicherten Vorwärts- und Rückwärts-Netzdatensätze.

mesh.GetVertices(ogVertices);
mesh.GetTriangles(ogTriangles, 0);
mesh.GetNormals(ogNormals);
mesh.GetUVs(0, ogUvs);

PositiveMesh.Clear();
NegativeMesh.Clear();
addedPairs.Clear();

Schritt 5: Durchqueren Sie alle Scheitelpunkte, um zu sehen, welche Scheitelpunkte sich im oberen Teil der Schnittfläche und welche Scheitelpunkte auf der unteren Stufe der Schnittfläche befinden, und platzieren Sie sie separat in PositiveMesh und NegativeMesh.

for(int i = 0; i < ogVertices.Count; ++i) {
    if (slice.GetDistanceToPoint(ogVertices[i]) >= 0)
        PositiveMesh.AddVertex(ogVertices, ogNormals, ogUvs, i); 
    else
        NegativeMesh.AddVertex(ogVertices, ogNormals, ogUvs, i);
}

Schritt6: Berechnen Sie den Schnittpunkt zwischen der Schnittfläche und dem Objekt, um die Erstellung einer neuen Oberfläche vorzubereiten;

// 3. Separate triangles and cut those that intersect the plane
for (int i = 0; i < ogTriangles.Count; i += 3)
{
    if (intersect.TrianglePlaneIntersect(ogVertices, ogUvs, ogTriangles, i, ref slice, PositiveMesh, NegativeMesh, intersectPair))
        addedPairs.AddRange(intersectPair);
}

Schritt 7: Neue Flächen entsprechend den neuen Punkten generieren

private void FillBoundaryFace(List<Vector3> added)
{
        // 1. Reorder added so in order ot their occurence along the perimeter.
        MeshUtils.ReorderList(added);

        // 2. Find actual face vertices
        var face = FindRealPolygon(added);

        // 3. Create triangle fans
        int t_fwd = 0,
            t_bwd = face.Count - 1,
            t_new = 1;
        bool incr_fwd = true;

        while (t_new != t_fwd && t_new != t_bwd)
        {
            AddTriangle(face, t_bwd, t_fwd, t_new);

            if (incr_fwd) t_fwd = t_new;
            else t_bwd = t_new;

            incr_fwd = !incr_fwd;
            t_new = incr_fwd ? t_fwd + 1 : t_bwd - 1;
        }
}

Schritt 8:  Instanziieren Sie ein neues Objekt und kopieren Sie die beiden ausgeschnittenen Netze, eines auf den ursprünglichen Knoten und das andere auf den neu erstellten Knoten.

// Create new Sliced object with the other mesh

GameObject newObject = Instantiate(obj, ObjectContainer);

newObject.transform.SetPositionAndRotation(obj.transform.position, obj.transform.rotation);

var newObjMesh = newObject.GetComponent<MeshFilter>().mesh;



// Put the bigger mesh in the original object

// TODO: Enable collider generation (either the exact mesh or compute smallest enclosing sphere)

ReplaceMesh(mesh, biggerMesh);

ReplaceMesh(newObjMesh, smallerMesh);
 

Anbei: Video-Tutorial

Unity / Empfohlene Empfehlungen Unity Architect Advanced: Detaillierte Erläuterung des Netzschneidalgorithmus www.bycwedu.com/promotion_channels/1997539005

Supongo que te gusta

Origin blog.csdn.net/Thomas_YXQ/article/details/134579036
Recomendado
Clasificación