Композитор рендеринга
Стадии Forward+ и Mobile, override камеры, проверенный C# callback и владение буферами.
Compositor подключает собственные эффекты к стадиям 3D-рендеринга. Используйте его для работы с буферами кадра, когда материала объекта и настроек Environment недостаточно. Поддерживаются Forward+ и Mobile; Compatibility этот путь не поддерживает.
Подключить эффект
- Создайте ресурс
Compositorв свойстве Compositor уWorldEnvironment. - Добавьте свой
CompositorEffectв Compositor Effects. Пустая строка массива не создаёт эффект. - Выберите Effect Callback Type, оставьте Enabled включённым и проверьте активную
Camera3D. - Если у камеры назначен собственный compositor, используется он: списки камеры и мира не объединяются. Даже пустой compositor камеры заменяет мировой. Удаление ресурса из свойства камеры восстанавливает мировой compositor.
Эффекты одной стадии выполняются в порядке массива. Разные стадии следуют порядку рендеринга, независимо от положения ресурсов в массиве. Getter CompositorEffects возвращает новый контейнер с общими ссылками на эффекты. После изменения состава назначьте массив обратно. Изменение effect.Enabled действует без повторного назначения массива.
Выбрать доступную стадию
| Стадия | Forward+ | Текущий Mobile | Момент вызова |
|---|---|---|---|
PreOpaque | Да | Да | До непрозрачной геометрии; это ещё не готовый цвет кадра. |
PostOpaque | Да | Нет, предупреждение | После непрозрачной геометрии. |
PostSky | Да | Нет вызова | После неба; стадия для отдельного specular. |
PreTransparent | Да | Да | До прозрачной геометрии. |
PostTransparent | Да | Да | После прозрачной геометрии, перед последующей постобработкой и tonemapping. |
Тест Windows/Vulkan подтвердил эти вызовы. Mobile выдаёт Post opaque rendering effect callback is not supported in the mobile renderer при наличии PostOpaque. Не переносите эффект между renderer без проверки стадий. Max — служебная граница перечисления, не стадия.
Проверить подключение из C#
Сохраните класс в BufferProbeEffect.cs, соберите C#-проект и добавьте этот ресурс в Compositor Effects. Пример проверяет размер и RID цвета/глубины каждого view. Он не меняет изображение и не является готовым фильтром.
using System.Threading;
using Reckfell;
[GlobalClass]
public partial class BufferProbeEffect : CompositorEffect
{
private int callbacks;
private int validFrames;
public int CallbackCount => Volatile.Read(ref callbacks);
public int ValidFrameCount => Volatile.Read(ref validFrames);
public BufferProbeEffect()
{
EffectCallbackType = EffectCallbackTypeEnum.PostTransparent;
}
public override void _RenderCallback(int effectCallbackType, RenderData renderData)
{
if (effectCallbackType != (int)EffectCallbackTypeEnum.PostTransparent)
return;
Interlocked.Increment(ref callbacks);
if (renderData.GetRenderSceneBuffers() is not RenderSceneBuffersRD buffers)
return;
Vector2I size = buffers.GetInternalSize();
if (size.X <= 0 || size.Y <= 0 || buffers.GetViewCount() == 0)
return;
for (uint view = 0; view < buffers.GetViewCount(); view++)
{
if (!buffers.GetColorLayer(view).IsValid || !buffers.GetDepthLayer(view).IsValid)
return;
}
Interlocked.Increment(ref validFrames);
}
}Через несколько отрисованных кадров CallbackCount и ValidFrameCount должны расти. Читать их можно из обычного скрипта, сохранив ссылку на назначенный эффект. При Enabled=false рост прекращается после завершения уже поставленных кадров. Так можно отличить ошибку подключения от ошибки собственного shader.
Буферы и дополнительные данные
RenderData и буферы принадлежат рендереру. Используйте их внутри callback, не сохраняйте RenderData на следующий кадр и не освобождайте чужие RID через FreeRid. Размер берите из GetInternalSize(), а не окна: масштаб рендеринга может их различать. Перебирайте GetViewCount() и слои каждого view; один слой не покрывает XR-стерео. Действующий RID не гарантирует содержимое готовой сцены на ранней стадии.
| Флаг | Для чего нужен |
|---|---|
AccessResolvedColor | Запрос обычного цветового буфера на промежуточной стадии при MSAA, а не отключение MSAA. |
AccessResolvedDepth | Запрос разрешённой глубины при MSAA; поддержка зависит от renderer и устройства. |
NeedsMotionVectors | В Forward+ запрашивает подготовку векторов движения. |
NeedsNormalRoughness | В Forward+ влияет на подготовку normal/roughness в prepass. |
NeedsSeparateSpecular | Отдельный specular в Forward+ на PostSky; скрыт для других стадий. |
Все флаги изначально выключены. В Inspector настройки resolved color/depth скрыты для PostTransparent. Запрашивайте только используемые данные: подготовка может добавлять проходы и память. Mobile не обязан предоставлять дополнительные буферы Forward+.
Потоки и владение
Callback выполняется на пути рендеринга. Не меняйте там дерево Entity, UI или ресурсы, одновременно используемые главным потоком. Пример обменивается только атомарными счётчиками; для своих параметров подготовьте согласованный снимок.
Используйте основной device через RenderingServer.GetRenderingDevice(): отдельный local device не владеет RID буферов кадра. Создавайте и освобождайте свои GPU-ресурсы на render thread, при необходимости через RenderingServer.CallOnRenderThread. Переиспользуйте shader/pipeline; при смене внутреннего размера пересоздавайте зависимые текстуры и uniform sets. Освобождайте только созданные вами RID после прекращения использования.
Диагностика
- Нет callback: проверьте Enabled, активную камеру, её override, массив эффектов и доступность стадии.
- Нет данных: проверьте стадию, размер, тип
RenderSceneBuffersRDи необходимые флаги. PreOpaque не содержит готовую непрозрачную сцену. - Эффект в одном глазу: обрабатывайте все view и слои.
- Ошибки после resize: проверьте размеры dispatch, границы shader и пересоздание своих ресурсов.
- Результат меняется после tonemapping: PostTransparent не означает «после всех эффектов».
Проверены реальные callback-вызовы, доступность RID, Enabled, копирование контейнера и override камеры в Windows/Vulkan Forward+ и Mobile. Не проверены compute shader, изменение пикселей, MSAA, XR, resize, reflection probes, производительность и другие backend. В текущих исходниках reflection probes исключаются из обычной диспетчеризации этих callback.