Как сделать StaticBatching для объектов созданных в рантайме

Общие вопросы о Unity3D

Как сделать StaticBatching для объектов созданных в рантайме

Сообщение s_vovik 03 май 2012, 10:50

Суть вопроса: необходимо в рантайме создавать кучу объектов со средним количеством полигонов и применять к ним технологию статик батчинга. Для упрощения восприятия предположим что у нас есть всего 1 объект (1800 вершин 1100 треугольников) и один метериал с обычным VertexLit шейдером. Делаем префаб, в нем отключаем cast & receive shadow, для пущей уверенности ставим галку Static.
Изображение

Для проверки закидываем в сцену 3 копии префаба, запускаем и проверяем что статик батчинг работает.
Изображение

Теперь нам нужно добиться такого же эффекта но только создавая объекты в рантайме. Удаляем из сцены все объекты. В справке находим статический класс StaticBatchingUtility http://unity3d.com/support/documentation/ScriptReference/StaticBatchingUtility и его единственный метод Combine. Пробуем выполнить такой код
Синтаксис:
Используется csharp
        for (int i = 0; i < 3; i++) {
            // Создаем новый объект
            GameObject go = (GameObject)Instantiate(assetBundle.mainAsset, Vector3.right * 4 + Vector3.left * 2 * i, Quaternion.LookRotation(Vector3.up, Vector3.forward));
            // Закидываем его в рут
            go.transform.parent = root.transform;
            // Добавляем в лист объектов
            gosList.Add(go);            
        }
        // Получаем одномерный массив GameObject-ов
        gos = gosList.ToArray();
        // Комбинируем в статик батчинг
        StaticBatchingUtility.Combine(gos, root);
 

Т.е. мы динамически создаем 3 объекта и комбинируем их в статический батчинг. На удивление все работает! Но нам нужно пойти дальше. Теперь мы должны добавить новый объект к уже существующим.

Добавляем в скрипт OnGUI() на нем делаем кнопку при нажатии на которую выполняется код
Синтаксис:
Используется csharp
        if (GUI.Button(new Rect(10, 10, 100, 30), "Instantiate")) {
            // Создаем новый объект
            GameObject go = (GameObject)Instantiate(assetBundle.mainAsset, Vector3.right * 4 + Vector3.left * 2 * i, Quaternion.LookRotation(Vector3.up, Vector3.forward));
            // Закидываем его в рут
            go.transform.parent = root.transform;
            // Добавляем в лист объектов
            gosList.Add(go);            
       
            // Получаем одномерный массив GameObject-ов
            gos = gosList.ToArray();
            // Комбинируем в статик батчинг
            StaticBatchingUtility.Combine(gos, root);
        }
 

А вот тут уже начинаются чудеса. Батчинг вроде работает но как то странно. Он собирает геометрию попарно (). Вот зависимость дравколов и батчинга от количества созданных динамически объектов:

Объекты 1 2 3 4 5 6 7...
Дравкол 1 1 2 2 3 3 4...
Батчинг 0 2 2 4 4 6 6...

Хотя если мы комбинируем все за один раз то получаем 1 дравкол!!!
Вопрос как это побороть и сделать так что бы геометрия добавлялась к уже имеющемуся батчингу?
Аватара пользователя
s_vovik
UNец
 
Сообщения: 12
Зарегистрирован: 22 май 2010, 09:30
Откуда: Москва
  • Сайт

Re: Как сделать StaticBatching для объектов созданных в рантайме

Сообщение Tolking 03 май 2012, 12:56

А как бачинг работает?

Может вместо массива объектов появляется один "общий", а старые удаляются? Тогда бы это все объяснило...
Ковчег построил любитель, профессионалы построили Титаник.
Аватара пользователя
Tolking
Адепт
 
Сообщения: 2716
Зарегистрирован: 08 июн 2009, 18:22
Откуда: Тула

