Uniform переменные

Uniform переменные в шейдерах WebGL в контексте CesiumJS представляют собой механизм передачи данных из JavaScript-кода в GPU-шейдеры, которые выполняются на стороне видеокарты. Uniform остаются неизменными в пределах одного вызова отрисовки и служат основным способом управления визуальными параметрами материалов, эффектов и процедурной генерации.

В архитектуре CesiumJS uniform-данные используются на нескольких уровнях: в материалах (Material), в appearance объектов (Appearance), в кастомных шейдерах Primitive, а также в постобработке сцены. Их роль заключается в связывании логики приложения с графическим конвейером WebGL без необходимости пересоздания шейдера при каждом изменении параметров.


В WebGL uniform — это глобальные переменные шейдера, доступные только для чтения в GLSL-коде. CesiumJS абстрагирует работу с ними через JavaScript-объекты, автоматически обновляя значения при рендере сцены.

В типичном случае uniform объявляется в GLSL:

uniform float u_time;
uniform vec3 u_color;
uniform float u_intensity;

В JavaScript-слое CesiumJS этим переменным соответствует объект uniforms, передаваемый в материал или шейдерную конфигурацию:

material.uniforms = {
    u_time: 0.0,
    u_color: Cesium.Color.RED,
    u_intensity: 1.0
};

Движок выполняет автоматическое преобразование типов Cesium → GLSL, включая Color, Cartesian3, числа и текстуры.


Uniform в системе материалов Material

Материальная система CesiumJS использует uniform как основной способ параметризации процедурных материалов.

Материал определяется через Cesium.Material, где GLSL-код описывает визуальное поведение:

const pulseMaterial = Cesium.Material.fromType("Color");
pulseMaterial.uniforms.color = Cesium.Color.YELLOW;

В кастомных материалах структура расширяется:

Cesium.Material._materialCache.addMaterial("pulseMaterial", {
    fabric: {
        type: "pulseMaterial",
        uniforms: {
            time: 0.0,
            speed: 2.0,
            color: Cesium.Color.WHITE
        },
        source: `
            czm_material czm_getMaterial(czm_materialInput materialInput)
            {
                czm_material material = czm_getDefaultMaterial(materialInput);
                float t = sin(time * speed);
                material.diffuse = color.rgb * t;
                material.alpha = 1.0;
                return material;
            }
        `
    }
});

Здесь uniforms становятся частью fabric-описания материала, а Cesium автоматически прокидывает их в шейдер.


Динамические uniform и связь с анимацией

Одним из ключевых применений uniform является динамическое изменение параметров во времени. CesiumJS предоставляет механизм обновления значений на каждом кадре через callback-функции.

const material = new Cesium.Material({
    fabric: {
        type: "DynamicColor",
        uniforms: {
            time: () => performance.now() * 0.001,
            color: Cesium.Color.BLUE
        },
        source: `
            czm_material czm_getMaterial(czm_materialInput input)
            {
                czm_material m = czm_getDefaultMaterial(input);
                m.diffuse = color.rgb * abs(sin(time));
                return m;
            }
        `
    }
});

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


Uniform в Primitive API

На уровне Primitive API uniform используются более явно, так как разработчик напрямую управляет шейдером.

const primitive = new Cesium.Primitive({
    geometryInstances: new Cesium.GeometryInstance({
        geometry: new Cesium.RectangleGeometry({
            rectangle: Cesium.Rectangle.fromDegrees(-100, 30, -90, 40)
        })
    }),
    appearance: new Cesium.MaterialAppearance({
        material: new Cesium.Material({
            fabric: {
                type: "CustomShader",
                uniforms: {
                    brightness: 1.5,
                    offset: 0.2
                },
                source: `
                    czm_material czm_getMaterial(czm_materialInput input)
                    {
                        czm_material material = czm_getDefaultMaterial(input);
                        material.diffuse = vec3(brightness - offset);
                        return material;
                    }
                `
            }
        })
    })
});

Primitive слой требует строгого соответствия типов uniform и GLSL, поскольку отсутствует высокий уровень абстракции Entity API.


Типизация uniform и преобразование данных

