Stencil buffer: маски и трафареты

Stencil buffer (трафаретный буфер) используется для контроля того, какие пиксели будут отрисованы на экране, а какие нет. Он работает совместно с глубинным буфером (depth buffer), но выполняет независимую задачу: создание масок, ограничение области рендеринга и сложные визуальные эффекты, такие как отражения, контуры и секционирование объектов.

В Three.js доступ к stencil buffer обеспечивается через объект WebGLRenderer и параметры материала, связанные с трафаретом.

const renderer = new THREE.WebGLRenderer({ antialias: true, stencil: true });

По умолчанию stencil buffer выключен, поэтому необходимо явно включить его при создании рендерера.


Конфигурация материала для stencil operations

Каждое графическое примитивное в Three.js может управлять stencil buffer через свойства материала:

  • stencilWrite — разрешает запись в stencil buffer (true или false).

  • stencilFunc — функция тестирования, определяет, какие пиксели будут отрисованы. Основные варианты:

    • THREE.AlwaysStencilFunc — всегда пропускает пиксель.
    • THREE.NeverStencilFunc — никогда не пропускает.
    • THREE.LessStencilFunc, THREE.LessEqualStencilFunc, THREE.GreaterStencilFunc и др.
  • stencilRef — опорное значение, с которым сравнивается содержимое stencil buffer.

  • stencilMask — маска для битовой операции с stencil buffer.

  • stencilFail, stencilZFail, stencilZPass — действия, выполняемые при:

    • stencilFail — если тест stencil не прошёл,

    • stencilZFail — если stencil тест прошёл, но depth test не прошёл,

    • stencilZPass — если прошли и stencil, и depth тесты. Варианты действий:

      • THREE.KeepStencilOp — оставить текущее значение,
      • THREE.ZeroStencilOp — обнулить,
      • THREE.ReplaceStencilOp — заменить на stencilRef,
      • THREE.IncrementStencilOp / THREE.DecrementStencilOp.

Пример настройки материала для маски:

const maskMaterial = new THREE.MeshBasicMaterial({
    color: 0xffffff,
    stencilWrite: true,
    stencilFunc: THREE.AlwaysStencilFunc,
    stencilRef: 1,
    stencilZPass: THREE.ReplaceStencilOp
});

Создание маски с помощью stencil buffer

Stencil buffer позволяет выделять области для последующей отрисовки объектов. Основной алгоритм:

  1. Создать объект-маску с материалом, который записывает значение в stencil buffer.
  2. Включить проверку stencil для объектов, которые должны отрисовываться только внутри маски.
  3. Настроить stencil function для объектов сцены.

Пример: отрисовка сферы только внутри куба-маски.

// Куб-маска
const maskGeometry = new THREE.BoxGeometry(2, 2, 2);
const mask = new THREE.Mesh(maskGeometry, maskMaterial);
scene.add(mask);

// Объект для рендеринга
const sphereGeometry = new THREE.SphereGeometry(1, 32, 32);
const sphereMaterial = new THREE.MeshBasicMaterial({
    color: 0xff0000,
    stencilWrite: true,
    stencilRef: 1,
    stencilFunc: THREE.EqualStencilFunc, // отрисовывать только там, где stencil равен 1
});
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
scene.add(sphere);

В этом примере трафаретный буфер ограничивает видимость сферы только областью куба.


Комбинирование stencil и depth buffer

Stencil buffer часто используется совместно с depth buffer для создания эффектов, которые зависят от глубины объектов. Например, отражение на плоскости с маской:

  1. Сначала отрисовывается отражающая плоскость в stencil buffer.
  2. Затем объект зеркально отражается и проходит stencil test — пиксели выводятся только там, где плоскость была отмечена.
  3. Далее рендерится остальная сцена с обычным depth test.

Это позволяет создавать сложные эффекты отражений и просвечивания, не влияя на остальную сцену.


Эффекты с несколькими stencil масками

Three.js позволяет использовать несколько уровней масок, комбинируя разные значения stencilRef. С помощью битовых операций (stencilMask) можно управлять наложением масок:

  • Каждой маске присваивается отдельный бит в stencil buffer.
  • Тест stencilFunc проверяет нужные биты.
  • Можно комбинировать THREE.AndStencilOp, THREE.OrStencilOp, THREE.InvertStencilOp для сложных пересечений областей.

Пример:

material.stencilWrite = true;
material.stencilRef = 0b0010; // второй бит
material.stencilFunc = THREE.EqualStencilFunc;
material.stencilMask = 0b0010;

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


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

Использование stencil buffer увеличивает нагрузку на графический процессор, так как требует дополнительной памяти и операций на пиксель. Рекомендуется:

  • Использовать stencil buffer только для областей, где это действительно необходимо.
  • Минимизировать количество операций stencilZPass и stencilFail.
  • Ограничивать размеры масок и сложность геометрии.

Stencil buffer в Three.js обеспечивает мощный инструмент для создания масок и специальных эффектов, позволяя контролировать рендеринг на пиксельном уровне, обеспечивая гибкость при построении сложных сцен.