Re: Как сделать StaticBatching для объектов созданных в рантайме

Сообщение s_vovik 03 май 2012, 13:42

СтатикБатчинг склеивает меш и делает CombinedMesh с кучей SubMeshies. Смысл в том что на логическом уровне мы оперируем отдельными объектами (т.е. GameObject-ы которые мы передали в массив для комбайна никуда не пропадают) НО при этом прорисовываются за один проход. Т.е. мы экономим DrawCalls и как следствие это положительно сказывается на FPS. Зачем это нужно? И почему не воспользоватся автоматическим StaticBatching который применяется для всех статических объектов в сцене? Потому что если нам нужно динамически изменять мир и добавлять кучу объектов (любая стратегия) авто-батчинг не работает. Если наш мир слишком большой и нам нужно динамически подгружать объекты и расставлять/убирать их из сцены (любая ММО) авто-батчинг не работает. В этих случаях мы должны сами батчить. Для этого есть утилита StaticBachingUtility, к сожалению она слишком мало документирована. При этом создать батч для новой группы объектов получается легко, а вот добавить новый объект к уже существующей сбатченой группе получается плохо.

Изображение
он батчит попарно вот таким макаром. И получается что на 6 объектов у нас 3 DrawCalls

а должен вот так
Изображение
тут на 6 объектов 1 DrawCalls
Аватара пользователя
s_vovik
UNец
 
Сообщения: 12
Зарегистрирован: 22 май 2010, 09:30
Откуда: Москва
  • Сайт

Re: Как сделать StaticBatching для объектов созданных в рантайме

Сообщение Tolking 03 май 2012, 14:27

Вот и похоже на то, что бачинг склеил все в один меш. (которого нет в списке, конечно) Как-то пометил что объекты забачены, затем добавили объект в список, пытаемся бачить, но он один не забачен - не бачится. Добавляем второй - объектов небаченных 2 они бачатся...

Нужно сначала разбачить все, а потом все забачить или бачить объект с баченым мешем...
Ковчег построил любитель, профессионалы построили Титаник.
Аватара пользователя
Tolking
Адепт
 
Сообщения: 2716
Зарегистрирован: 08 июн 2009, 18:22
Откуда: Тула

Re: Как сделать StaticBatching для объектов созданных в рантайме

Сообщение s_vovik 03 май 2012, 14:51

Tolking ты прав! компонент MeshRender содержит флаг isPartOfStaticBatch. К сожалению readonly. Поэтому мне пришлось грохать старый мешрендер для каждого уже созданного объекта и создавать на его место новый. Так же я каждому MeshFilter-y возвращал SharedMesh в default значение
Синтаксис:
Используется csharp
        int l = gosList.Count;
        GameObject go = (GameObject)Instantiate(abObj, Vector3.right * 4 + Vector3.left * 2 * l, Quaternion.LookRotation(Vector3.up, Vector3.forward));              
       
        if (l > 0) {            
            Mesh mesh = abObj.GetComponent<MeshFilter>().sharedMesh;
            foreach (GameObject gameObject in gosList) {
                MeshFilter meshFilter = gameObject.GetComponent<MeshFilter>();
                MeshRenderer meshRenderer = gameObject.GetComponent<MeshRenderer>();
                Material material = meshRenderer.sharedMaterial;
                DestroyImmediate(meshRenderer);
                               
                if (meshFilter) {                            
                    meshFilter.sharedMesh = mesh;
                    meshRenderer = gameObject.AddComponent<MeshRenderer>();
                    meshRenderer.castShadows = false;
                    meshRenderer.receiveShadows = false;
                    meshRenderer.sharedMaterial = material;
                }    
            }            
        }
               
        go.isStatic = true;
        go.transform.parent = root.transform;
        gosList.Add(go);
        gos = gosList.ToArray();            
       
        StaticBatchingUtility.Combine(gos, null);
 

и мы в шоколаде! :ymparty:

