reckfellENGINE

CanvasItem: цвет, координаты и освещение 2D

Полные шейдеры, таблицы встроенных значений, регионы Sprite2D и диагностика.

На этой странице

CanvasItem-шейдер управляет отрисовкой 2D: спрайтов, элементов интерфейса и других объектов CanvasItem. Ресурс Shader назначается в ShaderMaterial, а материал — объекту. Иерархия Entity и граф VisualShader — отдельные механизмы.

Первый работающий материал

  1. Создайте Sprite2D с видимой текстурой. Оставьте Modulate и SelfModulate белыми.
  2. Назначьте новый ShaderMaterial в Material.
  3. Создайте Shader в свойстве Shader материала и вставьте полный код ниже.
  4. Измените tint у материала: белый сохраняет исходный вид, зелёный оставляет зелёный канал, Alpha=0 скрывает объект.
Шейдер
shader_type canvas_item;
render_mode unshaded;
uniform vec4 tint : source_color = vec4(1.0);
void fragment() {
    COLOR *= tint;
}

В C# значение меняется через material.SetShaderParameter("tint", new Color(0, 1, 0, 1)). Имя должно совпадать с uniform. Два разных ShaderMaterial могут использовать один Shader с независимыми значениями; общий материал даёт общие параметры. Передача default(Variant) снимает значение материала и возвращает начальное значение uniform.

Пример использует unshaded, чтобы освещение не мешало проверять цвет. Для реакции на Light2D уберите этот режим. Подсказка source_color нужна для цветового параметра; обычный числовой вектор не следует автоматически считать цветом.

COLOR уже содержит текстуру

МестоСодержимое COLOR
Вход vertex()Цвет примитива × Modulate × SelfModulate.
Вход fragment()Интерполированный цвет вершин × выборка основной TEXTURE.
Выход fragment()Цвет и прозрачность, передаваемые дальнейшему освещению и смешиванию.

Поэтому COLOR *= texture(TEXTURE, UV) обычно умножает текстуру второй раз. Это частая причина затемнения. Чтобы получить только текстуру и обойти окраску вершин, задайте COLOR напрямую:

Шейдер
shader_type canvas_item;
render_mode unshaded;
void fragment() {
    COLOR = texture(TEXTURE, UV);
}

Чтобы, наоборот, использовать цвет вершин без основной текстуры, передайте его через varying:

Шейдер
shader_type canvas_item;
render_mode unshaded;
varying vec4 vertex_color;
void vertex() {
    vertex_color = COLOR;
}
void fragment() {
    COLOR = vertex_color;
}

В проверке текстура (0.8, 0.4, 0.2), Modulate (0.5, 1, 1) и SelfModulate (0.5, 1, 1) дают RGB (0.2, 0.4, 0.2). Повторное умножение текстуры даёт (0.16, 0.16, 0.04). Это функциональная проверка арифметики обычного 2D, а не правило для любого HDR/цветового конвейера.

Координаты: не смешивайте пространства

В vertex() VERTEX — локальные координаты геометрии в пикселях. По умолчанию движок применяет MODEL_MATRIX, затем CANVAS_MATRIX и SCREEN_MATRIX. Последние этапы учитывают холст/камеру и проекцию. Фрагментный VERTEX уже содержит положение в координатах холста после его преобразования; это не исходный локальный VERTEX.

UV адресует текстуру, SCREEN_UV — текущий viewport. FRAGCOORD.xy содержит координаты центра пикселя viewport, а не значения 0–1. Для проверенного Vulkan-пути начало сверху слева: FRAGCOORD.xy * SCREEN_PIXEL_SIZE совпадает с SCREEN_UV. Размер окна ОС и размер Sprite2D здесь не заменяют разрешение viewport.

TEXTURE_PIXEL_SIZE — обратный размер исходной текстуры: для 8×4 это (0.125, 0.25), даже если спрайт увеличен. Для отдельного пользовательского sampler используйте его собственный размер, а не размер основной TEXTURE.

У Sprite2D с регионом UV уже адресует подпрямоугольник общей текстуры. В проверенном прямоугольном пути REGION_RECT хранит нормализованные смещение и размер: регион (2,1,4,2) текстуры 8×4 даёт (0.25,0.25,0.5,0.5). Если нужен UV внутри этого региона, вычтите REGION_RECT.xy и разделите на REGION_RECT.zw при ненулевом размере. Не применяйте смещение региона повторно к уже готовому UV. Для flip, атласа, кадров анимации и других примитивов сначала проверьте фактические координаты.

Режим skip_vertex_transform отключает только автоматический переход из локального пространства в мировое. Полный пример сохраняет его вручную; преобразование холста и проекция остаются автоматическими:

