Fabric язык материалов

В CesiumJS система материалов построена вокруг декларативного описания внешнего вида геометрии через объектную модель, называемую Fabric. Fabric представляет собой JSON-описание материала, которое интерпретируется движком и транслируется в GLSL-шейдеры. Такой подход позволяет описывать визуальные эффекты без прямого ручного управления низкоуровневым WebGL-кодом, сохраняя при этом возможность тонкой кастомизации.

Fabric служит промежуточным слоем между JavaScript-логикой приложения и GPU-шейдерами. Он определяет структуру материала, входные параметры (uniforms), вспомогательные функции и фрагменты шейдерного кода, которые Cesium объединяет в итоговый материал.

Структура Fabric-объекта

Fabric описывается как JavaScript-объект, который может быть сериализован в JSON. Базовая структура включает несколько ключевых секций:

  • type — тип материала
  • uniforms — входные параметры шейдера
  • source — GLSL-фрагмент материала
  • дополнительные свойства (например, components, translucent)

Простейший пример Fabric:

const fabric = {
    type: 'Color',
    uniforms: {
        color: Cesium.Color.RED
    },
    source: `
        czm_material czm_getMaterial(czm_materialInput materialInput)
        {
            czm_material material = czm_getDefaultMaterial(materialInput);
            material.diffuse = color.rgb;
            material.alpha = color.a;
            return material;
        }
    `
};

Этот объект описывает материал, который окрашивает поверхность в заданный цвет.

Роль uniforms в Fabric

Uniforms представляют собой динамические параметры, передаваемые из JavaScript в GLSL. Cesium автоматически синхронизирует их между CPU и GPU.

Основные особенности uniforms:

  • могут быть числами, векторами, цветами, текстурами
  • поддерживают реактивное обновление
  • используются внутри GLSL через одноимённые переменные

Пример использования:

uniforms: {
    speed: 10.0,
    frequency: 2.0,
    color: Cesium.Color.BLUE
}

В шейдере:

uniform float speed;
uniform float frequency;
uniform vec4 color;

Uniforms позволяют создавать анимированные и параметрические материалы без изменения GLSL-кода.

GLSL-ядро Fabric

Центральной частью Fabric является строка source, содержащая GLSL-код. Cesium интегрирует этот код в общий пайплайн материалов.

Каждый материал обязан возвращать структуру czm_material, которая описывает:

  • diffuse — базовый цвет
  • alpha — прозрачность
  • emission — самосвечение
  • specular — отражение

Стандартный шаблон:

czm_material czm_getMaterial(czm_materialInput materialInput)
{
    czm_material material = czm_getDefaultMaterial(materialInput);

    // модификация свойств материала
    material.diffuse = vec3(1.0, 0.0, 0.0);
    material.alpha = 1.0;

    return material;
}

Fabric позволяет внедрять пользовательскую логику вычисления цвета, шума, градиентов и процедурных текстур.

Встроенные типы материалов

CesiumJS предоставляет набор стандартных Fabric-материалов, реализованных через ту же систему:

  • ColorMaterial
  • ImageMaterial
  • StripeMaterial
  • CheckerboardMaterial
  • PolylineOutlineMaterial
  • PolylineGlowMaterial

Каждый из них является декларацией Fabric-объекта с предопределённой логикой.

Цветовой материал

ColorMaterial задаёт однородную заливку поверхности.

const material = new Cesium.ColorMaterialProperty(
    Cesium.Color.ORANGE
);

Внутри Fabric это сводится к передаче одного uniform color, который используется для заполнения diffuse.

Image Material и текстурирование

ImageMaterial позволяет накладывать текстуру на геометрию.

Fabric-структура включает:

  • image — URL или текстура
  • repeat — повторение текстуры
  • color — модификатор цвета

Пример:

const fabric = {
    type: 'Image',
    uniforms: {
        image: 'texture.png',
        repeat: new Cesium.Cartesian2(2.0, 2.0)
    }
};

В GLSL текстура выборки выполняется через texture2D, а координаты UV интерполируются из геометрии.

Полосатые и процедурные материалы

StripeMaterial и CheckerboardMaterial демонстрируют процедурный подход Fabric. Они не используют внешние текстуры, а вычисляют цвет на основе координат.

