Unity 6.3
0 онлайн 101 гостей 3 в системе
Вход
UI системы Шаг 163 из 317

Создание и применение настраиваемых фильтров

Вы можете создать настраиваемые фильтры, чтобы применить любой эффект, который вы хотите к визуальному элементу.

Важно: Вы не можете использовать UI Shader Graph для создания собственных фильтров. Собственные фильтры являются пост-обрабатывающими шейдерами, которые применяют эффекты для отображения целей, содержащих поддеревья визуальных элементов. Они фундаментально отличаются от шейдеров, созданных с помощью UI Shader Graph, которые отображают элементы сетки UI напрямую.

Создание настраиваемого фильтра

Чтобы создать настраиваемый фильтр, выполните следующие действия.

  1. Создать ассет FilterFunctionDefinition. Этот ассет является простым ScriptableObject.

  2. Определите параметры фильтра, чтобы установить входные значения для фильтра. Поддерживаемые типы параметров фильтра: float и color. Когда это указано в USS, параметры фильтра, такие как число, пиксели, проценты, продолжительность и углы преобразуются в значения с плавающей запятой. Параметры могут быть установлены в стиле или непосредственно в UI BuilderНапример, blur(5px) фильтр имеет только один параметр: радиус размытия. Этот параметр пользовательские вихревой фильтр функция имеет два параметра, angle и radius вихря.

  3. Укажите interpolation default value (необязательно). Это значение используется для плавного перехода между состояниями фильтра.

  4. Добавить проходы эффекта. Фильтры часто требуют нескольких проходов для применения эффекта. Например, эффект размытия обычно имеет отдельные горизонтальные и вертикальные проходы. Каждый проход эффекта в фильтре включает:

    • Материал для выполнения: В Unity, шейдеры и материалы работают вместе, чтобы определить, как будут выглядеть поверхности. Шейдер — это программа, которая говорит GPU, как рендировать графику и определяет эффекты на поверхностях. Материалы хранят настройки, которые поступают в шейдер.
    • Индекс прохождения для этого материала.
  5. Используйте привязку данных, чтобы связать параметры фильтра со свойствами материала. Например, чтобы применить эффект оттенка, привязайте параметр цвета непосредственно к свойству материала _Tint. Для более сложной обработки параметров использует обратный вызов C#.

  6. Задайте поля (необязательно). Некоторым эффектам, например размытию, требуется дополнительное пространство вокруг текстуры для чтения и записи. При необходимости задайте статические поля или использовать обратный вызов C# для динамического вычисления маржи.

Для примера см. Создание настраиваемого вихревого фильтра.

Создать пользовательский фильтр в коде C#

Чтобы создать собственный фильтр на C#, объявите класс, наследующий FilterFunction и добавить параметры и эффекты фильтра. Следующее создает пользовательский фильтр в C# для пример вихревого фильтра:

var customSwirl = ScriptableObject.CreateInstance<FilterFunctionDefinition>();

customSwirl.parameters = new[] {
    new FilterParameterDeclaration() {
        interpolationDefaultValue = new FilterParameter(0.0f)
    },
    new FilterParameterDeclaration() {
        interpolationDefaultValue = new FilterParameter(0.5f)
    }
};

customSwirl.passes = new[]
{
    new PostProcessingPass()
    {
        material = SwirlMaterial,
        passIndex = 0,
        parameterBindings = new[] {
            new ParameterBinding() { index = 0, name = "_Angle" },
            new ParameterBinding() { index = 1, name = "_Radius" }
        }
    }
};

Применение настраиваемого фильтра в таблицах стилей

Чтобы применить свой пользовательский фильтр к визуальному элементу, используйте свойство filter в файле USS. Ссылайтесь на путь к ресурсу пользовательского фильтра, за которым следуют параметры, необходимые для фильтра. Например:

.custom-effect {
    filter: filter("/Assets/Filters/CustomFilter.asset" 10px);
}

Вы можете комбинировать пользовательские фильтры с встроенными фильтрами в одном и том же свойстве фильтра. Это позволяет создавать сложные визуальные эффекты путем наложения фильтров. Например:

.custom-effect-blur {
    filter: filter("/Assets/Filters/CustomFilter.asset" 10px) blur(25px);
}