Шейдер
shader_type canvas_item;
render_mode unshaded, skip_vertex_transform;
void vertex() {
    VERTEX = (MODEL_MATRIX * vec4(VERTEX, 0.0, 1.0)).xy;
}
void fragment() {
    COLOR = vec4(UV, 0.0, 1.0);
}

В world_vertex_coords, напротив, MODEL_MATRIX уже применяется до пользовательского vertex(). Не умножайте на неё повторно.

Режимы отрисовки

РежимДействие
blend_mixОбычное смешивание по alpha; режим по умолчанию.
blend_addАддитивное смешивание.
blend_subВычитающее смешивание.
blend_mulУмножающее смешивание.
blend_premul_alphaRGB должен быть заранее умножен на alpha; не умножайте его дважды.
blend_disabledСмешивание выключено; alpha=0 сама по себе не скрывает RGB.
unshadedОбход освещения и модуляции холста; исходная окраска объекта сохраняется.
light_onlyПоказывает вклад освещения вместо обычного базового цвета.
skip_vertex_transformЛокальное → мировое преобразование задаётся в vertex() вручную.
world_vertex_coordsПользовательский vertex() получает мировые координаты.

Выберите один blend-режим. Для premultiplied alpha сначала вычислите окончательную alpha, затем умножьте RGB на неё ровно один раз. discard отбрасывает выбранный фрагмент; простое обнуление alpha зависит от blend-режима.

Встроенные значения по этапам

in — только чтение; out — результат, которому нельзя приписывать осмысленное начальное значение; inout — начальное значение с возможностью изменения. Sampler предназначен для чтения выборок. Доступность имени определяется этапом: varying передаёт выбранные данные между этапами, но не делает любой встроенный символ доступным везде.

TIME доступен глобально: время рендерера в секундах с учётом Engine.TimeScale. Пауза дерева сама по себе его не останавливает. Период сброса задаёт rendering/limits/time/time_rollover_secs, по умолчанию 3600 секунд. Для управляемой игровой анимации передавайте собственный uniform времени. Глобальные PI, TAU и E — математические константы float.

vertex()

Тип и имяЗначение
inout vec2 VERTEXЛокальная позиция; режимы преобразования описаны выше.
inout vec2 UVКоординаты текстуры, передаваемые fragment().
inout vec4 COLORЦвет вершин с окраской объекта, до основной текстуры.
inout float POINT_SIZEРазмер примитива точки.
in mat4 MODEL_MATRIX / CANVAS_MATRIX / SCREEN_MATRIXЛокальные → мировые → холст → clip координаты.
in int INSTANCE_ID / VERTEX_IDИндексы экземпляра отрисовки и вершины; не ID игрового Entity.
in vec4 INSTANCE_CUSTOMПользовательские данные экземпляра; смысл задаёт производитель. У частиц обычно rotation, phase, animation frame в xyz.
in vec4 CUSTOM0 / CUSTOM1Дополнительные атрибуты примитива, если они предоставлены.
in vec2 TEXTURE_PIXEL_SIZEОбратный размер основной текстуры.
in bool AT_LIGHT_PASSВсегда false; не переключатель повторного прохода.

fragment()

Тип и имяЗначение
in vec4 FRAGCOORDЦентр пикселя viewport; xy в пикселях.
in vec2 SCREEN_UV / SCREEN_PIXEL_SIZEUV viewport / обратное разрешение viewport.
in vec2 UV / POINT_COORDUV текстуры / координаты внутри точечного примитива.
in vec4 REGION_RECTПрямоугольник источника текущего пути отрисовки; для проверенного Sprite2D нормализован.
sampler2D TEXTURE / NORMAL_TEXTURE / SPECULAR_SHININESS_TEXTUREОсновная текстура, нормали и specular/shininess.
in vec2 TEXTURE_PIXEL_SIZEОбратный размер TEXTURE.
in vec4 SPECULAR_SHININESSВыборка цвета/показателя блеска.
inout vec3 NORMALНормаль, которую использует освещение 2D.
out vec3 NORMAL_MAP / out float NORMAL_MAP_DEPTHНормаль в кодировке карты и глубина её влияния; NORMAL_MAP заменяет NORMAL.
inout vec2 VERTEX / SHADOW_VERTEXПоложение фрагмента / отдельное положение для расчёта теней; изменение здесь не перемещает растеризованную геометрию.
inout vec3 LIGHT_VERTEXПоложение для освещения, z — высота.
inout vec4 COLORЦвет с основной текстурой; выход этапа.
in bool AT_LIGHT_PASSВсегда false.

light()