StripeMaterial:

  • использует синусоидальные функции или дискретизацию координат
  • позволяет задавать направление и ширину полос

CheckerboardMaterial:

  • вычисляет чередование цветов через mod() и целочисленные операции

Фрагмент логики:

float x = floor(st.s * stripes);
float y = floor(st.t * stripes);
float pattern = mod(x + y, 2.0);

Polyline материалы

Polyline материалы являются отдельной категорией Fabric, оптимизированной для линий.

PolylineGlowMaterial

Создаёт эффект свечения вдоль линии:

  • используется экспоненциальное затухание от центра линии
  • управляется параметром glowPower

PolylineOutlineMaterial

Сочетает основной цвет и обводку:

  • color — основной цвет
  • outlineColor — цвет контура
  • outlineWidth — толщина

Fabric для линий учитывает экранную координатную систему, а не только мировые координаты, что позволяет сохранять постоянную толщину при масштабировании камеры.

Система объединения Fabric

Cesium не использует Fabric напрямую в шейдере. Перед выполнением происходит этап компоновки:

  1. сбор всех uniforms из JavaScript
  2. объединение GLSL-фрагментов
  3. генерация финального shader program
  4. кэширование результата

Fabric может включать вспомогательные функции:

source: `
    float noise(vec2 st)
    {
        return fract(sin(dot(st, vec2(12.9898,78.233))) * 43758.5453);
    }

    czm_material czm_getMaterial(czm_materialInput materialInput)
    {
        czm_material material = czm_getDefaultMaterial(materialInput);
        float n = noise(materialInput.st);
        material.diffuse = vec3(n);
        return material;
    }
`

Cesium вставляет такие функции в общий namespace шейдера.

Материал с функциями и модульность Fabric

Fabric поддерживает композицию логики через дополнительные секции:

  • functions — вспомогательные GLSL-функции
  • components — разбиение материала на части
  • translucent — управление прозрачностью

Пример:

const fabric = {
    uniforms: {
        amplitude: 1.0
    },
    functions: `
        float wave(float x) {
            return sin(x);
        }
    `,
    source: `
        czm_material czm_getMaterial(czm_materialInput materialInput)
        {
            czm_material material = czm_getDefaultMaterial(materialInput);
            float w = wave(materialInput.st.s * 10.0);
            material.diffuse = vec3(w);
            return material;
        }
    `
};

Поведение translucency

Свойство translucent управляет режимом прозрачности:

  • true — материал участвует в alpha-blending
  • false — отрисовка как непрозрачного объекта
  • функция — динамическое определение прозрачности

Пример:

translucent: function(material) {
    return material.uniforms.alpha < 1.0;
}

Особенности исполнения Fabric в runtime

Fabric-материалы в Cesium проходят несколько этапов жизни:

  • создание MaterialProperty
  • генерация Fabric-объекта
  • компиляция GLSL
  • привязка к Primitive или Entity
  • обновление uniforms в каждом кадре

Материалы могут быть реактивными: изменение uniform автоматически приводит к пересборке или обновлению shader state.

Производительность и ограничения Fabric

Fabric предоставляет высокий уровень абстракции, но накладывает ограничения:

  • увеличение количества уникальных Fabric приводит к росту числа shader program
  • динамические uniforms могут вызывать частые перекомпиляции
  • сложные GLSL-выражения ухудшают производительность на мобильных GPU

Оптимизация обычно достигается через:

  • переиспользование Fabric-объектов
  • минимизацию уникальных шейдеров
  • перенос логики в uniforms вместо GLSL

Пользовательские материалы как расширение Fabric

Создание собственного материала сводится к определению Fabric-объекта с кастомным GLSL:

const customMaterial = new Cesium.Material({
    fabric: {
        type: 'CustomWave',
        uniforms: {
            time: 0.0
        },
        source: `
            czm_material czm_getMaterial(czm_materialInput materialInput)
            {
                czm_material material = czm_getDefaultMaterial(materialInput);
                float wave = sin(materialInput.st.s * 10.0 + time);
                material.diffuse = vec3(0.0, wave, 1.0);
                return material;
            }
        `
    }
});

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