499 lines
54 KiB
Markdown
499 lines
54 KiB
Markdown
---
|
||
id: aego/material.Vec4.X
|
||
---
|
||
Хранит первую вещественную компоненту числового vec4 материала; она не становится красным цветовым каналом автоматически.
|
||
|
||
---
|
||
id: aego/material.Vec4.Y
|
||
---
|
||
Хранит вторую вещественную компоненту числового vec4 материала; её смысл определяет параметр шейдера.
|
||
|
||
---
|
||
id: aego/material.Vec4.Z
|
||
---
|
||
Хранит третью вещественную компоненту числового vec4 материала; её смысл определяет параметр шейдера.
|
||
|
||
---
|
||
id: aego/material.Vec4.W
|
||
---
|
||
Хранит четвёртую вещественную компоненту; она не считается альфой, если получатель явно не придаёт ей такой смысл.
|
||
|
||
---
|
||
id: engine.MaterialParameter
|
||
---
|
||
Хранит одно явное типизированное переопределение материала узла. Это запись внутри MaterialOverride, а не добавляемый компонент или ассет материала.
|
||
|
||
---
|
||
id: engine.MaterialParameter.Name
|
||
---
|
||
Указывает объявленный параметр шейдера по точному имени, а не по позиции в раскладке материала.
|
||
|
||
---
|
||
id: engine.MaterialParameter.Kind
|
||
---
|
||
Сохраняет объявленный тип параметра, чтобы удаление или последующая смена типа не переинтерпретировали сохранённое значение молча.
|
||
|
||
---
|
||
id: engine.MaterialParameter.Value
|
||
---
|
||
Хранит вещественные, векторные или цветовые компоненты согласно Kind. Integer и Boolean сохраняют значения отдельно.
|
||
|
||
---
|
||
id: engine.MaterialParameter.Integer
|
||
---
|
||
Хранит целочисленное переопределение как точное знаковое 32-битное значение без преобразования через вещественные компоненты.
|
||
|
||
---
|
||
id: engine.MaterialParameter.Boolean
|
||
---
|
||
Хранит явное значение true/false булева параметра; использование поля определяется типом Kind.
|
||
|
||
---
|
||
id: render2d/materials
|
||
title: Материалы и шейдеры
|
||
---
|
||
# Материалы и шейдеры
|
||
|
||
Материал — общий ассет. Назначьте его компоненту [«Отрисовка спрайта»](ref:engine.SpriteRenderer) или [Mesh2D](ref:engine.Mesh2D), затем добавьте [«Переопределение материала»](ref:engine.MaterialOverride) для значений отдельного узла. Итоговое значение выбирается последовательно: значение шейдера по умолчанию, явное значение материала, явное переопределение узла. Режим игры и предпросмотр используют отдельное состояние узлов.
|
||
|
||
## Создание и редактирование
|
||
|
||
1. В палитре **Создать** с поиском выберите **Шейдер**, **Материал** или **Материал из шейдера**. Редактируемые исходники — [.aeshader](ref:formats/aeshader) и [.aemat](ref:formats/aemat).
|
||
2. Выберите материал в **Ассетах** и настройте шейдер, вариант, смешивание, параметры и текстуры. Сохранение общего материала затрагивает всех потребителей этого ассета.
|
||
3. Назначьте его нужному SpriteRenderer или Mesh2D. Отдельно сохраните исходник материала и сцену с этим назначением.
|
||
4. Добавьте **Переопределение материала**, если отличаться должен только этот узел. Измените объявленный параметр экземпляра, сравните второй узел с тем же материалом, затем нажмите **Сброс** для восстановления наследования.
|
||
|
||
Исходник материала и его Инспектор используют общую историю отмены. Одно перетаскивание параметра создаёт один шаг истории. Сброс восстанавливает наследование даже при совпадении явного и унаследованного значений; это не присваивание нуля.
|
||
|
||
Выберите спрайт, назначьте поле материала и добавьте **Переопределение материала**. Переопределять можно только числовые параметры с аннотацией `@instance`. Текстуры, варианты и смешивание принадлежат ассету материала. Удалённые поля сохраняются как неизвестные значения, а неразрешённые ссылки сохраняют свой AssetID.
|
||
|
||
## Переносимый шейдер
|
||
|
||
Пишите GLSL 330 с метаданными Aego. Импортёр подготавливает и проверяет исходники GLSL 330 и GLSL ES 3.00. Он разрешает включения из `engine/` и проектные включения из `assets/shaders/include/`. Общедоступные поля определяются объявлениями: параметр, который драйвер исключил при оптимизации, остаётся в Инспекторе.
|
||
|
||
```glsl
|
||
#pragma aego abi 1
|
||
#pragma domain sprite
|
||
#include "engine/sprite.glsl"
|
||
#pragma material
|
||
uniform float Opacity; // @instance @default(1) @range(0, 1)
|
||
#pragma end
|
||
void fragment() {
|
||
aego_out = aego_sample() * Opacity;
|
||
}
|
||
```
|
||
|
||
`aego_sample()` уже возвращает линейный цвет с RGB, умноженными на альфу. Дополнительная прозрачность умножает весь полученный `vec4` один раз. Обычные значения `vec4` остаются числовыми; семантика Color применяется только к параметрам с аннотацией `@color`. Отдельную текстуру шума считывайте по `aego_local`, независимо от положения спрайта в атласе. `aego_sample_at` ограничивает выборку областью этого спрайта и возвращает прозрачный цвет за её пределами.
|
||
|
||
Sprite и Mesh2D — разные домены шейдеров. Материал должен соответствовать формату вершин своего потребителя. Время кадра принадлежит текущему миру, данные View — текущей камере, значения материала — его принятой ревизии. Пользовательская вершинная стадия отключает отсечение на CPU для использующего её Sprite или Mesh2D. CPU не анализирует GLSL, чтобы угадать границы смещения вершин.
|
||
|
||
## Анимация и перезагрузка
|
||
|
||
Tween и Animation обращаются к объявленному параметру экземпляра через путь свойства. Последняя часть пути — имя параметра из схемы шейдера, например:
|
||
|
||
```text
|
||
MaterialOverride.Flash
|
||
MaterialOverride.Threshold
|
||
```
|
||
|
||
Привязки обновляются после принятия нового поколения разметки. Целочисленные ключи анимации сохраняют точность знакового целого числа; для дробных значений Tween использует интерполяцию с плавающей точкой.
|
||
|
||
Сохраните шейдер или включаемый файл, чтобы импортировать новое поколение. Компиляция, линковка, проверка разметки и миграция зависимых материалов должны завершиться успешно до его принятия на границе кадра. При ошибке сохраняются прежние программа, схема и значения. Панель проблем открывает строку исходного шейдера или включаемого файла; номер столбца не показывается, если компилятор его не сообщил.
|
||
|
||
Ошибка в текущем исходнике блокирует строгий экспорт, даже если сцена продолжает использовать последнюю рабочую версию. Перед экспортом сохраните изменённые буферы шейдеров, материалов и включаемых файлов. Экспорт содержит подготовленные артефакты шейдеров и материалов: игре не требуются импортёр и glslang.
|
||
|
||
## Демонстрация
|
||
|
||
Проект `examples/materials` показывает полный сценарий: полупрозрачные спрайты A/B/A с общим атласом, соседние экземпляры с разными переопределениями, Mesh2D, две камеры, Tween для Flash и анимацию Dissolve. В его README приведены шаги редактирования, отмены, перезагрузки и экспорта на desktop и web. Предпросмотр Инспектора использует отдельный материал и прозрачную внеэкранную поверхность; если GPU недоступен, он показывает причину вместо фиктивного успешного изображения.
|
||
|
||
---
|
||
id: aego/material
|
||
summary: Находит ссылки на встроенные материалы и шейдеры, назначает материал узлу и представляет числовые четырёхкомпонентные значения.
|
||
---
|
||
|
||
Используйте `material.Builtin("Flash")`, а также имена `Dissolve`, `Outline`, `Additive`, `PixelSnap`, `Unlit`, `Lit` и `LitMesh2D` (`Default` — псевдоним Unlit). Неизвестное имя вызывает ошибку. Lit и LitMesh2D вычисляют вклад каждого выбранного источника с учётом нормали своей поверхности. `material.Assign` записывает ссылку в SpriteRenderer или Mesh2D текущего мира. Для пользовательского ассета назначьте экспортируемое поле `AssetRef<Material>` или установите свойство Material соответствующего компонента.
|
||
|
||
```aes run
|
||
package demo
|
||
|
||
import (
|
||
"aego/material"
|
||
"aego/log"
|
||
)
|
||
|
||
script SharedMaterial {
|
||
func Ready() {
|
||
ref := material.Builtin("Flash")
|
||
sprite := node.Get<SpriteRenderer>()
|
||
if sprite != nil {
|
||
material.Assign(node, ref)
|
||
}
|
||
weights := material.Vec4{0.1, 0.2, 0.3, 0.4}
|
||
log.Info("Flash reference available; numeric lane X=" + string(weights.X))
|
||
}
|
||
}
|
||
```
|
||
|
||
У узла с MaterialOverride доступны `SetFloat`, `SetInt`, `SetBool`, `SetVec2`, `SetVec3`, `SetVec4`, `SetColor`, соответствующие методы чтения и `Reset`. Обычный vec4 использует `material.Vec4`, цветовой параметр — линейный `Color`. Несоответствие типа вызывает ошибку до изменения узла.
|
||
|
||
---
|
||
id: engine.MaterialOverride
|
||
summary: Явные типизированные значения материала отдельного узла, наследующие значения общего ассета.
|
||
---
|
||
|
||
Используйте компонент, когда два узла разделяют материал, но им нужны разные числовые значения. Инспектор показывает объявленные параметры экземпляра и унаследованные значения. Сохраняйте переопределения режима редактирования со сценой; изменения во время Play затрагивают игровую копию, а не общий исходник `.aemat`.
|
||
|
||
Методы записи принимают точное `name` параметра шейдера, например `Flash`, а не AssetID или ключ экземпляра постобработки. Тип объявления должен совпадать с методом, а параметр должен разрешать переопределение через `@instance`. Вещественные компоненты должны быть конечными, целые — помещаться в int32, а узел поддерживает не более восьми активных явных параметров. Диапазон в Инспекторе шейдера не обещает автоматического ограничения значения скриптовым методом.
|
||
|
||
Методы чтения возвращают итоговое значение с учётом значений шейдера по умолчанию, материала и переопределения узла. Сброс удаляет явное значение вместо сохранения текущего унаследованного. Удалённые параметры шейдера остаются в `values` для восстановления, но не передаются как активные значения шейдера. У [PostProcess](ref:render2d.PostProcess) другая область действия — экземпляр эффекта.
|
||
|
||
## Пример Flash одного узла
|
||
|
||
Прикрепите скрипт к узлу SpriteRenderer с MaterialOverride и запустите Play. Он назначает встроенный материал Flash, записывает одно переопределение и сбрасывает его. Журнал показывает унаследованное значение, переопределение, результат удаления через Reset и снова унаследованное значение. Без нужных компонентов скрипт сообщает об ошибке, а не пропускает пример молча.
|
||
|
||
```aes check
|
||
package demo
|
||
|
||
import (
|
||
"aego/log"
|
||
"aego/material"
|
||
)
|
||
|
||
script FlashProbe {
|
||
func Ready() {
|
||
sprite := node.Get<SpriteRenderer>()
|
||
values := node.Get<MaterialOverride>()
|
||
if sprite == nil || values == nil {
|
||
log.Error("FlashProbe needs SpriteRenderer and MaterialOverride")
|
||
return
|
||
}
|
||
material.Assign(node, material.Builtin("Flash"))
|
||
inherited := values.GetFloat("Flash")
|
||
values.SetFloat("Flash", 0.65)
|
||
log.Info("inherited=" + string(inherited) + ", explicit=" + string(values.GetFloat("Flash")))
|
||
removed := values.Reset("Flash")
|
||
log.Info("reset=" + string(removed) + ", inherited=" + string(values.GetFloat("Flash")))
|
||
}
|
||
}
|
||
```
|
||
|
||
```aescene check
|
||
scene version=1 {
|
||
node "d3400000000000000000000000000001" name="Flash probe" enabled=true layers=1 {
|
||
transform { position=[0, 0] rotation=0 scale=[1, 1] }
|
||
component "engine.SpriteRenderer" version=1 {
|
||
sprite=asset("ae900000000000000000000000000010") size=[2, 2] tint=#ffffffff
|
||
}
|
||
component "engine.MaterialOverride" version=1 { values=[] }
|
||
component "engine.Script" version=1 { script "game.demo.FlashProbe" enabled=true {} }
|
||
}
|
||
node "d3400000000000000000000000000002" name="Camera" enabled=true layers=1 {
|
||
transform { position=[0, 0] rotation=0 scale=[1, 1] }
|
||
component "engine.Camera2D" version=1 { zoom=40 active=true }
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
id: engine.MaterialOverride.SetFloat
|
||
---
|
||
`name` выбирает float-параметр с `@instance`. `value` должно быть конечным; вызов меняет только переопределение узла и не сохраняет сцену или общий материал.
|
||
|
||
---
|
||
id: engine.MaterialOverride.SetInt
|
||
---
|
||
`value` должно помещаться в знаковый int32, хотя скриптовый `int` шире. Ошибка диапазона или типа сохраняет прежнее переопределение.
|
||
|
||
---
|
||
id: engine.MaterialOverride.SetBool
|
||
---
|
||
`name` выбирает bool-параметр с `@instance`. Это типизированное булево присваивание, а не преобразование целого или вещественного числа.
|
||
|
||
---
|
||
id: engine.MaterialOverride.SetVec2
|
||
---
|
||
`value` задаёт две конечные компоненты объявленного параметра экземпляра vec2. Другая размерность вектора вызывает ошибку типа.
|
||
|
||
---
|
||
id: engine.MaterialOverride.SetVec3
|
||
---
|
||
`value` задаёт три конечные компоненты объявленного параметра экземпляра vec3. Другая размерность вектора вызывает ошибку типа.
|
||
|
||
---
|
||
id: engine.MaterialOverride.SetVec4
|
||
---
|
||
`value` — числовой [material.Vec4](ref:aego/material.Vec4). Для параметра шейдера с цветовой семантикой используйте SetColor: эти типы не взаимозаменяемы.
|
||
|
||
---
|
||
id: engine.MaterialOverride.SetColor
|
||
---
|
||
`value` — линейный Color для vec4-параметра с цветовой подсказкой и `@instance`. Вызов не интерпретирует числовые Vec4 как цвета.
|
||
|
||
---
|
||
id: engine.MaterialOverride.GetFloat
|
||
---
|
||
`name` выбирает объявленный float-параметр. При отсутствии явного значения узла результат учитывает наследование; неразрешённый материал или несовместимый тип вызывают ошибку скрипта.
|
||
|
||
---
|
||
id: engine.MaterialOverride.GetInt
|
||
---
|
||
Возвращает итоговое знаковое значение int32 как скриптовый `int`, без промежуточного вещественного преобразования. Объявленный параметр должен иметь целый тип.
|
||
|
||
---
|
||
id: engine.MaterialOverride.GetBool
|
||
---
|
||
Возвращает итоговое типизированное булево значение. Не трактует нулевой или ненулевой числовой параметр шейдера как bool.
|
||
|
||
---
|
||
id: engine.MaterialOverride.GetVec2
|
||
---
|
||
Возвращает две итоговые компоненты объявленного vec2. Метод не отбрасывает компоненты вектора большей размерности.
|
||
|
||
---
|
||
id: engine.MaterialOverride.GetVec3
|
||
---
|
||
Возвращает три итоговые компоненты объявленного vec3. Метод не отбрасывает компоненты вектора большей размерности.
|
||
|
||
---
|
||
id: engine.MaterialOverride.GetVec4
|
||
---
|
||
Возвращает числовой [material.Vec4](ref:aego/material.Vec4). Параметр с цветовой подсказкой требует GetColor.
|
||
|
||
---
|
||
id: engine.MaterialOverride.GetVec4Lane
|
||
---
|
||
`lane` равен 0 для X, 1 для Y, 2 для Z или 3 для W обычного vec4. Индекс вне диапазона или цветовой параметр вызывают ошибку скрипта.
|
||
|
||
---
|
||
id: engine.MaterialOverride.GetColor
|
||
---
|
||
Возвращает итоговый линейный Color параметра с цветовой подсказкой. Это не преобразование обычного vec4 в цвет.
|
||
|
||
---
|
||
id: engine.MaterialOverride.Reset
|
||
---
|
||
Удаляет явное значение `name` этого узла, в том числе сохранённое для удалённого параметра шейдера. Возвращает false, если явного значения не было; не обнуляет общий материал.
|
||
|
||
---
|
||
id: engine.Light2D
|
||
summary: Освещает спрайты и двумерные меши Lit точечным, коническим, направленным или текстурным источником.
|
||
try: try/Light2D.aescene
|
||
try_select: engine.Light2D
|
||
---
|
||
|
||
[Light2D](ref:engine.Light2D) влияет на [SpriteRenderer](ref:engine.SpriteRenderer) с материалом `Lit` и [Mesh2D](ref:engine.Mesh2D) с `LitMesh2D`. Материалы Unlit сохраняют исходный цвет. Добавьте источник через палитру создания, настройте интенсивность и радиус в Инспекторе, затем перетащите ручки радиуса и направления в Scene View. Один жест создаёт одну команду отмены.
|
||
|
||
Point освещает круг. Spot направлен вдоль локальной +X; `innerAngle` и `outerAngle` задают полную ширину конуса в радианах: внешний угол π/2 означает 90°. Sprite применяет текстуру `cookie` как маску с её собственной областью. Directional использует поворот узла для азимута и `elevation` для направления к поверхности. Тени Directional не поддерживаются; их включение даёт диагностическое сообщение.
|
||
|
||
`height` направляет локальный свет к нормали поверхности. Для плоской нормали и света строго в плоскости спрайта диффузный вклад может быть нулевым. Высота меняет освещение, а окклюдеры остаются плоскими. Радиус задаётся в мировых единицах и не зависит от масштаба узла источника.
|
||
|
||
`layers` выбирает слои получателей. `shadowCasterMask` независимо выбирает слои окклюдеров; эти настройки не заменяют маски столкновений. Один набор получателей учитывает до восьми источников. При превышении бюджета выбор определяется приоритетом, оценкой влияния и постоянным ID. Scene View выделяет исключённые источники; Профайлер считает исключения и разделения батчей по свету. Порядок прозрачных Sprite/Mesh2D сохраняется.
|
||
|
||
В Try it тёплый свет расположен рядом с холодным Spot. Измените интенсивность тёплого источника и сравните его вклад, затем нажмите «Сброс». Источники неподвижны, чтобы результат изменения было легче увидеть.
|
||
|
||
---
|
||
id: engine.LightOccluder2D
|
||
summary: Задаёт полигон или открытую цепочку, отбрасывающую тени от локальных источников без обязательного Physics.
|
||
try: try/LightOccluder2D.aescene
|
||
try_select: engine.LightOccluder2D
|
||
---
|
||
|
||
[LightOccluder2D](ref:engine.LightOccluder2D) задаёт геометрию тени отдельно от видимого спрайта. `Polygon` использует локальные точки; `closed` замыкает простой полигон без отверстий. Отключите его для цепочки. Валидатор отклоняет нечисловые координаты, рёбра нулевой длины и самопересечения. Спрайт не становится окклюдером автоматически.
|
||
|
||
В Scene View перетаскивайте вершину для перемещения, середину ребра для вставки вершины; Alt + щелчок удаляет вершину. Каждый жест использует одну транзакцию History. `ShapeProvider` копирует локальные контуры поставщика, например Physics, по `shapeNode` и постоянному ключу формы. Physics необязателен: Try it использует только ручной полигон. Изменение collision mask не меняет `layers` этого компонента.
|
||
|
||
Point, Spot и Sprite используют общий алгоритм плоских теней. Перекрывающиеся объёмы объединяются. Свет внутри замкнутого контура блокируется им; получатель не игнорирует собственный контур автоматически. `cullMode` выбирает направление рёбер независимо от односторонности физической цепочки. Мягкость фильтрует маску в ограниченном радиусе, не моделируя площадной источник.
|
||
|
||
Окклюдер за экраном участвует, если его тень достигает видимого получателя. Свет и геометрия теней используют presentation transforms, включая интерполированное движение. Движение только камеры не пересчитывает статические маски в пространстве света. Движение света или окклюдера, изменение контура, разрешения или фильтрации инвалидирует нужную маску. Инспектор показывает активные слои и их источники; режим теней Scene View показывает видимость на поверхностях Lit.
|
||
|
||
---
|
||
id: engine.LightingEnvironment2D
|
||
summary: Настраивает фоновое освещение, цветовой режим вывода и бюджет теней для выхода камеры.
|
||
try: try/LightingEnvironment2D.aescene
|
||
try_select: engine.LightingEnvironment2D
|
||
---
|
||
|
||
[LightingEnvironment2D](ref:engine.LightingEnvironment2D) задаёт ambient, экспозицию, `Auto`/`LDR`/`HDR`, разрешение масок и бюджет локальных теней. Поле `camera` выбирает Camera2D; пустая ссылка применяет окружение ко всем двумерным выходам мира. Несколько подходящих окружений дают ошибку: порядок регистрации не выбирает победителя. Без окружения белый ambient с интенсивностью один сохраняет совместимые значения Lit.
|
||
|
||
LDR использует линейную цель RGBA8. HDR использует проверенную RGBA16F и преобразование для вывода. Auto выбирает HDR, когда выходу нужно освещение и устройство его поддерживает. Если framebuffer нельзя использовать, Инспектор показывает причину перехода на LDR. UI и редакторские наложения остаются вне тональной компрессии сцены. Включённый на камере Bloom обрабатывает итоговый цвет мира перед преобразованием для вывода.
|
||
|
||
Scene View имеет собственный выход и не подставляет ID игровой камеры. Нажмите **Предпросмотр в Scene View** в Инспекторе окружения, чтобы явно выбрать его. Изменения полей сразу видны в этом виде. **Использовать окружение сцены по умолчанию** снимает выбор. Он хранится отдельно для каждого документа и не меняет ссылки камер, prefab, Play или историю отмены. Удаление или отключение окружения сохраняет выбранный ID и показывает диагностику до восстановления узла либо сброса выбора.
|
||
|
||
Панель Scene View позволяет отключить освещение или показать преобразованные нормали и видимость теней. Настройки действуют только в этом виде. Вывод прозрачной HDR-цели сохраняет контракт premultiplied alpha. Аддитивное свечение перед отображением нужно смешать с непрозрачным фоном либо сохранить в линейной промежуточной RGBA16F. Additive суммирует RGB и сохраняет альфу назначения.
|
||
|
||
---
|
||
id: aego/lighting
|
||
summary: Меняет освещение и диагностическое отображение presentation view текущего мира.
|
||
---
|
||
|
||
`SetEnabled` сравнивает освещённое изображение с исходным альбедо. `SetDebug` принимает `lighting`, `normals` или `shadows`; неизвестное имя вызывает ошибку до изменения вида. `Enabled` и `Debug` читают состояние вида. Эти вызовы не редактируют ассеты материалов, компоненты сцены или игровое состояние.
|
||
|
||
```aes check
|
||
package demo
|
||
|
||
import (
|
||
"aego/lighting"
|
||
"aego/log"
|
||
)
|
||
|
||
script InspectLighting {
|
||
func Ready() {
|
||
lighting.SetEnabled(true)
|
||
lighting.SetDebug("normals")
|
||
log.Info("Lighting display: " + lighting.Debug())
|
||
}
|
||
}
|
||
```
|
||
|
||
Компоненты света используют обычные типизированные привязки и пути анимации. Например, `Light2D.intensity`, `Light2D.range`, `Light2D.color` и углы конуса доступны Tween и Animation. В дорожке MaterialOverride выберите объявленный параметр Lit по имени: `NormalStrength`, `EmissionIntensity` или `ReceiveShadows`. Ссылки normal map и cookie — ресурсы текстур, а не числовые переопределения экземпляра.
|
||
|
||
### Фонарь в platformer
|
||
|
||
Откройте `examples/platformer`: его основной уровень — каноническая демонстрация освещения. Лампы используют emission для собственного вида и отдельные Light2D для соседних поверхностей. Платформы и стены имеют ручные контуры теней. HUD и меню остаются Unlit. В проект включены реальные исходники альбедо, normal и height с `.meta` и постоянными ID.
|
||
|
||
Измените цвет, высоту или радиус лампы, проверьте Undo/Redo, Play и зарегистрированные действия отладки освещения. Bloom на основной камере создаёт ореол вокруг ярких пикселей лампы и искр. За освещение соседних поверхностей отвечает Light2D. Экспортируйте проект на native и web обычным строгим экспортом. Демо сайта собирается из этого же platformer без отдельной правки копии уровня.
|
||
|
||
---
|
||
id: render2d/normal-mapping
|
||
title: Карта нормалей и движущийся свет
|
||
---
|
||
# Карта нормалей и движущийся свет
|
||
|
||
Левый спрайт использует карту нормалей, полученную из карты высот; правый — то же альбедо и плоскую нормаль. Движущийся точечный свет вычисляет освещение каждой поверхности отдельно. Меняйте высоту, интенсивность и радиус в интерактивном инспекторе. Height задаёт высоту над плоскостью спрайта, а тени остаются плоскими.
|
||
|
||
Полный исходный проект находится в `modules/render2d/res/docs/try/lighting/NormalMapping.assets`. Откройте его `project.aego` в редакторе, чтобы менять изображения, материал и скрипт движения вместе. Копия одного `.aescene` не включает внешние ресурсы. Браузер получает подготовленные артефакты этого проекта через обычный загрузчик ресурсов: импортёр изображений и поиск файлов нормалей во время игры не запускаются.
|
||
|
||
В `surface_height.png.meta` указаны `semantic=height`, `generate_normal=true`, `height_channel=r`, `height_strength=12` и `normal_y=up`. Импорт создаёт линейную карту нормалей. Явная ссылка `NormalMap` материала Lit указывает на её постоянный AssetID. Альбедо имеет цветовую семантику без автоматической связи с нормалью, поэтому правый образец остаётся плоским. Переименование файла вместе с `.meta` сохраняет связь.
|
||
|
||
Для готовой карты задайте `semantic=normal` и `normal_y=up` либо `down` согласно направлению зелёного канала в исходнике. Нормали, маски источника и высоты нельзя импортировать как цвет sRGB. Независимо упакованный атлас нормалей сохраняет собственную область, исходный размер и смещение обрезки. Поворот областей атласа по-прежнему не поддерживается. Если нормаль кадра недоступна, ссылка сохраняется, а до исправления используется плоская нормаль.
|
||
|
||
Если поверхность выглядит плоской, проверьте Lit, пересечение маски света со слоями спрайта, положительный Height, доступность NormalMap и ненулевой NormalStrength. Слишком яркое фоновое освещение скрывает вклад источника. Режим «Нормали поверхности» в окне сцены показывает преобразованную нормаль без экспозиции и тонального преобразования. В `examples/platformer` этот же путь используется в настоящей игре.
|
||
|
||
---
|
||
code: LGT001
|
||
title: Недопустимые параметры света
|
||
---
|
||
|
||
Источник исключён из отрисовки из-за недопустимого типа, интенсивности, радиуса, высоты или угла.
|
||
|
||
**Исправление.** Выберите поддерживаемый тип, конечные неотрицательные интенсивность и высоту, положительный радиус, полные углы Spot в пределах `0 <= InnerAngle <= OuterAngle <= pi` и угол возвышения Directional в интервале `(0, pi/2]`.
|
||
|
||
---
|
||
code: LGT002
|
||
title: Тени направленного света не поддерживаются
|
||
---
|
||
|
||
Направленный свет освещает поверхность, но не поддерживает тени от локальных масок.
|
||
|
||
**Исправление.** Выключите Shadows у Directional или используйте Point, Spot либо Sprite для локальных теней.
|
||
|
||
---
|
||
code: LGT003
|
||
title: Неверные или конфликтующие настройки окружения
|
||
---
|
||
|
||
Для выхода заданы конфликтующие окружения, неверные параметры либо недоступная камера или цель предпросмотра.
|
||
|
||
**Исправление.** Оставьте одно окружение с действующей камерой, неотрицательным ambient, положительной экспозицией, разрешением теней степенью двойки от 64 до 2048 и бюджетом от 0 до 16 либо сбросьте устаревший выбор в Scene View.
|
||
|
||
---
|
||
code: LGT004
|
||
title: Неверный контур окклюдера
|
||
---
|
||
|
||
У контура тени неподдерживаемая геометрия, недопустимый источник или неизвестный режим отсечения.
|
||
|
||
**Исправление.** Выберите поддерживаемый источник и направление обхода, затем задайте простой полигон из 3–256 конечных вершин или открытую цепочку без самопересечений и рёбер нулевой длины.
|
||
|
||
---
|
||
code: LGT005
|
||
title: Недоступны поставщик геометрии или форма
|
||
---
|
||
|
||
Для сохранённой ссылки окклюдера не найден владелец, поставщик геометрии или ключ формы.
|
||
|
||
**Исправление.** Восстановите узел или поставщика, выберите доступную форму либо задайте полигон вручную без Physics.
|
||
|
||
---
|
||
code: LGT006
|
||
title: Недоступная или несовместимая текстура поверхности
|
||
---
|
||
|
||
Текстура освещения отсутствует либо её назначение несовместимо с использованием в материале или источнике света.
|
||
|
||
**Исправление.** Восстановите ресурс с сохранённым AssetID или назначьте совместимую линейную нормаль либо маску и проверьте исходные размеры и обрезку парных кадров.
|
||
|
||
---
|
||
code: LGT007
|
||
title: Неверное преобразование отображения света
|
||
---
|
||
|
||
Из-за вырожденного базиса или неконечных координат отображаемое преобразование не задаёт нужную ориентацию света либо окклюдера.
|
||
|
||
**Исправление.** Проверьте узел и родителей на нулевой масштаб и неконечные координаты как в исходной, так и в интерполированной позе.
|
||
|
||
---
|
||
code: LGT008
|
||
title: Превышен бюджет геометрии или ресурсов освещения
|
||
---
|
||
|
||
Данные освещения превышают бюджет ресурсов либо целевая текстура не может представить запрошенный результат.
|
||
|
||
**Исправление.** Прочтите указанное ограничение и упростите контур, измените бюджет источников либо скомпонуйте аддитивное свечение на непрозрачном фоне до вывода на экран.
|
||
|
||
---
|
||
id: render2d/image-viewer
|
||
title: Просмотр и настройка изображений и спрайтов
|
||
---
|
||
# Просмотр и настройка изображений и спрайтов
|
||
|
||
Откройте PNG двойным щелчком в Assets, перейдите к кадру из Inspector или нажмите **Открыть изображение / спрайт**. Каждый исходник открывается обычной вкладкой рабочей области. Кадры используют документ и историю метаданных своего изображения: открытие другого кадра меняет выбор в той же вкладке. PNG — текущий поддерживаемый формат исходников текстур. Сгенерированные страницы атласа и встроенные текстуры доступны только для чтения. Неподдерживаемые бинарные ассеты показывают информацию о ресурсе и не попадают в текстовый редактор.
|
||
|
||
## Пиксели и упаковка в атлас
|
||
|
||
**Исходные пиксели** — оригинальный файл или выбранный исходный кадр. **Импортированные пиксели** — активный runtime-артефакт с его форматом, линейной обработкой и premultiply. Импортированный кадр показывается в исходном логическом холсте, с прозрачными полями обрезки. **Открыть атлас** отдельно показывает упакованную страницу и выделяет регион. Координаты исходника и идентификаторы кадров не подменяются координатами атласа.
|
||
|
||
Колесо масштабирует изображение под курсором, средняя или правая кнопка перемещает его. **Вписать** подбирает масштаб по области просмотра; **1:1** сопоставляет тексель физическому пикселю экрана с учётом масштаба редактора. При достаточном увеличении видна сетка. RGB и отдельные R/G/B/Alpha показывают исходные байты каналов, включая RGB под нулевой альфой, и не меняют настройки импорта. Строка состояния содержит логические координаты пикселя и четыре значения RGBA8 выбранного представления. RGBA показывает прозрачность на шахматном, светлом или тёмном фоне.
|
||
|
||
Pivot исходника/кадра нормирован в исходном логическом холсте и описывает авторское выравнивание. У превью отдельное начало в центре; существующие узлы сохраняют `SpriteRenderer.Origin`. Выбор кадров использует постоянные ID: переименование и перестановка не создают новые привязки каналов. Этот просмотрщик не рисует пиксели и не создаёт кадры анимации.
|
||
|
||
## Роли и каналы поверхности
|
||
|
||
Просмотрщик, Inspector и структурированные CLI/MCP-мутации используют один документ метаданных. Правки остаются несохранёнными до **Сохранить настройки**. Одно перетаскивание числового поля создаёт один шаг Undo. Чистый документ подхватывает внешние изменения; при несохранённых правках возникает конфликт. Сохранение не перезаписывает неподтверждённую внешнюю версию.
|
||
|
||
| Роль исходника | Значение при импорте |
|
||
|---|---|
|
||
| `color` | Цветовой RGB, заданное кодирование sRGB/linear, линейная альфа, premultiply в линейном пространстве |
|
||
| `normal` | Линейные данные без premultiply; явное направление `normal_y=up/down` |
|
||
| `height` | Линейные данные; выбранный канал, сила и Y используются для генерации нормали при импорте |
|
||
| `emission-rgb` | Straight RGB с заданным цветовым пространством; альфа текстуры не умножает эмиссию |
|
||
| `emission-mask` | Скаляр из выбранного канала, умножающий цвет и интенсивность эмиссии материала |
|
||
| `cookie-mask` / `mask` | Линейная скалярная маска из выбранного канала |
|
||
| `data` | Неизменённые линейные каналы данных |
|
||
|
||
У цветового исходника доступны **Нормаль**, **Источник высот** и **Эмиссия**. Выберите совместимый исходник/кадр или перетащите его из Assets. Проверяются роль, логические размеры и pivot; независимо упакованные карты сохраняют собственные UV и trim. **Отключено явно** отличается от **Наследовать / найти**. Неразрешённый AssetID остаётся виден для исправления или удаления и не заменяется другим файлом молча.
|
||
|
||
Привязка целого листа нужна импортёру для создания постоянных соответствий кадров. Она не растягивает весь лист нормалей на один спрайт. При отсутствии привязки кадра используются плоская нормаль или параметры эмиссии материала. Автопоиск `_n`, `_e` и `_em` превращается в ссылки по ID при импорте; явные привязки приоритетнее.
|
||
|
||
Параметры генерации принадлежат исходной карте высот и общие для всех её потребителей. Откройте связанный источник, чтобы изменить `height_channel`, `height_strength` или `normal_y`. Явная Нормаль имеет приоритет перед Height. Ссылка на сырую карту высот с отключённой генерацией сохраняет ID и объясняет, почему пока не даёт нормаль.
|
||
|
||
## Превью, сохранение и Undo
|
||
|
||
**Lit-поверхность** использует подготовленный встроенный Lit, обычный кэш материалов и настоящий renderer в отдельной сессии. Перетаскивайте свет по поверхности. Высота, цвет, интенсивность, ambient, Lit/Unlit, включение нормали/эмиссии и Flip X/Y действуют только в превью. Они не вызывают игровые скрипты, не меняют сцену или prefab overrides и не сохраняют настройки импорта. Отражение корректно преобразует нормали. Эмиссия меняет собственный выход поверхности, но не освещает соседей и не создаёт Bloom. Без GPU просмотр пикселей и метаданных остаётся доступным, а Lit-превью сообщает об отсутствующем устройстве.
|
||
|
||
Для карты высот включите **Генерировать нормаль при импорте** и меняйте параметры. Приватный кандидат строится по снимкам изображения и текущих метаданных. **Кандидат карты нормалей** показывает его байты; Lit-поверхность использует тот же результат. Одновременно работает одна задача, новые правки заменяют ожидающую. Ошибка импорта или загрузки на GPU оставляет предыдущее рабочее превью. Изменившийся исходник, устаревшая версия метаданных, смена графического контекста или закрытая вкладка не могут опубликовать старый результат.
|
||
|
||
**Сохранить настройки** проверяет текущий кандидат карты высот, сохраняет метаданные и применяет обычную транзакцию импорта/reload. Пиксели PNG не меняются. После явного сохранения **Undo/Redo** также подготавливает и атомарно применяет рабочую версию артефактов в работающей сцене. Буфер метаданных остаётся несохранённым; файл на диске меняется только при следующем явном Save. При ошибке рабочего импорта runtime сохраняет last-good. Закрытие с отбрасыванием правок восстанавливает сохранённые ресурсы. Строгий экспорт редактора блокирует несохранённые метаданные и не экспортирует старую дисковую версию незаметно.
|
||
|
||
В `examples/platformer` откройте `assets/surfaces/platform.png`, перейдите к `platform_height.png`, измените силу, посмотрите сгенерированную нормаль и Lit-поверхность, затем сохраните и отмените изменение, сравнивая платформу в реальном уровне. В `runner.png` выберите существующий кадр и включайте Flip X/Y для проверки связанного кадра нормалей. Лампа показывает настоящий ассет маски эмиссии. Эти же ассеты входят в native/web export и платформер на сайте.
|
||
|
||
## Диагностика
|
||
|
||
| Код | Значение и исправление |
|
||
|---|---|
|
||
| `TEX001` | Некорректная настройка, роль, прямоугольник кадра, размер или pivot канала. Исправьте типизированное значение или выберите соответствующий исходник/кадр. |
|
||
| `TEX002` | Явный AssetID канала не разрешается. Верните ассет, выберите совместимый ID или отключите/сбросьте привязку. Исходный ID сохраняется. |
|
||
| `TEX003` | Подготовка кандидата или импорт завершились ошибкой. Исправьте причину из диагностики импортёра; предыдущая рабочая версия остаётся активной. |
|
||
|
||
Inspector каналов, команды `texture.get/schema/set/channel/channel-reset/reset/validate/apply` и структурированные операции используют одну схему и одинаковые проверки. Из панели можно перейти к зависимостям и потребителям. Байты импортированного артефакта не становятся редактируемым документом.
|