Время прочтения: 4 мин.

Часто, ввиду большого объёма данных, графики получаются захламлёнными и трудно читаемыми. Благодаря третьему измерению, мы получаем возможность более наглядного представления. Однако в таком случае серьёзной проблемой является производительность, т.к. для отображения большого количества данных, потребляется большое количество ресурсов.

Возможная 3D визуализация зарплат по регионам

Существует ряд ПО для объёмной визуализации, большинство из которых, например, MatLab, или Matplotlib, не только не обеспечивают необходимую производительность, для комфортного анализа данных, но и не обладают достаточной интерактивностью при взаимодействии с ним.

Большим преимуществом способа, описанного в этой статье, является то, что игровой движок Unity производит расчёты не на процессоре, а на более производительной видеокарте, благодаря чему, без особых трудностей возможно работать с миллионами единиц данных. Так же не составляет особого труда просмотр визуализации со всех сторон, благодаря простоте реализации вращения, перемещения и зуммирования. Перейдём непосредственно к реализации. После создания стандартного проекта Unity, в окне «Project manager» необходимо установить пакет: «High Definition RP».

После окончания установки импортируем «Visual Effect Graph» в проект.

И в окне сцены создадим «Visual Effect».

В качестве шаблона для «Visual Effect» выбираем раннее созданный «Visual Effect Graph».

Подготовим «Visual Effect Graph» для отображения статичного облака точек. Для этого перейдём в режим редактирования «Visual Effect Graph» и воссоздадим следующую структуру (добавление новых элементов осуществляется с помощью клавиши пробел):

Далее необходимо создать скрипт, для обращения к нашему графу. Назовём скрипт: «PointRender» и перенесём туда код:

using UnityEngine;
using UnityEngine.VFX;

public class PointRender : MonoBehaviour
{
    Texture2D texColor;
    Texture2D texPosition;
    Texture2D texScale;
    VisualEffect vfx;
    const uint resolution = 2048;
    int texWidth;
    int texHeight;
    uint particleCount;

    private void Start()
    {
        vfx = gameObject.GetComponent<VisualEffect>();
    }

    private void UpdateGraph()
    {
        vfx.Reinit();
        vfx.SetUInt(Shader.PropertyToID("ParticleCount"), particleCount);
        vfx.SetTexture(Shader.PropertyToID("TexColor"), texColor);
        vfx.SetTexture(Shader.PropertyToID("TexPosition"), texPosition);
        vfx.SetTexture(Shader.PropertyToID("TexScale"), texScale);
        vfx.SetUInt(Shader.PropertyToID("Resolution"), resolution);
    }

    public void SetParticles(Vector3[] positions, Color[] colors, Vector3[] scales)
    {
        particleCount = 0;
        texWidth = positions.Length > (int)resolution ? (int)resolution : positions.Length;
        var height = Mathf.CeilToInt((float)positions.Length / resolution);
        texHeight = Mathf.Clamp(height, 1, (int)resolution);
        texColor = new Texture2D(texWidth, texHeight, TextureFormat.RGBAFloat, false);
        texPosition = new Texture2D(texWidth, texHeight, TextureFormat.RGBAFloat, false);
        texScale = new Texture2D(texWidth, texHeight, TextureFormat.RGBAFloat, false);
        for (int y = 0; y < texHeight; y++)
            for (int x = 0; x < texWidth; x++)
            {
                int index = x + y * texWidth;
                if (index > positions.Length - 1)
                    break;
                texColor.SetPixel(x, y, colors[index]);
                var sizeData = new Color(scales[index].x, scales[index].y, scales[index].z);
                texScale.SetPixel(x, y, sizeData);
                var positionData = new Color(positions[index].x, positions[index].y, positions[index].z);
                texPosition.SetPixel(x, y, positionData);
            }
        texColor.Apply();
        texScale.Apply();
        texPosition.Apply();
        particleCount = (uint)positions.Length;
        UpdateGraph();
    }
}

Всё готово! Для проверки работоспособности, добавим в метод «Start» следующий код, в котором создадим произвольный набор значений цвета, позиций и размера частиц:

    private void Start()
    {
        vfx = gameObject.GetComponent<VisualEffect>();
        int particleCount = 1000;
        Vector3[] positions = new Vector3[particleCount];
        Color[] colors = new Color[particleCount];
        Vector3[] sizes = new Vector3[particleCount];
        for (int i = 0; i < particleCount; i++)
        {
            positions[i] = new Vector3(Random.value * 100, Random.value * 100, Random.value * 100);
            colors[i] = Random.ColorHSV();
            sizes[i] = new Vector3(Random.value * 10, Random.value * 10, Random.value * 10);
        }
        SetParticles(positions, colors, sizes);
    }

После запуска сцены увидим примерно такую картину. Тысяча частиц, с произвольным размером, цветом и позицией.