Эта техника популярна для игр, поскольку позволяет добавить визуального объёма простым объектам, состоящим из плоских полигонов, без существенного понижения производительности, как это было бы при создание физических интерьеров. Иллюзия помещения достигается путём использования cubemaps и эффекта параллакса.

Пример эффективного использования эффекта: Spiderman
Пример эффективного использования эффекта: Spiderman

Но не смотря на это, по какой-то причине, на русском языке нет туториала, как реализовать эффект интерьеров.

Я покажу реализацию в Unity используя Shader Graph, однако, путём некоторых манипуляций, вы можете воспроизвести эффект и в Unreal Engine используя Material Graph (изменить вертикальную ось на z, изменить формат cubemaps). Математика, стоящая за параллаксом, принципиально одинаковая.


Алгоритм параллакса для интерьера

  1. Подготавливаем UV координаты. Поскольку cubemap объёмный, UV координаты используются как Vector3.Умножение понадобиться в дальнейшем, пока не обращайте внимание. Ограничиваем координаты при помощи frac диапозоном от 0 до 1, выставляем точу отсчёта по центру. Хардкодим z = -1.

  2. Подготавливаем вектор взгляда в Tangent пространстве, чтобы он правильно складывался с UV.

Уже сейчас, если использовать этот вектор как координаты для проецирования cubemap, можно получить что-то похожее, только изображение будет слишком растянутым и без параллакса.

  1. Расчёт параллакса. Берём обратное число (Reciprocal) вектора взгляда. Получаем абсолютное значение обратного вектора, умножаем обратной вектор взгляда на UV. Находим разность абсолютного значения и произведения. Находим минимальное значение из трёх координат. Умножаем его на вектор взгляда и добавляем к UV результат произведения

Теперь мы можем передать полученый значения в ноду Sample Cubemap, и вуаля, получим эффект параллакса с иллюзией, будто спроецированные стороны cubemap находятся на внутренней стороне куба

Возвращаясь в начало, можно использовать ноду умножения в самом начале, чтобы контролировать количество проекций интерьеров.

Однако, у этого алгоритма есть минус, одна из сторон оказывается перевёрнута.

"Красивым" математическим способом избавиться от этого переворачивания у меня не удалось, поэтому был имплементирован следующий костыль (ваши предложения по решению проблемы, если такие есть, пишите в комментариях):

Берём z координату и сравниваем её с неким float F по формуле round(z * F) <= F / 2. Если результат сравнения true, то умножаем y координату на -1.

В результате сторона поворачивается, но остаётся артефакт на прилегающей боковой стороне размером 1/(F/2) от длины стороны.

Артефакт при F = 2
Артефакт при F = 2

Подняв F до нескольких тысяч можно минимизировать этот артефакт, однако решение всё равно не идеально.

Артефакт при F = 5000, маленькие полоски видны под некоторыми углами
Артефакт при F = 5000, маленькие полоски видны под некоторыми углами

Почему нельзя пойти простым путём, и просто не проверять, равна ли координата -0.5? Дело в том, что по какой-то причине, у куба в его собственном пространстве ровная боковая грань по какой-то причине не имеет одинаковой координаты, и мы получаем куда более неприятный артефакт:

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


Особенности cubemap

При создании cubemap для текстур сторон следует использовать изображения без перспективы, иначе вы лишь исказите интерьер, которому глубину придаёт шейдер.

Можно также в шейдере интерьера добавить карту нормалей, гладкости и т.д., но я в этом вижу мало смысла. Интерьеры вещь фоновая, и слишком усложнять рендеринг ради минимальной разницы не целесообразно. В конце концов, в человеке пауке этого не делали.


Фасад

Тем более, что интерьеры будут находиться за стеклом. (У вас должна быть либо текстура фасада с альфа прозрачностью на окнах, либо отдельная текстура прозрачности, как у меня, нарисованная в фотошопе).

Превращаем текущий Graph в SubGrapph, который возвращает итоговый Vector3. Можно также открыть UV и переменную являющуюся количеством комнат.

Итак, шейдер фасада:

Выставляем UV координаты. Поскольку у меня на одной текстуре фасада четыре окна, значение FacadeGrid умножаю на два. Использую эффект Френеля, чтобы сделать окна менее прозрачными под большими углами, как в реальной жизни.

Использую текстуру прозрачности умноженную на эффект Френеля для смешивания текстуры и получения базового цвета. Для расчета emission умножаем цвет интерьера на прозрачность с эффектом Френеля. В качестве гладкости передаю просто базовую прозрачность.


Заключение

В результате мы получаем красивый интерьер здания, который ярок и чёток под прямым улом, и скрыт отражением при взгляде со стороны. При этом, мы нисколько не увеличивали число полигонов.

Куб с шейдером интерьеров
Куб с шейдером интерьеров

Комментарии (9)


  1. VBDUnit
    30.08.2023 22:09
    +3

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

    Можно развить идею — добавить в этот куб ещё карты нормали и сделать parallax mapping, чтобы мебель была не плоская и нарисованная на стенах куба, а как‑бы торчала из стены. При этом, для оптимизации, можно умножать силу параллакса на обратное расстояние до камеры, и отключать на определённом расстоянии. Тогда на ближнем плане мебель будет объёмная, по мере удаления она будет незаметно и плавно «врастать» в стены, превращаясь в плоскую, и, начиная с определённой дистанции, шейдер параллакса просто бы переставал работать, чтобы не грузить видеокарту.


    1. Zara6502
      30.08.2023 22:09
      +1

      я когда зашел в статью я и думал что будет показан вариант параллакса, но как-то не вижу я объем, все диваны как были плоские так и остались.

      а, я кажется понял, автор перепутал интерьер и экстерьер. тогда всё встает на свои места.


      1. 5a5ha Автор
        30.08.2023 22:09
        +1

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

        А если для игрока не принципиально или невозможно близко подойти к окну и рассмотреть, то плоскость дивина относительно стены будет незаметна.


        1. Jianke
          30.08.2023 22:09
          +1

          Лучше, тогда, сделать чтобы диван и тому подобные объекты были только на дальней стене, а боковые стены имели бы только встроенную мебель, неторчащую из стен.


        1. Zara6502
          30.08.2023 22:09

          Я и говорю, вы ошиблись с названием, вы сделали объемным экстерьер - здание, а не интерьер - содержимое комнаты.


  1. Zara6502
    30.08.2023 22:09

    Возможно нужно было вставить небольшую анимацию чтобы видеть изменения в условном 3D, потому что я не увидел на скриншотах изменение от первого к последнему.


  1. Jianke
    30.08.2023 22:09

    Ах! Вот как это оказывается устроенно!


  1. Readme
    30.08.2023 22:09

    Для интересующихся — отличный перевод статьи 2018 года на Хабре про эту же технику:
    Иллюзия пространства: как новый Spiderman рендерит помещения без геометрии


  1. Myz17
    30.08.2023 22:09

    Если кто-то захочет сделать это ручками: https://humus.name/index.php?page=3D&ID=80