Применение пользовательских фильтров в коде C#

В следующем примере показано, как применить фильтр размытия и пользовательский фильтр к визуальному элементу в C#:

// Create a blur filter using the built-in blur filter function
var blur = new FilterFunction(FilterFunctionType.BuiltinBlur);
blur.AddParameter(new FilterParameter(20.0f));

// Create a custom filter function definition
var custom = new FilterFunction(functionDef);
// Add parameters to the custom filter
custom.AddParameter(new FilterParameter(Color.black));

// Apply both the blur and custom filter to the visual element
element.style.filter = new List<FilterFunction> { blur, custom };

Динамическая разница

Вы можете использовать обратный вызов C# для динамического вычисления границ для фильтра.

В следующем фрагменте кода C# задаются динамические поля для функции размытия. Поскольку дополнительные поля зависят от радиуса размытия, статических полей здесь недостаточно.

Перед расчетом размера текстуры рендеринга вызовите этот обратный вызов. Это позволит вам указать поля для заданной FilterFunction. В этом примере радиус размытия читается как первый параметр функции, который затем используется для установки полей.

effect.computeRequiredReadMarginsCallback = ComputeMargins;
effect.computeRequiredWriteMarginsCallback = ComputeMargins;

// ...

static PostProcessingMargins ComputeMargins(FilterFunction func)
{
    float radius = func.GetParameter(0).floatValue; // Blur radius value
    return new PostProcessingMargins() {
        left = radius, top = radius, right = radius, bottom = radius
    };
}

Комплексный параметр

Для сложных параметров используйте обратный вызов C#. В следующем примере показано, как использовать обратный вызов для обработки параметра фильтра. Он преобразует одно значение сепии в полноцветную матрицу. Реализуя обратный вызов, вы можете вручную устанавливать свойства материала вместо того, чтобы полагаться на автоматическую привязку параметров. Для этого предоставьте обратному вызову MaterialPropertyBlock, который затем устанавливает свойства материала напрямую, обходя процесс автоматической привязки.

effect.prepareMaterialPropertyBlockCallback = PrepareSepiaMatrix;

// ...

static void PrepareSepiaMatrix(MaterialPropertyBlock mpb, FilterFunction func)
{
    float s = func.parameters[0].floatValue; // Sepia value

    var colorMatrix = new Matrix4x4(
        new Vector4(0.393f + 0.607f * (1 - s), 0.349f - 0.349f * (1 - s), 0.272f - 0.272f * (1 - s), 0),
        new Vector4(0.769f - 0.769f * (1 - s), 0.686f + 0.314f * (1 - s), 0.534f - 0.534f * (1 - s), 0),
        new Vector4(0.189f - 0.189f * (1 - s), 0.168f - 0.168f * (1 - s), 0.131f + 0.869f * (1 - s), 0),
        new Vector4(0, 0, 0, 1));

    mpb.SetMatrix("_ColorMatrix", colorMatrix);
}

Настраиваемый фильтр UV регион

UI Toolkit отображает отфильтрованные элементы в текстурный атлас до применения фильтров-шайдеров. В результате соответствующие координаты UV покрывают только подрегион полного диапазона 0–1. При рисовании фильтров координаты UVs, предоставленные фрагмент-шайдеру, настраиваются для соответствия региону UV. В простых случаях достаточно взять текстуру с координатами UV.

Однако, если ваш пользовательский фильтр требует манипуляций с UV, вам нужно преобразовать UVs в диапазон 0-1, а затем обратно в прямоугольник атласа UV. Чтобы выполнить преобразование, сначала извлеките индекс прямоугольника фильтра UV из данных вершины с помощью функции GetFilterRectIndex:

#include "UnityUIEFilter.cginc"

v2f vert (FilterVertexInput v)
{
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    o.rectIndex = GetFilterRectIndex(v); // Pass filter UV rect index to fragment shader
    return o;
}

В вершинном или фрагментном шейдере вы можете извлечь прямую UV с помощью функции GetFilterUVRect, а затем переотобразить UVs в и из диапазона 0-1:

