Информационный портал по безопасности » Программирование » Веб-разработка » Optimization Unity3d UI by GPU (for example minimap) или создаем миникарту без дополнительных камер и спрайтов

 

Optimization Unity3d UI by GPU (for example minimap) или создаем миникарту без дополнительных камер и спрайтов

Автор: admin от 14-08-2017, 01:00, посмотрело: 1 033

Всем привет!



«Если можешь что-то посчитать на GPU, делай это»

// Конечно в рамках разумного



Optimization Unity3d UI by GPU (for example minimap) или создаем миникарту без дополнительных камер и спрайтов



VS



Optimization Unity3d UI by GPU (for example minimap) или создаем миникарту без дополнительных камер и спрайтов

Обращаем внимание на разницу в фпс



Начну, пожалуй, с предыстории. Один из наших программистов, решил проверить UI на предмет падения фпс. И мы нашли интересную зависимость, при отключении миникарты фпс поднимался в процентном соотношении. Интересно. Нужно решать проблему. Сразу напишу что про атласы и различные пулы, мы пробовали. И тогда я решил заняться этим вопросом более детально. И тут первая мысль, которая меня посетила, UI использует материал, значит можно все перенести на ГПУ, начнем.



Начал гуглить, находил много скажем так, нехороших решений, например таких как

Не делайте так, даже если хотите сделать быстро.



Почему выше пример плохо, можно найти в комментариях. Но для ленивых я перечислю.

streeter12 4 июля 2016 в 14:51

Данный метод несмотря на свою простоту имеет явные недостатки.



1. Низкая производительность.

2. Для добавления новых меток надо создавать новые сферы (лишний хлам в префабах).

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

4. Для смены внешнего вида метки необходимо создавать меш!

5. Лишние объекты на каждой сцене => лишняя сложность => сложнее разработка и поддержка.

6. Сложно тестировать => больше возможных багов (с учетом 3).

7. Реалтаймовая замена типа.

8. Нужно захломлять сцену фейковой подложкой.

9. Как быть с теми кто не должен учитывать вращение? обнулять в Update или таскать через tranform.position.

и т.д.



Нечего не найдя, в итоге начал писать шейдер.



С чего я начал.



1. Нужно как то хранить сами иконки. Атлас подойдет. Теперь нужно найти подходящий для тестов. Взял из игры WOW. там он уже был правильного содержания.



2. Нужно как то пробросить эту информацию в шейдер. Плюс по возможности максимально отказаться от различных UI преобразований, например координаты относительно чего то и тд.



В общем, начнем реализацию.



	
static const int MaxCount = 256;
float4 _Targets[MaxCount];
float _WorldSizeX;
float _WorldSizeY;


Такие поля я объявил в шейдере. Поставил максимально 256 на мапе точек. Остальные будут отбрасываться. float _WorldSizeX поле решило проблему с преобразованиями, я как есть решил кормить Vector3 в шейдер. Не хитрыми махинациями, я получил индексы и нормализованные координаты, и тем самым получил практически то что хотел.



Нюанс: GC считает y иначе снизу в врех. Я конечно сделал обратные значения, чтобы левая верхняя была индексом 0. Но потом решил отказаться от этой затеи. Лучше решать это путем верхнего слоя. Например кодом.



И того у меня были заняты 3 поля из Vector4 (X Z W) чем я занял Y будет позже.



Тесты



Сделав шейдер, я решил проверить разницу. Теперь фпс был на уровне добавление удаления всего одного ui элемента. Скорость возросла существенно, на первых двух скриншотах можно увидеть разницу.



Для примера как примерно выглядит скрипт для шейдера.



protected virtual void Update ()
{
        for (int i = 0; i < list.Length; i++)
        {
            list[i] = new Vector4(0, 0, 0, -1);
        }

        for (int i = 0; i < list.Length &&  i < targets.Count ; i++)
       {
            list[i] = new Vector4(targets[i].x *k.x, targets[i].y, targets[i].z*k.y, targets[i].w);
        }
	    image.GetModifiedMaterial(image.material).SetVectorArray("_Targets", list);
}


Как это использовать, мы опустим. Скажем у нас есть менеджер со списком точек для миникарты. А вы его используете как вам удобно. Кто подписками, кто сингелтоном, кто статикой. Главное не забывать обнулить список, чтобы не было неприятных моментов, и загнать все в шейдер.