Тип и имяЗначение
in vec4 FRAGCOORDТот же центр пикселя в пикселях, не нормализованный UV.
in vec3 NORMAL / LIGHT_VERTEXНормаль / положение для расчёта света после fragment().
in vec4 COLOR / SPECULAR_SHININESSЦвет после fragment() / параметры блеска.
in vec2 UV / SCREEN_UV / POINT_COORD / TEXTURE_PIXEL_SIZEUV текстуры, UV viewport, координата точки и обратный размер текстуры.
sampler2D TEXTUREОсновная текстура объекта.
in vec4 LIGHT_COLOR / in float LIGHT_ENERGYЦвет текущего света и энергия; у PointLight2D учитывается текстура света.
in vec3 LIGHT_POSITION / LIGHT_DIRECTIONПозиция и направление света в пространстве освещения; у направленного света позиция нулевая.
in bool LIGHT_IS_DIRECTIONALПризнак DirectionalLight2D.
inout vec4 LIGHTРезультат текущего света; пользовательская функция заменяет стандартную.
out vec4 SHADOW_MODULATEМножитель цвета теней в этой точке.

Освещение и карты нормалей

Без light() действует стандартное освещение. Объявление собственной light() заменяет его, даже если тело пустое: в проверенном случае вклад становится нулевым. unshaded обходит пользовательское освещение. Для проверки источника настройте его текстуру, энергию, видимость и совпадающие маски объекта/света; CanvasModulate влияет на базовое освещение.

Полный шейдер с вкладом света по нормали:

Шейдер
shader_type canvas_item;
void light() {
    float ndotl = max(0.0, dot(NORMAL, LIGHT_DIRECTION));
    LIGHT = vec4(LIGHT_COLOR.rgb * COLOR.rgb * LIGHT_ENERGY * ndotl, LIGHT_COLOR.a);
}

Это простой диффузный расчёт, без отдельной specular-модели. CanvasTexture позволяет задать карту нормалей. Для явной выборки карты в кодировке RGB используйте NORMAL_MAP, например присвойте ему texture(NORMAL_TEXTURE, UV).rgb внутри fragment(); NORMAL — уже вектор нормали для освещения. Не подменяйте его необработанным RGB. Проверьте ориентацию карты на реальном источнике света.

SDF: отдельное пространство координат

LightOccluder2D с назначенным OccluderPolygon2D и включённым SDF Collision участвует в поле расстояний viewport. У нового LightOccluder2D этот флаг включён конструктором. Видимость и область/масштаб SDF viewport влияют на результат.

ФункцияНазначение
vec2 screen_uv_to_sdf(vec2 uv)UV viewport → координаты поля SDF.
vec2 sdf_to_screen_uv(vec2 sdf_pos)Обратное преобразование в UV viewport.
float texture_sdf(vec2 sdf_pos)Чтение расстояния в пространстве SDF, а не произвольных пикселях окна.
vec2 texture_sdf_normal(vec2 sdf_pos)Направление градиента поля; не нормаль 3D-модели.

Эти функции доступны из fragment()/light() и вызванных оттуда функций, но не vertex(). Не передавайте обычный UV текстуры в texture_sdf без перевода пространства. Для экранной текстуры используйте собственный sampler с hint_screen_texture; встроенного SCREEN_TEXTURE в текущем реестре нет. См. экранные текстуры.

Диагностика

СимптомЧто проверить
Текстура слишком тёмнаяПовторное умножение TEXTURE, Modulate/SelfModulate и влияние освещения.
Изменение tint не действуетТочное имя, нужный ShaderMaterial и отсутствие перезаписи COLOR ниже по коду.
Alpha=0, но RGB виденblend_disabled, аддитивный режим или неверный premultiplied RGB.
Эффект растягивается при масштабированииИспользуется размер текстуры, объекта или viewport не из того пространства.
Регион выбирается дваждыUV уже включает источник региона; проверьте REGION_RECT.
Объект смещён/исчез после vertex()Повторный MODEL_MATRIX либо пропущенный переход при skip_vertex_transform.
Свет исчез после добавления light()Пользовательская функция должна записывать LIGHT; проверьте режим, источник и маски.
SDF не соответствует объектамПолигон окклюдера, SDF Collision, область viewport и преобразование screen_uv_to_sdf.

В Windows Forward+/Vulkan проверены пять полных примеров выше и фактические пиксели цвета, alpha, координат, региона, преобразования и пользовательского света. Полная матрица встроенных значений, SDF, TIME/пауза, прочие blend-режимы и world_vertex_coords сверены по исходникам; для них здесь не заявлена отдельная проверка всех платформ, рендереров и экспортов.

Документация Reckfell EngineНаверх ↑
БЫСТРЫЙ ПЕРЕХОДНачало работыEntity и иерархияC# & .NET 10Сцены и префабы
Руководства и полный API Reckfell K
reckfellENGINE
RECKFELL ACCOUNT

Вход в аккаунт

Войдите в аккаунт Reckfell.

Один аккаунт для всей платформы Reckfell.