CesiumJS поддерживает несколько типов данных для uniform:

  • float → JavaScript Number
  • vec2/vec3/vec4 → Cesium.Cartesian2 / Cartesian3 / Color
  • mat3/mat4 → Cesium.Matrix3 / Matrix4
  • sampler2D → Cesium.Texture

Пример передачи текстуры:

material.uniforms.image = Cesium.buildModuleUrl("Assets/Textures/waterNormals.jpg");

Cesium автоматически загружает и связывает текстуру с uniform sampler2D.


Uniform и система czm_* встроенных переменных

В шейдерах CesiumJS присутствует набор встроенных uniform и varying переменных, начинающихся с префикса czm_. Они предоставляют доступ к состоянию сцены:

  • czm_frameNumber — номер текущего кадра
  • czm_viewerPosition — позиция камеры
  • czm_sunDirection — направление солнца
  • czm_morphTime — время морфинга глобуса

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

uniform float speed;

void main()
{
    float t = czm_frameNumber * speed;
    gl_FragColor = vec4(abs(sin(t)));
}

Эти значения не требуют ручной передачи из JavaScript, но могут комбинироваться с пользовательскими uniform.


Передача uniform через Property систему

В Entity API CesiumJS uniform-значения часто связываются с Property-объектами, позволяющими динамическое обновление на уровне данных.

entity.polygon.material = new Cesium.ColorMaterialProperty(
    new Cesium.CallbackProperty(() => {
        return Cesium.Color.fromRandom({ alpha: 1.0 });
    }, false)
);

Внутри такие конструкции транслируются в uniform-параметры материала, обновляемые на каждом кадре.


Кэширование uniform и производительность

CesiumJS минимизирует стоимость обновления uniform через внутренние механизмы кэширования:

  • одинаковые значения не пересылаются повторно в GPU
  • материалы с одинаковыми uniform могут переиспользовать shader program
  • изменение uniform не вызывает пересборку шейдера

Это критично для сцен с большим количеством объектов, где изменение одного параметра не должно приводить к дорогостоящей перекомпиляции GLSL.


Кастомные шейдеры и ShaderSource

При использовании Cesium.ShaderSource uniform становятся частью единого графа шейдеров.

const shader = new Cesium.ShaderSource({
    sources: [`
        uniform float amplitude;
        void main()
        {
            gl_FragColor = vec4(amplitude);
        }
    `]
});

Далее uniform привязывается через appearance:

appearance.uniforms = {
    amplitude: 0.75
};

Uniform в контексте постобработки

PostProcessStage также активно использует uniform для управления эффектами:

const stage = new Cesium.PostProcessStage({
    fragmentShader: `
        uniform float intensity;
        void main()
        {
            vec4 color = texture2D(colorTexture, v_textureCoordinates);
            gl_FragColor = color * intensity;
        }
    `,
    uniforms: {
        intensity: 1.2
    }
});

Здесь uniform влияет на финальный цвет кадра после рендеринга сцены.


Ограничения и особенности использования

Uniform в CesiumJS имеют ряд технических ограничений WebGL:

  • ограниченный размер uniform buffer
  • невозможность хранения массивов больших объёмов
  • необходимость избегать частого пересоздания объектов
  • зависимость от драйвера GPU при интерполяции типов

При сложных сценах предпочтение отдается текстурам или uniform buffer objects (в расширенных конфигурациях WebGL2).


Практика организации uniform в крупных сценах

В сложных приложениях uniform структурируются как конфигурационные слои:

const uniforms = {
    global: {
        time: 0,
        cameraHeight: 0
    },
    material: {
        glowIntensity: 1.0,
        colorShift: Cesium.Color.WHITE
    }
};

Такой подход упрощает синхронизацию между различными шейдерными программами и снижает риск конфликтов имен.


Связь uniform и архитектуры рендеринга CesiumJS

Внутри render loop CesiumJS uniform обновляются на этапе подготовки команд отрисовки. Каждая primitive или entity формирует render command, содержащий:

  • ссылку на shader program
  • набор uniform
  • состояние рендеринга (blend, depth test)

GPU получает уже готовый пакет данных, минимизируя CPU overhead.


Uniform-подход в CesiumJS формирует базовый слой взаимодействия между JavaScript и GPU-шейдерами, обеспечивая управляемую, предсказуемую и высокопроизводительную модель изменения визуальных параметров в реальном времени.