Optimization Unity3d UI by GPU (for example minimap) или создаем миникарту без дополнительных камер и спрайтов



Результат работы. Как было видно, я добавил еще и вращение. Но для того что-бы его использовать следует сделать иконки на достаточном расстоянии друг от друга в атласе. Я не стал выдумывать каких то сложных алгоритмов отсечения в шейдере, гораздо проще все это сделать текстурой.



Тот самый свободный Y, я решил использовать в качестве угла. С ним пришлось отдельно повозится, причина была выше, а именно инверсия координаты Y. Именно из-за вращения я решил использовать все как есть.



fixed a = _Targets[i].y;
					fixed resultX = tX;
					fixed resultY = tY;
					if (a != 0)
					{
						fixed x0 = minX + (sizeX * 0.5);
						fixed y0 = minY + (sizeY * 0.5);
						resultX = x0 + (tX - x0) * cos(a) - (tY - y0) * sin(a);
						resultY = y0 + (tY - y0) * cos(a) + (tX - x0) * sin(a);
					}


Вот теперь шейдер готов. Последний штрих, это смягчение альфа канала на границах. Я использовал интерполяцию, от афьфа до барьера. Так картинка получилась качественее.



Выводы



Тест для статьи. Я не стал делать полное соответствие. Это бессмысленно, и поэтому контент отличается. Но чтобы понять разницу, этого более чем предостаточно. Следующим этапом, когда дойдут руки, на аналогичную систему хочу перевести тайловый инвентарь.



Выложу тестовые коды и результаты профайлера. Заполнение спрайтами.



 	protected virtual void Start ()
    {
        tests = new List<RectTransform>();
        float x = 10;
        float y = 10;

	    for (int i = 0; i < 256; i++)
	    {
	        int idx = Random.Range(0, prefabs.Count);

	        GameObject obj = Instantiate(prefabs[idx].gameObject);
	        RectTransform t = obj.GetComponent<RectTransform>();
            t.SetParent(parent);
            t.localPosition = new Vector3(startPosition.x  + x, startPosition.y + y, 0);
	        if (useRotation)
	        {
	            t.rotation = Quaternion.Euler(0, 0, Random.Range(0, 360));
	        }
	        x += step;
	        if (i % 20 == 0)
	        {
	            y += step;
	            x = 0;
	        }
	        tests.Add(t);
        }
	}


Заполнения через прокси класс для шейдера

item.type = Random.Range(0, 64); означает тип иконки



	void Start () {
        tests = new List<Item2>();
        float x = 10;
        float z = 10;

	    for (int i = 0; i < 256; i++)
	    {
            Item2 item = new Item2();
	        item.position = new Vector3(x, 0, z);
	        if (useRotation)
	        {
	            item.rotation = Random.Range(0, 360);
	        }
	        item.type = Random.Range(0, 64);
	        x += step;
	        if (i % 20 == 0)
	        {
	            z += step;
	            x = 0;
	        }
	        tests.Add(item);

        }
	}


Профайлер для шейдера (на сцене еще 3 куба)



Optimization Unity3d UI by GPU (for example minimap) или создаем миникарту без дополнительных камер и спрайтов



Профайлер для спрайтов (на сцене еще 3 image в качестве префабов)



Optimization Unity3d UI by GPU (for example minimap) или создаем миникарту без дополнительных камер и спрайтов



и напоследок пример с маской:



Optimization Unity3d UI by GPU (for example minimap) или создаем миникарту без дополнительных камер и спрайтов



P.S. «Если можешь что-то посчитать на GPU, делай это»

Источник: Хабрахабр

Категория: Программирование / Веб-разработка

Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

Добавление комментария

Имя:*
E-Mail:
Комментарий:
  • bowtiesmilelaughingblushsmileyrelaxedsmirk
    heart_eyeskissing_heartkissing_closed_eyesflushedrelievedsatisfiedgrin
    winkstuck_out_tongue_winking_eyestuck_out_tongue_closed_eyesgrinningkissingstuck_out_tonguesleeping
    worriedfrowninganguishedopen_mouthgrimacingconfusedhushed
    expressionlessunamusedsweat_smilesweatdisappointed_relievedwearypensive
    disappointedconfoundedfearfulcold_sweatperseverecrysob
    joyastonishedscreamtired_faceangryragetriumph
    sleepyyummasksunglassesdizzy_faceimpsmiling_imp
    neutral_faceno_mouthinnocent