Optymalizacja – Granice Obiektów

Czy zdarzyło Ci się kiedyś, że obiekty znajdowały się po za obszarem kamery (ang. camera frustum), a mimo to zostały wyrenderowane? Najprawdopodobniej powodem były granice obiektów, które mogą zajmować znacznie więcej miejsca niż sam obiekt 🙂 .

Wyobraźmy sobie, że mamy prostą scenę z kilkoma obiektami 3d i kamerą:

Rys. 1. Prosta scena z kamerą i czterema obiektami 3d. Tylko jeden z obiektów jest widoczny w kamerze.

Jeśli użyjemy frame debuggera, żeby sprawdzić ile obiektów zostało przetworzonych, to zobaczymy, że wszystkie zostały wyrenderowane.

Rys. 2. Scena sprawdzona frame debuggerem. Wszystkie obiekty zostały wyrenderowane mimo, że tylko jeden jest widoczny.

Jest to spowodowane przez granice obiektów, które nachodzą na obszar kamery. Jak możemy je sprawdzić? Możemy użyć prostego skryptu, który wyświetli nam gizmo reprezentujące granice renderera.

using UnityEngine;

[ExecuteInEditMode]
public sealed class BoundingBoxView : MonoBehaviour
{
    [SerializeField]
    private Renderer rendererComponent;
    [SerializeField]
    private Color boxColor = Color.red;
    [SerializeField]
    private Color selectedBoxColor = Color.green;

    private void Awake()
    {
        if (rendererComponent == null)
        {
            rendererComponent = GetComponent<Renderer>();
        }
    }

    private void OnDrawGizmos()
    {
        if (rendererComponent != null)
        {
            var bounds = rendererComponent.bounds;

            var gizmosColor = Gizmos.color;
            Gizmos.color = boxColor;
            Gizmos.DrawWireCube(bounds.center, bounds.size);
            Gizmos.color = gizmosColor;
        }
    }

    private void OnDrawGizmosSelected()
    {
        if (rendererComponent != null)
        {
            var bounds = rendererComponent.bounds;

            var gizmosColor = Gizmos.color;
            Gizmos.color = selectedBoxColor;
            Gizmos.DrawWireCube(bounds.center, bounds.size);
            Gizmos.color = gizmosColor;
        }
    }
}

Kiedy dodamy skrypt BoundingBoxView do wszystkich obiektów 3d na scenie i ustawimy im różne kolory gizmo to powinniśmy zobaczyć coś podobnego jak na rys. 3.

Rys. 3. Gizmo reprezentujące granice naszych obiektów 3d.

Jak możemy zobaczyć, wszystkie gizmo nachodzą na obszar kamery. Z tego powodu wszystkie obiekty na scenie zostały wyrenderowane. Czy da się to jakoś zoptymalizować? Obiekty można podzielić na mniejsze kawałki co spowoduje, że każda składowa będzie miała własne, mniejsze granice.

Rys. 4. Ten sam widok ale wyświetlony za pomocą mniejszej liczby odwołań do karty graficznej. Obiekty Arch i PolyShape zostały podzielone na mniejsze kawałki co zmniejszyło ich granice. Obiekty te przestały nachodzić na obszar kamery i nie są już niepotrzebnie renderowane.

Granice są wyznaczane jako prostopadłościan okalający wszystkie wierzchołki obiektu. Prostopadłościan jest liczony względem globalnych współrzędnych. To znaczy, że ściany prostopadłościanu są równoległe do globalnych osi X, Y, Z. Z tego powodu obiekt może mieć różne granice w zależności od aktualnego obrotu. Jest to istotne jeśli obiekty na scenie nie są wyrównane do globalnych osi współrzędnych tj. są względem nich obrócone. Powinieneś zwrócić na to uwagę podczas dzielenia obiektów 3d na mniejsze kawałki w czasie optymalizacji sceny.

Rys. 5. Porównanie granic tego samego obiektu w przypadku innego obrotu. Granice się różnią, ponieważ muszą być wyrównane do globalnych osi współrzędnych.

Na koniec wyobraźmy sobie, że nasza scena zbudowana jest ze statycznych obiektów i mamy wypalone mapy światła. Wszystkie obiekty zostały złączone w jeden combined mesh przez statyczny batching i są wyświetlane w jednym odwołaniu do karty graficznej. Czy nadal warto dzielić obiekty na mniejsze kawałki? Nie oszczędzimy przecież odwołań do karty graficznej, bo wszystko jest renderowane za jednym razem.

Dobre wieści 🙂 ! Nadal warto podzielić obiekty na mniejsze. Ograniczymy liczbę wierzchołków przetwarzanych przez kartę graficzną, ponieważ połączony mesh jest najprawdopodobniej zbudowany z części, które mogą być włączane/wyłączane niezależnie. Unity pominie części, które nie muszą zostać wyrenderowane, co uprości geometrię.

Rys. 6. Porównanie wydajności batchingu. W zależności od widoku nie wszystkie części zostały przetworzone mimo, że są połączone w jeden combined mesh.

Materiały

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *