[MonoGame] Octree und "Voxelisierung" eines Meshes

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 1 Antwort in diesem Thema. Der letzte Beitrag () ist von φConst.

    [MonoGame] Octree und "Voxelisierung" eines Meshes

    Hallo,
    ich habe mich mal wieder an Voxel herangetastet und wollte ein relativ simples Mesh (Teapot model) voxelisieren, also mittels Würfel approximieren.

    Ergebnis:


    Funktioniert zwar, problematisch ist jedoch die Laufzeit.

    Das Prinzip funktioniert folgendergestalt:

    Eine Klasse extrahiert die Dreiecke des Models, und fügt sie einer Liste hinzu.
    Ein Octree wird initialisiert und mittels einer relativ simplen Methode geprüft ob Dreieck und BoundingBox kollidieren.

    Dazu werden einfach die Seiten der Dreiecke als Rays, also Geraden behandelt und eben geprüft ob jeweils eines der drei Geraden im BoundingBox ist.

    Die Akkuratesse der Approximation wird bestimmt durch den Treshhold von

    a) dem Abstand der Gerade vom BoundingBox und
    b) der minimalen Größe der BoundingBox ab der dann das Octree zu Ende zerlegt wurde.

    Über eine For-Schleife prüfen ob BoundingBox des derzeitigen Octree-Childs mit irgendeinem Dreieck des Models kollidiert, falls ja
    dann: Generierung acht neuer Kinder mit der Größe / 2 und prüfen ob diese denn mit irgend einem Dreieck kollidieren, et cetera; wenn ja, füge Child hinzu zur Liste.
    Andernfalls: Schleife weiter laufen.

    Bei simplen Modellen ist die Geschwindigkeit ja noch akzeptabel, möchte man jedoch komplexere Modelle laden mit mehr als 30 000 Dreiecken kann das schon eine Weile dauern.
    Das Problem ist folgende Methode:

    C#-Quellcode

    1. public bool Run()
    2. {
    3. if (this.Size.LengthSquared() < TRESHOLD.LengthSquared())
    4. return false;
    5. for (int i = _Index; i < TriangulatedMesh.ModelTriangles.Count; i++)
    6. {
    7. var _Triangle = TriangulatedMesh.ModelTriangles[i];
    8. if (_Triangle.IntersectsBoundingBox(BoundingBox))
    9. {
    10. Vector3 _newSize = Size / 2;
    11. Octree _Child0 = new Octree(TriangulatedMesh, _newSize, Position, i);
    12. Octree _Child1 = new Octree(TriangulatedMesh, _newSize, Position + new Vector3(_newSize.X, 0, 0), i);
    13. Octree _Child2 = new Octree(TriangulatedMesh, _newSize, Position + new Vector3(0, _newSize.Y, 0), i);
    14. Octree _Child3 = new Octree(TriangulatedMesh, _newSize, Position + new Vector3(_newSize.X, _newSize.Y, 0), i);
    15. Octree _Child4 = new Octree(TriangulatedMesh, _newSize, Position + new Vector3(0, 0, _newSize.Z), i);
    16. Octree _Child5 = new Octree(TriangulatedMesh, _newSize, Position + new Vector3(_newSize.X, 0, _newSize.Z), i);
    17. Octree _Child6 = new Octree(TriangulatedMesh, _newSize, Position + new Vector3(0, _newSize.Y, _newSize.Z), i);
    18. Octree _Child7 = new Octree(TriangulatedMesh, _newSize, Position + new Vector3(_newSize.X, _newSize.Y, _newSize.Z), i);
    19. if (_Child0.Run())
    20. {
    21. Childs.Add(_Child0);
    22. FullQuadtree.Add(_Child0);
    23. }
    24. if (_Child1.Run())
    25. {
    26. Childs.Add(_Child1);
    27. FullQuadtree.Add(_Child1);
    28. }
    29. if (_Child2.Run())
    30. {
    31. Childs.Add(_Child2);
    32. FullQuadtree.Add(_Child2);
    33. }
    34. if (_Child3.Run())
    35. {
    36. Childs.Add(_Child3);
    37. FullQuadtree.Add(_Child3);
    38. }
    39. if (_Child4.Run())
    40. {
    41. Childs.Add(_Child4);
    42. FullQuadtree.Add(_Child4);
    43. }
    44. if (_Child5.Run())
    45. {
    46. Childs.Add(_Child5);
    47. FullQuadtree.Add(_Child5);
    48. }
    49. if (_Child6.Run())
    50. {
    51. Childs.Add(_Child6);
    52. FullQuadtree.Add(_Child6);
    53. }
    54. if (_Child7.Run())
    55. {
    56. Childs.Add(_Child7);
    57. FullQuadtree.Add(_Child7);
    58. }
    59. return true;
    60. }
    61. }
    62. return false;
    63. }


    Was schnell auffällt: Je weniger die Methode "Intersects" true zurückgibt, desto länger wird die Schleife fortgesetzt.
    Deswegen war meine Idee statt bei jedem Run-Aufruf von 0 anzufangen, einfach vom letzten erfolgreich Treffer-Index des Parents zu kontinuieren.

    Aber auch damit dauert es ziemlich lange (um die 30 Sekunden oder länger!).

    Deswegen war meine zweite Idee nur diejenigen Triangle zu laden, die innerhalb des Parent-BoundingBox sich befinden...
    Problem: Schon diese Operation allein würde das ganze nochmal ziemlich in die Länge ziehen.

    Ich habe Videos auf YouTube gesehen, wo das ziemlich flott vonstatten ging.
    Was tun die da, was ich nicht tu?

    Lieben Dank, und sonnige Grüße.
    _
    Und Gott alleine weiß alles am allerbesten und besser.
    Lösung Gott sei Dank gefunden:

    C#-Quellcode

    1. public bool Run()
    2. {
    3. if (this.Size.LengthSquared() < TRESHOLD.LengthSquared())
    4. return false;
    5. List<Triangle> _temporary = new List<Triangle>();
    6. if (Parent == null)
    7. _temporary = TriangulatedMesh.ModelTriangles;
    8. else _temporary = Parent.ArealTriangles;
    9. for (int i = 0; i < _temporary.Count; i++)
    10. {
    11. var _Triangle = _temporary[i];
    12. if (_Triangle.IntersectsBoundingBox(BoundingBox))
    13. {
    14. ArealTriangles = _temporary.FindAll(p => p.IntersectsBoundingBox(BoundingBox));
    15. Vector3 _newSize = Size / 2;
    16. Octree _Child0 = new Octree(TriangulatedMesh, _newSize, Position, i) { Parent = this };
    17. Octree _Child1 = new Octree(TriangulatedMesh, _newSize, Position + new Vector3(_newSize.X, 0, 0), i) { Parent = this };
    18. Octree _Child2 = new Octree(TriangulatedMesh, _newSize, Position + new Vector3(0, _newSize.Y, 0), i) { Parent = this };
    19. Octree _Child3 = new Octree(TriangulatedMesh, _newSize, Position + new Vector3(_newSize.X, _newSize.Y, 0), i) { Parent = this };
    20. Octree _Child4 = new Octree(TriangulatedMesh, _newSize, Position + new Vector3(0, 0, _newSize.Z), i) { Parent = this };
    21. Octree _Child5 = new Octree(TriangulatedMesh, _newSize, Position + new Vector3(_newSize.X, 0, _newSize.Z), i) { Parent = this };
    22. Octree _Child6 = new Octree(TriangulatedMesh, _newSize, Position + new Vector3(0, _newSize.Y, _newSize.Z), i) { Parent = this };
    23. Octree _Child7 = new Octree(TriangulatedMesh, _newSize, Position + new Vector3(_newSize.X, _newSize.Y, _newSize.Z), i) { Parent = this };
    24. if (_Child0.Run())
    25. {
    26. Childs.Add(_Child0);
    27. FullQuadtree.Add(_Child0);
    28. }
    29. if (_Child1.Run())
    30. {
    31. Childs.Add(_Child1);
    32. FullQuadtree.Add(_Child1);
    33. }
    34. if (_Child2.Run())
    35. {
    36. Childs.Add(_Child2);
    37. FullQuadtree.Add(_Child2);
    38. }
    39. if (_Child3.Run())
    40. {
    41. Childs.Add(_Child3);
    42. FullQuadtree.Add(_Child3);
    43. }
    44. if (_Child4.Run())
    45. {
    46. Childs.Add(_Child4);
    47. FullQuadtree.Add(_Child4);
    48. }
    49. if (_Child5.Run())
    50. {
    51. Childs.Add(_Child5);
    52. FullQuadtree.Add(_Child5);
    53. }
    54. if (_Child6.Run())
    55. {
    56. Childs.Add(_Child6);
    57. FullQuadtree.Add(_Child6);
    58. }
    59. if (_Child7.Run())
    60. {
    61. Childs.Add(_Child7);
    62. FullQuadtree.Add(_Child7);
    63. }
    64. return true;
    65. }
    66. }
    67. return false;
    68. }


    Erläuterung: Jedes mal wenn ein Parent mit einem Triangle kollidiert, werden diejenigen Triangles geladen, die auch innerhalb des Parent sind; das wird so fortgeführt bis jedes Child nur noch paar Durchläufe machen muss.
    Funktioniert jetzt viel besser.

    "Hochauflösende" Voxel-Repräsentation in nur 0.6 Sekunden!



    ( :
    Und Gott alleine weiß alles am allerbesten und besser.