float2 NormalizeUVs(float2 uv, float4 uvRect)
{
    // Normalize UV coordinates based on the atlas rect
    return float2(
        (uv.x - uvRect.x) / uvRect.z,
        (uv.y - uvRect.y) / uvRect.w
    );
}

float2 MapToUVRect(float2 uv, float4 uvRect)
{
    // Map UV coordinates to the atlas rect
    return float2(
        uv.x * uvRect.z + uvRect.x,
        uv.y * uvRect.w + uvRect.y
    );
}

half4 frag (v2f i) : SV_Target
{
    // Get the UV rect from the index
    float4 uvRect = GetFilterUVRect(i.rectIndex);

    // Convert the atlas UVs to the 0-1 range
    float2 uv = NormalizeUVs(i.uv, uvRect);

    // Do UVs manipulations, (e.g. flip Y axis)
    uv.y = 1.0f - uv.y;

    // Convert the UVs back to the atlas region
    uv = MapToUVRect(uv, uvRect);

    return tex2D(_MainTex, uv);
}

Примечание: Текстура, переданная в _MainTex при создании пользовательского фильтра, предварительно умножена. Это означает, что значения RGB уже были умножены на альфа-канал, так как текстура является прямым результатом предыдущего прохождения.

Настраиваемый фильтр и гамма-соображения силы

При работе в UI Toolkit силой гамма рабочий поток в линейное цветовое пространство (когда вы включаете Гамма-рендеринг сил в 2000 году Настройки панели asset), вам необходимо принять дополнительные меры в отношении цветового выхода настраиваемых фильтров. В этом рабочем процессе последний примененный фильтр должен линейизировать цвет перед выходом.

Чтобы включить функциональность UIE_OUTPUT_LINEAR, включите в свой шейдер инструкцию multi-compile:

#pragma multi_compile _ _UIE_OUTPUT_LINEAR

UI Toolkit автоматически включает ключевое слово UIE_OUTPUT_LINEAR на материале при применении последнего фильтра, что позволяет проверить это ключевое слово и понять, когда нужно линеаризовать цвет. Учтите, что это требуется только при рабочем процессе force gamma. Для интерфейса редактора в линейных проектах force gamma включена неявно.

Следующий пример показывает, как обрабатывать соображения силы гаммы в пользовательском фильтре шейдера:

half4 frag(v2f IN) : SV_Target
{
    half4 col = tex2D(_MainTex, IN.uv);

    // Apply filter effect.

    // Linearize the color for the force gamma workflow.
    #if UIE_OUTPUT_LINEAR
    col.rgb = GammaToLinearSpace(col.rgb);
    #endif

    return col;
}

В следующей таблице кратко описано поведение настраиваемых фильтров в различных конфигурациях цветового пространства проекта при чтении из и записи в конечные объекты отображения:

Project конфигурация Положение фильтра Ввод цветового пространства Выходное цветовое пространство Требуется специальная обработка
Проект «Гамма» Любой Гамма Гамма Нет. Шейдеры работают в гамма-пространстве
Линейный проект без силы гамма Любой Линейный Линейный Нет. Шейдеры работают в линейном пространстве
Линейный проект с силой гамма Неокончательный пропуск Гамма Гамма Нет. Промежуточные проходы сохраняют гамму
Линейный проект с силой гамма Финальный проход Гамма Линейный Преобразовать выход в линейный с помощью GammaToLinearSpace

ПримечаниеПри использовании MaterialPropertyBlock.SetColor для установки параметров цвета в пользовательских фильтрах в линейном проекте, в котором включена функция force gamma, цвета предоставляются в линейном пространстве, в то время как ваш фильтр-шайдер работает в гамма-пространстве (как показано в предыдущей таблице). Чтобы сохранить точность цвета, преобразуйте эти цветовые параметры в гамма-пространство в вашем шейдере:

// Convert color parameter from linear to gamma space in shader.
fixed4 colorParam = _ColorParameter;
colorParam.rgb = LinearToGammaSpace(colorParam.rgb);
// Use the converted colorParam in your gamma-space filter calculations.

Это преобразование обеспечивает соответствие параметров цвета гамма-пространству цветов, в котором работает фильтр, предотвращая проблемы с точностью цвета.

Дополнительные ресурсы