Спасибо всем кому была интересна эта тема и отдельно спасибо Tolking :)
Аватара пользователя
s_vovik
UNец
 
Сообщения: 12
Зарегистрирован: 22 май 2010, 09:30
Откуда: Москва
  • Сайт

Re: Как сделать StaticBatching для объектов созданных в рантайме

Сообщение Neodrop 03 май 2012, 15:43

Я старые меши назначал на меш-рендерер. Сохранённые перед первой поклейкой.
Побробуй так, без убивания/создания MeshRenderer - просто меши замени руками из сохранённого пула. Куда, кстати, сохраняй только SharedMesh
Упс - правка : не помогает. :-?
Добавить neodrop в Skype
Изображение
"Спасибо!" нашему порталу, вы сможете сказать ЗДЕСЬ.
Если проблема не решается честно, нужно её обмануть! || Per stupiditas at Astra!
Страх порождает слабость. Бесстрашных поражают пули.
Протратившись на блядях байтах, на битах не экономят.
Аватара пользователя
Neodrop
Админ
 
Сообщения: 8480
Зарегистрирован: 08 окт 2008, 15:42
Откуда: Питер
Skype: neodrop
  • Сайт

Re: Как сделать StaticBatching для объектов созданных в рантайме

Сообщение Neodrop 03 май 2012, 16:41

После батчинга не забудь вызвать Resources.UnloadUnusedAssets();
Иначе VBO будут копиться со страшной силой.
Добавить neodrop в Skype
Изображение
"Спасибо!" нашему порталу, вы сможете сказать ЗДЕСЬ.
Если проблема не решается честно, нужно её обмануть! || Per stupiditas at Astra!
Страх порождает слабость. Бесстрашных поражают пули.
Протратившись на блядях байтах, на битах не экономят.
Аватара пользователя
Neodrop
Админ
 
Сообщения: 8480
Зарегистрирован: 08 окт 2008, 15:42
Откуда: Питер
Skype: neodrop
  • Сайт

Re: Как сделать StaticBatching для объектов созданных в рантайме

Сообщение s_vovik 04 май 2012, 08:53

Resources.UnloadUnusedAssets(); Действительно классная штука. Спасибо Neo )))

Несколько интересных фактов о батчинге:
+ 1 DrawCall на каждые 10К треугольников в кадре хотя CombinedMesh один и тот же
+ 1 CombinedMesh на каждые 65535 вершин
Если удалить GameObject в СombinedMesh геометрия останется, хотя в сцене отрисовываться не будет

Думаю вопрос исчерпан и тему можно закрывать
Аватара пользователя
s_vovik
UNец
 
Сообщения: 12
Зарегистрирован: 22 май 2010, 09:30
Откуда: Москва
  • Сайт

Re: Как сделать StaticBatching для объектов созданных в рантайме

Сообщение Neodrop 26 май 2012, 21:52

Новый метод в 3.5.2 : Resources.UnloadAsset()
Добавить neodrop в Skype
Изображение
"Спасибо!" нашему порталу, вы сможете сказать ЗДЕСЬ.
Если проблема не решается честно, нужно её обмануть! || Per stupiditas at Astra!
Страх порождает слабость. Бесстрашных поражают пули.
Протратившись на блядях байтах, на битах не экономят.
Аватара пользователя
Neodrop
Админ
 
Сообщения: 8480
Зарегистрирован: 08 окт 2008, 15:42
Откуда: Питер
Skype: neodrop
  • Сайт

Re: Как сделать StaticBatching для объектов созданных в рантайме

Сообщение Syberex 28 май 2012, 15:12

Это и есть тот статик батчинг доступный только в Про?
Аватара пользователя
Syberex
Адепт
 
Сообщения: 2292
Зарегистрирован: 14 янв 2011, 20:35
Откуда: Кострома
  • Сайт


Вернуться в Общие вопросы

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 19