Русский
Русский
English
Статистика
Реклама

Шейдеры

Перевод Создание шейдерной анимации в Unity

24.06.2020 08:22:39 | Автор: admin
Недавно я работал над анимацией респауна и спецэффектом главного героя моей игры King, Witch and Dragon. Для этого спецэффекта мне нужна была пара сотен анимированных крыс.


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

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

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

Я подробно опишу создание вот этой анимации:

Rat animation

Подготовка


Для начала я создал в Blender низкополигональную модель крысы.

Rat model

Для разделения модели на разные части (тело, хвост, лапы) я использую UV-координаты. Чтобы всё работало как нужно, модель нужно развернуть особым образом.

Rat UV

Основное движение крысы это прыжки. Чтобы все вершины анимировались плавно и синхронно, нам нужно разместить всё тело крысы вдоль одной из осей. Для удобства я разместил её вдоль горизонтальной оси U.

Глаза, внутренняя часть уха и нос должны иметь другой цвет на текстуре, поэтому я разместил их отдельно, но выполнил смещение только по оси V, не меняя горизонтальных координат. Если переместить их по горизонтальной оси, то их движение не будет синхронизировано с движением головы.

Хвост крысы занимает левую половину развёртки (координаты U от 0,0 до 0,5). Это будет нашей маской хвоста, которую мы используем для анимации хвоста.

Лапы расположены в нижней половине развёртки (координаты V от 0,0 до 0,4). Это наша маска лап. Кроме того, лапы сжаты по горизонтальной оси U, чтобы предотвратить нежелательную деформацию при движении вперёд-назад. Так как в своём проекте я использую cel-shading без детализованной текстуры, сжатые UV не вызовут проблем.

На основании этой UV-развёртки я создал диффузную текстуру. Теперь мы можем начать работу над шейдером.

Если вы хотите повторять все шаги, то можете скачать модель в формате FBX с диффузной текстурой отсюда.

Создание шейдера


Сначала я создам этот шейдер в Shader Graph движка Unity, а затем покажу текстовую версию.

Давайте создадим Unlit Graph, в качестве Preview выберем Custom Mesh, а затем выберем модель крысы.


Накладываем текстуру


Создадим новый параметр Texture 3D, это будет наша основная диффузная текстура. Создадим нод Sample Texture 2D, соединим наш новый параметр с его полем texture, а затем соединим нод с полем Color нода Master.


В дальнейшем мы будем работать только с вершинами.

Основное движение


Мы создадим его при помощи синусоиды. Чтобы настроить форму волны, нам нужны три параметра:

  • Jump Amplitude высота прыжков крысы
  • Jump Frequency частота прыжков крысы
  • Jump Speed скорость движения вдоль вертикальной оси


Два основных нода, которые позволят нам создать анимированное движение это Time и UV. В ноде UV нам нужно будет использовать каждую ось по отдельности, поэтому мы соединим его с нодом Split, который даст нам доступ к каждому из каналов.

Мы можем управлять скоростью прокрутки синусоиды, умножив нод Time на параметр Jump Speed.

Если умножить горизонтальный компонент UV на Jump Frequency, то мы сможем управлять сжатием и растягиванием синусоиды.

Сумма этих произведений даст нам синусоиду нужной формы. По умолчанию синусоида возвращает значения в интервале от -1,0 до 1,0. Если мы умножим их на Jump Amplitude, то получим траекторию прыжка.

Building sive wave

Теперь нам нужно применить результат к позициям вершин, но только к их вертикальной компоненте (локальной оси Y). Мы используем нод Position и соединим его с нодом Split. Затем прибавим значение синусоиды к компоненте Y и соберём всё вместе при помощи нода Combine. Подключим выход этого нода к полю Vertex Position нода Master.

Apply Y-offset

Крыса начала двигаться, но не совсем так, как нам нужно. Она больше походит на дельфина, чем на крысу.

Rat sine wave

Как говорилось выше, синусоида может возвращать как положительные, так и отрицательные значения. Если мы теперь поместим крысу на поверхность, она будет нырять в неё. Кроме того, у синусоиды очень плавные экстремумы, из-за чего анимация походит на плавание.

Чтобы исправить это, мы используем нод Absolute. Эта функция отзеркаливает все отрицательные значения в положительные и выходные данные этой функции всегда положительны.

Absolute values of the sine wave

Сверху обычные значения, снизу абсолютные.

Давайте добавим этот нод к нашему графу.

Graph absolute node

Теперь анимация стала более скачущей.

Rat absolute sine wave

Это уже больше похоже на прыжки или скачки, но всё равно далеко от желаемого. Сейчас проблема заключается в том, что как только лапы касаются земли, они отскакивают обратно в воздух. Это логично с точки зрения формы синусоиды, но неестественно с точки зрения движения крысы. Давайте будем какое-то время удерживать лапы крысы на земле перед следующим прыжком.

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

Sine wave vertical offset

Сверху абсолютное значение, посередине со смещением, внизу максимум между синусом и нулём.

Чтобы реализовать это в графе, нам нужен новый параметр Jump Vertical Offset, который позволит нам настраивать величину сдвига синусоиды.

Graph abs sine offset

Теперь крыса может какое-то время оставаться на земле.

Rat jump with vertical offset

Дополнительное раскачивание хвоста


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

Мы используем UV-координаты для маскировки хвоста и анимирования его отдельно от тела.

Мы знаем, что хвост расположен в левой половине UV-развёртки. Создадим плавный градиент от 0,0 до 0,5 (или даже до 0,6, чтобы эффект был более плавным) по горизонтальной оси U. В 0,0 у нас будет белый цвет, в 0,6 и далее чёрный. Чем ярче пиксель градиента, тем больше дополнительного движения прикладывается к вершине. По сути, на кончик хвоста он будет влиять сильнее всего, а ближе к телу эффект будет ослабевать.

Для создания этого градиента мы используем нод Smooth Step.

Также нам потребуется новый параметр Tail Extra Swing для задания величины дополнительного движения.

Умножив этот новый параметр на выход нода Smooth Step, мы получим распределение движения вдоль хвоста. Затем мы прибавим его к параметру Jump Amplitude, чтобы получить окончательное движение тела, учитывающее дополнительное раскачивание хвоста.

Graph tail mask

Graph tail mask full

Теперь движение хвоста более заметно (Tail Extra Swing = 0.3).

Rat extra tail swing

Движение лап


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

  • Legs Amplitude величина перемещения лап относительно их позиций по умолчанию
  • Legs Frequency частота движения лап

Нам не нужен параметр Legs Speed, потому что движение лап должно быть синхронизировано с движением тела, поэтому мы снова используем параметр Jump Speed. Единственное, что здесь стоит учитывать: поскольку мы используем абсолютное значение синусоиды, за один цикл получается два прыжка. Поэтому чтобы компенсировать это, мы используем Jump Speed * 2.

Лапы должны двигаться вперёд и назад (и положительное, и отрицательное смещение), поэтому в этом случае нам не понадобится нод Absolute.

Graph legs sine wave

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

Мы снова используем нод Smooth Step, но на этот раз в качестве входного параметра выберем вертикальную ось UV. Давайте зададим градиент от 0,1 до 0,4.

Почему 0,1, а не 0,0? Чтобы избежать деформации лап. Все вершины ниже уровня 0,1 будут иметь одинаковое смещение.

Нам нужно настроить значение Legs Frequency таким образом, чтобы когда передние лапы идут вперёд, задние двигались назад, и наоборот. В моём случае я задал значение 10.

Graph legs mask

Давайте прибавим результат к локальной позиции вершин по Z. Я временно отключил движение тела, чтобы отдельно посмотреть на анимацию лап.

Graph isolated legs movement

Rat isolated legs movement

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

Rat slomo

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

Чтобы решить эту проблему, создадим новый параметр Legs Phase Offset, позволяющий нам компенсировать эту разность фаз и согласовать анимации.

Чтобы это смещение фаз работало, нужно добавить его в нод Time уже после умножения на Jump Speed (чтобы не изменять скорость), но перед всеми остальными манипуляциями.

Graph phase offset

После настройки значения (я указал -1,0) анимация стала правильной.

Rat with phase offset

Вот как это выглядит при нормальной скорости.

Rat final

Готовый граф:

Complete graph

Текстовая версия шейдера


Для тех, кто пока не перешёл на URP/HDRP или просто предпочитает писать шейдеры вручную, вот версия в коде:

Shader "Unlit/Rat"{    Properties    {        _JumpSpeed("Jump Speed", float) = 10        _JumpAmplitude("Jump Amplitude", float) = 0.18        _JumpFrequency("Jump Frequency", float) = 2        _JumpVerticalOffset("Jump Vertical Offset", float) = 0.33        _TailExtraSwing("Tail Extra Swing", float) = 0.15        _LegsAmplitude("Legs Amplitude", float) = 0.10        _LegsFrequency("Legs Frequency", float) = 10        _LegsPhaseOffset("Legs Phase Offset", float) = -1        [NoScaleOffset]        _MainTex ("Texture", 2D) = "white" {}    }    SubShader    {        Tags { "RenderType"="Opaque" }        LOD 100        Pass        {            CGPROGRAM            #pragma vertex vert            #pragma fragment frag            #include "UnityCG.cginc"            struct appdata            {                float4 vertex : POSITION;                float2 uv : TEXCOORD0;            };            struct v2f            {                float2 uv : TEXCOORD0;                float4 vertex : SV_POSITION;            };            sampler2D _MainTex;            float4 _MainTex_ST;            half _JumpSpeed;            half _JumpAmplitude;            half _JumpFrequency;            half _JumpVerticalOffset;            half _TailExtraSwing;            half _LegsAmplitude;            half _LegsFrequency;            half _LegsPhaseOffset;            v2f vert (appdata v)            {                float bodyPos = max((abs(sin(_Time.y * _JumpSpeed + v.uv.x * _JumpFrequency)) - _JumpVerticalOffset), 0);                float tailMask = smoothstep(0.6, 0.0, v.uv.x) * _TailExtraSwing + _JumpAmplitude;                bodyPos *= tailMask;                v.vertex.y += bodyPos;                float legsPos = sin(_Time.y * _JumpSpeed * 2 + _LegsPhaseOffset + v.uv.x * _LegsFrequency) * _LegsAmplitude;                float legsMask = smoothstep(0.4, 0.1, v.uv.y);                legsPos *= legsMask;                v.vertex.z += legsPos;                v2f o;                o.vertex = UnityObjectToClipPos(v.vertex);                o.uv = TRANSFORM_TEX(v.uv, _MainTex);                return o;            }            fixed4 frag (v2f i) : SV_Target            {                fixed4 col = tex2D(_MainTex, i.uv);                return col;            }            ENDCG        }    }}

Заключение


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

Такой способ подойдёт для фоновых объектов, не находящихся в основном фокусе внимания игрока. Кроме того, по сравнению с рендерерами мешей со скиннингом (Skinned Mesh Renderers), он обеспечивает более высокую производительность.

Надеюсь, он был для вас интересным и/или полезным.
Подробнее..

Перевод Анализ графики Red Dead Redemption 2

01.07.2020 10:24:00 | Автор: admin
Одна из моих любимейших игр, Red Dead Redemption, в 2018 году вернулась с приквелом для консолей. В 2019 году её выпустили для PC, и мне наконец удалось поиграть в неё; меня сразу же поразила её графика. Однако я расстроился: мне едва удавалось играть при средних настройках с 25 FPS на настольном GPU 1050Ti. Понимаю, машина у меня не очень мощная, но 25 FPS на средних настройках?

Сегодня мы рассмотрим несколько примеров кадров из игры и проанализируем использованные в игре графические приёмы.

Предисловие


Это неофициальный анализ игры. Я просто проанализировал захват кадров при помощи RenderDoc. Если вы хотите узнать информацию от самих разработчиков, то можете изучить слайды с доклада на SIGGRAPH Фабиана Байера. Слайды (внизу страницы), видео (начинается с 1:58:00).

Также можно прочитать анализ графики GTA5, выполненный Адрианом Корреже [перевод на Хабре]. Так как и RDR2, и GTA5 созданы одной компанией и используют один движок, часть приёмов из GTA5 присутствует и здесь.

Ещё один важный момент я не являюсь опытным программистом графики и по-прежнему новичок в этой области. Поэтому многое мне непонятно. Если вы найдёте ошибки или то, что можно улучшить, пишите мне. Ну, поехали!

Разбираем кадр


Вот наш основной кадр для анализа:


Кадр захвачен на PC со средними настройками.

В играх наподобие RDR2 почти невозможно исследовать все приёмы в одном кадре. Их работа переносится между несколькими кадрами. Поэтому я выполнил захват нескольких кадров, но показанный выше будет основным. Он содержит множество свойств, в том числе: прожекторные и точечные источники освещения, направленное освещение (его очень мало, но оно есть), здания, NPC, лошадь, деревья, растительность, облака и т.д. В нём можно показать большинство используемых в игре техник рендеринга.

RDR2 это игра с открытым миром, поэтому потоковая загрузка данных выполняется постоянно. Следовательно, построение кадра начинается с набора таких задач, как создание и удаление текстур, просмотры ресурсов шейдеров, просмотры с беспорядочным доступом, обновление дескрипторов, буферов и т.д.

Карта земли/грязи


Грязь играет большую роль. Кроме того, что она является игровой механикой, грязь делает окружения более реалистичными. Игра рендерит текстуры следов людей и лошадей в карту смещений (displacement map) вместе с текстурами следов колёс повозок. Эта аккумулированная текстура используется для Parallax Occlusion Mapping при рендеринге рельефа.

MudMap

Карта грязи: R16_UNORM размером 2048x2048

Небо и облака


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

Карта окружений


Карты окружений (Environment maps) основной источник отражений RDR2, а также GTA5.
Как и GTA5, RDR2 генерирует кубическую карту окружений из позиции камеры. Движок игры генерирует тонкий GBuffer для карты окружений, похожий на используемый в Far Cry 4.

EnvironmentMapAlbedo

Грани кубической карты окружений (альбедо): RGBA8_SRGB

EnvironmentMapNormal

Грани кубической карты окружений (нормали): RGBA8_UNORM

EnvironmentMapDepth

Грани кубической карты окружений (глубина): D32S8

Генерация кубических карт окружений в каждом кадре может оказаться очень тяжеловесной задачей. Для снижения вычислительных затрат RDR2 использует оптимизации. Например, игра отрисовывает только статичные и непрозрачные объекты, выполняет усечение по пирамиде видимости (frustum culling) перед рендерингом каждой грани и отрисовывает версии моделей с пониженным уровнем LOD. Однако я выяснил, что количество полигонов рельефа всё равно очень высок для карт окружений.

После прохода G-Buffer генерируется кубическая карта окружений неба при помощи параболоидной карты неба и текстур облаков. Следующим этапом является свёртка. Для Image Based Lighting движок RDR2 использует split sum approximation. В этом способе используется предварительно отфильтрованная карта окружений вместе с LUT BRDF окружений. Для фильтрации игра сворачивает кубическую карту окружений и хранит свёрнутые версии в уровнях mip-текстур кубической карты.

Перед выполнением прохода освещения для кубической карты освещения RDR2 рендерит в ещё одну кубическую текстуру запечённое крупномасштабное ambient occlusion. Игра использует screen space ambient occlusion, но SSAO помогает только при мелких масштабах. Запечённое ambient occlusion помогает выполнять затемнение в бОльших масштабах, например, затемнение террас и интерьеров.

EnvironmentMapBakedAO

Грани кубической карты окружений (запечённое AO): R8_UNORM

Для вычисления освещения карт окружений игра использует отложенный тайловый рендеринг. Усечение света (light culling) и освещение вычисляются вместе в одном проходе вычислительного шейдера для каждой грани карты окружений. (Благодарю за эту подсказку @benoitvimont.) Также для запечённого освещения игра использует технику карты освещения мира с видом сверху (top-down world lightmap) похожую на использованную в Assassin's Creed III.

Для каждой грани кубической карты RDR2 рендерит окончательный цвет поверх текстуры окружений неба. Затем игра фильтрует кубическую карту окружений так же, как и кубическую карту окружений неба.

EnvironmentMapFinal

Грани кубической карты окружений (окончательные): R11G11B10_FLOAT

Также когда игрок находится рядом со зданием, RDR2 загружает карты окружений, расположенные в интерьерах зданий. Существуют также G-Buffers кубических карт, потоково загружающиеся с диска.

BakedEnvironmentMapAlbedo

Запечённые грани кубической карты окружений (альбедо): BC3_SRGB (запечённое AO хранится в альфа-канале)

BakedEnvironmentMapNormal

Запечённые грани кубической карты окружений (нормали): BC3_UNORM

BakedEnvironmentMapDepth

Запечённые грани кубической карты окружений (глубина): R16_UNORM

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

Проход G-Buffer


Этот этап начинается с предварительного прохода глубин рельефа, а затем игра рендерит сцену в G-Buffers.

GBuffer 0 RGB GBuffer 0 A
AlbedoTarget
AlbedoTargetA


  • RGBA8_SRGB этот буфер содержит в каналах RGB альбедо (базовый цвет). Я не совсем понимаю, для чего нужны данные альфа-канала, но они используются на этапе сглаживания.


GBuffer 1 RGB GBuffer 1 A
NormalTarget
NormalTargetA

  • RGBA8_UNORM: каналы RGB содержат нормали, а альфа-канал содержит что-то, относящееся к ткани и волосам.


GBuffer 2 RGB GBuffer 2 A
MaterialTarget
MaterialTargetA

  • RGBA8_UNORM: этот target используется для свойств материалов.
    • R: Reflectance(f0)
    • G: Smoothness
    • B: Metallic
    • A: содержит затенение (этот канал будет использоваться как маска теней на последующих этапах)

GBuffer 3 R GBuffer 3 B
Material2TargetR
Material2TargetB

  • RGBA8_UNORM: красный канал содержит полости. В синем канале снова какие-то загадочные данные. А в альфа-канале находятся данные, относящиеся к волосам. В зелёном канале мне не удалось ничего найти.

GBuffer 4 RG
MotionBlurTarget

  • RG16_FLOAT: этот буфер содержит скорость в экранном пространстве для реализации motion blur.

GBuffer 5 Depth GBuffer 5 Stencil
DepthTarget
StencilTarget

  • D32S8: как и GTA5, RDR2 использует для глубины обратную z, а также стенсил-буфер для присвоения значений определённой группе мешей.

Из запечённых данных генерируется ещё один target:

GBuffer 6 R GBuffer 6 G
BakedAO
MysteryTarget

Этот буфер содержит в красном канале такое же запечённое ambient occlusion, что и на этапе карт окружений. Но в этой текстуре есть и другие каналы. Зелёные канал содержит данные, напоминающие данные в синем канале GBuffer 3. Я опять не понимаю, для чего используются эти данные. У захваченных кадров я не смог найти никаких данных в синем и альфа-канале. Я изучу это более подробно.

Генерация карт теней


После этапа G-Buffer игра начинает рендерить карты теней (shadow maps). Она использует 2D-массивы текстур для карт теней точечных источников и кубические массивы текстур для карт теней прожекторных источников.

В некоторых играх для карт теней используется большая текстура атласа теней (например, в DOOM). Одно из преимуществ такого метода заключается в том, что размер карты теней может сильно варьироваться в зависимости от расстояния. При использовании массивов текстур эта гибкость теряется, потому что все текстуры в массиве должны иметь одинаковый размер. В RDR2 есть три разных массива текстур для разного уровня качества. Например, у прожекторных источников есть:

  • 512x768 D16 для далёких источников
  • 1024x1536 D16 для источников на среднем расстоянии (а при средних настройках графики и на ближнем расстоянии)
  • 2048x3072 D16 для близких источников (при высоких/ультракачественных настройках)

Точечные источники отбрасывают тени во всех направлениях. Чтобы справиться с этой задачей, в играх используется техника под названием Omnidirectional Shadow Mapping, при которой сцена рендерится в кубическую карту глубин из позиции камеры. При помощи этой техники отрендерены тени от костра и тени от фонаря Артура. Как и прожекторные источники, точечные имеют три разных массива для разных настроек качества.

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

Большинство источников света на стенах прожекторные, но игра не генерирует для них всенаправленную карту теней (omnidirectional shadow map). Вместо неё она генерирует карту теней и копирует память этой карты теней в кубический массив карты теней точечных источников.


Слева карта теней прожекторных источников размером 1024x1536, справа те же данные изображения в кубическом формате текстуры 512x512

Обратите внимание, что в локальных картах теней хранятся линейные z.

Это объясняет то, почему для прожекторных источников разработчики не использовали карту теней квадратной формы. Количество пикселей в карте теней прожекторных источников и кубической карте точечных источников должно быть одинаковым. Вы наверняка увидели, что правое изображение нарезано на части. Так получилось, потому что ширина карты теней прожекторного и точечного источников отличаются.

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

Ещё один интересный момент заключается в том, что процесс выполняется наоборот. Возьмём для примера Сен-Дени один из крупнейших городов в игре. Игра генерирует omnidirectional shadow maps для прожекторных источников и копирует эти данные в массив карт теней прожекторных источников. Я не знаю, зачем в RDR2 наложение теней выполняется таким образом. В Интернете мне не удалось найти никаких подобных приёмов.

Наложение теней направленного освещения в RDR2 реализовано почти так же, как в GTA5. Это Cascaded Shadow Mapping с четырьмя каскадами. В качестве каскада используется каждый тайл размером 1024x1024 из текстуры 1024x4096 (при средних настройках графики).

ShadowCascades

Атлас теней направленного освещения: R16_UNORM

Этап освещения


Наконец настало время для объединения всех этих карт окружений, gbuffers, карт теней и буферов ao.

Этот этап состоит из двух проходов: первый для глобального освещения (солнца/луны), второй для локальных источников.

Проход глобального освещения


Игра рендерит полноэкранный четырёхугольник для вычисления направленного освещения, которым в нашем кадре является лунный свет. Также используется запечённое освещение из упомянутой выше карты освещения мира с видом сверху.

LightPass1

Проход локального освещения


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

Чтобы избежать необязательных вызовов шейдеров, игра использует тестирование с ограничением по глубине дополнительную функцию OpenGL/D3D11, которая стала нативной в Vulkan/D3D12. Также она использует стенсил-тестирования для отбрасывания пикселей, поглощённых просвечивающими объектами, например, стёклами окон. Эти объекты будут рендериться во время прямого прохода.

LightPass2

Рендеринг воды и отражения


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

  • Как говорилось выше, карты окружений это основной источник отражений. Для стандартных отражений. например, для отражений в окнах, игра использует их.
  • Зеркала рендерятся с планарными отражениями, при которых сцена рендерится заново с направления отражения. Этот процесс также выполняется отложенным рендерингом.
  • Отражения в воде используют отражения экранного пространства (screen space reflections) в сочетании с картой отражений, сгенерированной в начале кадра.

Этап прямого затенения


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

Но прямой проход может быть затратным:

  • В нём используются прямые шейдеры затенения, которые более затратны отложенных.

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

  • Они отрисовывают каждый толстый просвечивающий объект дважды.

Для достижения правильного смешения необходимо сначала отрендерить задние грани объекта, а затем отрендерить передние. Из-за этого большинство просвечивающих объектов в сцене отрисовывается дважды. Двухмерные объекты-четырёхугольники (например, окна) рендерятся один раз.

  • Кроме того, между отрисовками происходит изменение состояния конвейера.

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

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

BloomMask

Target яркости bloom: R8_UNORM

Заметьте, что яркость bloom увеличивается на дальних расстояниях, чтобы туманные области больше светились.

Постобработка


На этом этапе выполняются временнОе сглаживание, bloom, motion blur, эффект глубины видимости и другие эффекты. Постобработке я планирую посвятить отдельный пост. Поэтому мы не будем особо её здесь обсуждать, но я бы хотел сказать пару слов о bloom. Благодаря объёмному освещению в основном render target уже есть свечение источников освещения.

Реализация bloom в RDR2 очень похожа на реализацию, описанную в Next Generation Post Processing in Call of Duty: Advanced Warfare.

  • В качестве входных данных берётся target без пороговых значений,
  • Render target R11G11B10_FLOAT уровня 7-mip ,
  • Билинейный фильтр 13-го порядка при даунсэмплинге, tent-фильтр 3x3 при апскейлинге.

Затем игра комбинирует этот отфильтрованный target эффекта bloom с основным target, а также с target яркости bloom.

Выводы


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

  • Первое, что я заметил игра выполняет множество переключений с вычислений к графике и обратно. Она использует асинхронные вычисления, если их включить. (Их нельзя включить в игровых настройках, придётся изменять конфигурационный файл игры.) Вот пример для эффекта bloom: игра переключается на вычислительные шейдеры и выполняет какую-то работу, затем снова переключается на графику и выполняет даунсэмплинг, затем снова переключается на вычисления для выполнения какой-то другой работы, а потом заново переключается на графику для апсэмплинга. Затратны ли такие переключения для GPU? Оправдывает ли выгода от вычислительных шейдеров затраты на переключения?
  • Ещё одна странность заключается в том, что RDR2 очищает большинство текстур. Это странно, потому что обычно игры стараются избегать необязательную очистку текстур (например, очистку GBuffers). Оказывают ли эти очистки текстур реальное влияние на производительность? Обязательно ли очищать эти текстуры?
  • Третья странность: в одном кадре есть три (а может и больше) одинаковых прохода даунсэмплинга глубин. Один для SSAO, второй для SSR, и ещё один для этапа генерации объёмного тумана и столбов света. Почему игра не использует подвергнутый даунсэмплингу target глубин из SSAO для других этапов?

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

Послесловие


Вот и всё! RDR2 это превосходно выглядящая игра, не только благодаря использованным графическим приёмам, но и из-за арта и освещения; всё выглядит просто феноменально. Я влюбился в цветовую палитру этой игры. Особенно в ночное время суток, оно напоминает мне Как трусливый Роберт Форд убил Джесси Джеймса, Старикам тут не место и другие вестерны, снимавшиеся 35-миллиметровыми камерами.
Подробнее..

Перевод Скрываем воду внутри лодки

23.04.2021 10:10:48 | Автор: admin
При разработке игр про лодки, да и любых других игр с обширными водными поверхностями, существует проблема сокрытия поверхности воды, когда на ней что-то плавает. Я расскажу о решении, используемом в моей игре Sail Forth на движке Unity, но эта методика применима для любого другого движка.


Та самая проблема. Тащите ведро!

Так как в большинстве игр вода это просто большая плоскость, логично, что плавающие на ней объекты будут пересекаться с её поверхностью!

Как же нам устранить эту проблему? Мне известны две основные методики: одна основана на деформировании меша воды вокруг корпуса судна, вторая заключается в маскировании поверхности воды внутри судна. Я знаю, как использовать вторую методику, поэтому мы реализуем её.

Решение состоит из трёх компонентов:

  • Создание меша маски для каждого судна
  • Написание шейдера для меша маски
  • Изменение шейдера воды для использования маски

Меш маски


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


Меш маски воды

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

Шейдер маски


Теперь, когда у нас есть меш маски, нам нужен шейдер и материал, который будет скрывать поверхность воды там, где отрисовывается маска. Сначала мы реализуем это только с помощью буфера глубин.

Shader "Custom/WaterMask"{  SubShader   {    Pass     {      // Render the mask after regular geometry, but before masked geometry and      // transparent things.      // You may need to adjust the queue value depending on your setup      Tags {"RenderType"="Opaque" "Queue"="Geometry+10" "IgnoreProjector"="True" }      // Don't draw in the RGBA channels; just the depth buffer      // This is important for making our mask mesh invisible      ColorMask 0      // We write to the depth buffer which will hide the water below our mask mesh      ZWrite On      // We don't want anything to draw in front of our mask,       // as it would allow the water to then be drawn on top of us      ZTest Off    }  }}

Шейдер для меша маски воды

Пока таким будет весь шейдер для маски воды! Краткое описание: он выполняет рендеринг после всей непрозрачной (opaque) геометрии (например, лодки) и до воды, и не записывает никаких цветов, зато выполняет запись глубин. Последнее означает, что мы не сможем его увидеть, но он перекрывает объекты за собой. Он не перекрывает саму лодку, потому что отрисовывается после лодки.


Включение и отключение меша маски

Если мы применим этот шейдер к мешу маски, при условии, что очередь рендеринга воды находится после очереди рендеринга маски, то вы увидите, что это уже работает!

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

Это вполне может быть достаточным решением для вашей игры, как и для меня в течение первых нескольких лет разработки. Однако возникает одна проблема:


Ужасно

Что здесь происходит? Лодка качается на волнах, и иногда часть волн поднимается над верхней частью лодки. Это означает, что вода ближе к камере, чем маска, поэтому проходит Z-тест и рендерится.

Можно считать, что технически это правильно, ведь часть лодки в буквальном смысле находится под водой, поэтому вода и должна отображаться! Можно также сказать, что нужно исправить физику, чтобы лодка никогда не опускалась ниже поверхности воды, но это может быть слишком сложно настроить. К счастью, можно исправить шейдер, чтобы мы больше никогда не видели воду внутри лодки!

Стенсил-буфер


Если вы незнакомы со стенсил-буфером, то по сути его можно представить как ещё один экран, на который можно выполнять отрисовку, но который содержит не цвета, а числа. Шейдеры могут указывать значения, записываемые в стенсил-буфер, а также операцию сравнения, не позволяющую шейдеру выполнять отрисовку, если стенсил-буфер не соответствует выбранному значению. Он похож на вспомогательный буфер глубин, в который можно записывать любое значение по своему выбору.

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

Shader "Custom/WaterMask"{  SubShader   {    Pass     {      // Render the mask after regular geometry, but before masked geometry and      // transparent things.      // You may need to adjust the queue value depending on your setup      Tags {"RenderType"="Opaque" "Queue"="Geometry+10" "IgnoreProjector"="True" }      // Don't draw in the RGBA channels; just the depth buffer      // This is important for making our mask mesh invisible      ColorMask 0      // Writing to z depth isn't necessarily required, but might hide       // any extra effects your water has like caustics when the boat interior is below the water surface      ZWrite On      // We don't want anything to draw in front of our mask,       // as it would allow the water to then be drawn on top of us      ZTest Off      // The real meat of the solution.       // Ref - The value this stencil operation is in reference to. I arbitrarily picked '1'.      // Comp - The comparison method for deciding whether to draw a pixel.       //        For drawing the mask, we always want it to render regardless of the       //        stencil state, so I chose 'always'      // Pass - What to do with the stencil state after drawing a pixel. I chose 'replace',       //        which means that whatever was in the stencil buffer will be replaced with '1'       //        where our mask is drawn.      Stencil       {        Ref 1        Comp always        Pass replace      }    }  }}

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

Stencil это просто означает, что на данном проходе шейдера мы будем выполнять операцию со стенсилом

Ref 1 значение стенсила, на которое мы будем ссылаться, равно 1

Comp always когда мы смотрим на текущее значение стенсила, шейдер всегда должен отрисовывать пиксель вне зависимости от значения стенсила.

Pass replace при отрисовке пикселя мы должны заменять текущее значение стенсила нашим значением, то есть 1.

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

Теперь нам нужно использовать эту информацию стенсила в шейдере воды.

Маскирование в шейдере воды


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

Pass{  ZWrite On  // Mask the water using the stencil buffer  Stencil   {    Ref 1    Comp notequal    Pass keep  }  CGPROGRAM  #pragma vertex waterVert  #pragma fragment waterFrag  ENDCG}

Это очень похоже на код стенсила, который мы писали в шейдере маскировки, но немного с другими параметрами.

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

Поэтому мы делаем параметр Comp равным notequal, то есть сравнение будет выполняться на неравенство. Если значение стенсила не равно 1, то тест стенсила оказывается пройденным.

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


Вода, вода повсюду, но ни капли в нашей лодке!

После внесения этого изменения мы сможем увидеть стенсил-буфер в действии! Здесь я перемещаю лодку вверх и вниз, намного глубже поверхности воды, но мы видим, что вода никогда не отрисовывается поверх внутренней части лодки.

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

Ещё одна проблема, похожая на ситуацию с потоплением, рассмотрена в этой замечательной статье: https://simonschreibt.de/gat/black-flag-waterplane/. Высокая волна может встать между лодкой и камерой, которая в таком случае неправильно создаст стенсил-буфер, что приведёт к некрасивому артефакту. Конкретно в вашей игре такая ситуация может и не возникнуть, или наоборот, будет очень заметной.


Упс

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

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

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


Артефакт устранён
Подробнее..

Атомная бомба ударная волна и разрушение

21.05.2021 12:08:14 | Автор: admin

Сегодня исполняется 100 лет со дня рождения Андрея Сахарова, отца советского термоядерного оружия, одного из ведущих создателей самой мощной бомбы в истории человечества АН602. Благодаря труду этого ученого и многих других физиков, математиков и инженеров, СССР достиг ядерного паритета в Холодной Войне, и тем самым сделал бессмысленным её переход в горячую стадию.

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

Проблема окружающего мира

Атомный взрыв это нечто совершенно выдающееся, то, что должно потрясти маленький игровой мир до основания и затронуть все элементы окружения. Мир должен разделиться на до и после так, как оно происходило в реальной жизни.

Световая вспышка озаряет все пространство в радиусе десятков километров, будучи видимой сквозь облака и туман. По мере прохождения ударной волны облака из-за локального нагрева испаряются, а затем конденсируются заново. Во влажную погоду возможна конденсация облаков и куполов из воздуха, как это происходило во время испытаний Crossroads "Able" или РДС-3 .

Конденсационный купол во время испытания РДС-3Конденсационный купол во время испытания РДС-3

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

Воздействие ударной волны на зданиеВоздействие ударной волны на здание

Реализация этих воздействий хотя бы на визуальном уровне является сложной задачей со множеством аспектов, и по части внешнего вида, и по части производительности. Серьезной проблемой и отличием эффекта от остальных является его огромный размер: любая микродеталь по факту является и макродеталью тоже. Поэтому изменение алгоритма и его настройка для улучшения какого-то одного аспекта (например, воздействие волны в городской застройке) может привести к снижению качества в других ситуациях. А усложнение алгоритма за счет разных веток для разных ситуаций серьезно снижает производительность шейдеров.

Ударная волна

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

При виде сверху границы между волюметриком и декалью практически не видно. Зеленой стрелкой показана граница ударной волны, реализованной декальюПри виде сверху границы между волюметриком и декалью практически не видно. Зеленой стрелкой показана граница ударной волны, реализованной декалью

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

Красным обведен "честный" волюметрический эффект. Обведенное зеленым пространство занято декалью.Красным обведен "честный" волюметрический эффект. Обведенное зеленым пространство занято декалью.

Для того, чтобы поднятая с земли пыль лучше соответствовала ландшафту, в качестве диффузного цвета для нее используется самый грубый лод виртуальной текстуры земли, интерполированный в сторону серо-коричневого "пыльного" цвета. В большинстве случаев такой цвет получается адекватным.

Облака и конденсационные купола

Для отображения воздействия атомного взрыва на небо был написан вариант шейдера облаков, в котором функция плотности подвергается возмущению ударной волной. В некотором радиусе от эпицентра облака испаряются. Также, в этом шейдере было реализовано освещение облаков мощной световой вспышкой взрыва. Разумеется, подобная модификация шейдера заметно увеличила время его выполнения (в среднем в полтора раза), особенно для погоды с малым числом облаков (ведь проходящая ударная волна создает новые облака в пустом пространстве).

Ночной взрыв. Освещение облаков, конденсационный купол. Видно испарение облаков в сфере вокруг взрываНочной взрыв. Освещение облаков, конденсационный купол. Видно испарение облаков в сфере вокруг взрыва

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

Конденсационное облакоКонденсационное облако

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

Граница между частями облака. Сплошным цветом показан занятый конденсационным облаком объем.Граница между частями облака. Сплошным цветом показан занятый конденсационным облаком объем.

Само появление конденсационных куполов и их конфигурация случайны. Но вероятность их появления и длительность сильно зависит от влажности воздуха (являющейся одним из параметров погоды) и концентрации облаков на локации, поэтому во влажную и туманную погоду они будут появляться почти всегда.

Разрушение

Одним из самых трудносимулируемых эффектов атомного взрыва является разрушение объектов, попавших под ударную волну. Для ударной волны атомного взрыва характерны не только колоссальные размеры, но и высокий скоростной напор, швыряющий обломки в сторону от эпицентра.

Перед взрывом...Перед взрывом......и после...и после

Для игры со свободной камерой, вроде War Thunder, требуется показать разрушение всех объектов. Нельзя выбрать какой-то один ракурс или одну группу объектов, разрушение требуется всему на всех локациях. Ситуация усложняется тем, что большой процент ассетов вообще не имеет анимаций разрушения или разрушенного состояния. А даже если эти анимации имеются, то они реализованы на клиент-серверной архитектуре и выполняются на CPU, и одновременная анимация тысяч объектов мгновенно выберет весь ресурс производительности.

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

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

На текущий момент нет технической возможности реализовать поражающий эффект светового излучения в виде воспламенения и дымления поверхностей реализовано только освещение и обугливание. Поэтому на этом этапе разработки было принято решение увеличить поражающую мощь ударной волны для зрелищности эффекта. При фактической мощности взрыва 40 килотонн ударная волна соответствует взрыву в 100 кт.

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

Траектория каждого обломка рассчитывается, исходя из силы ударной волны на заданном расстоянии. В зависимости от высоты поражаемого объекта и силы волны рассчитывается линия среза. Чем выше объект и чем слабее волна, тем выше располагается линия среза. Для плотной городской застройки это имитирует гашение ударной волны у земли при сохранении поражающей силы на высоте. Но в общем случае это предположение о прочности неверно и требует серьезной доработки.

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

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

"Шахматный" паттерн, используемый для эффекта"Шахматный" паттерн, используемый для эффекта

Цена разрушения

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

Геометрические шейдеры в игровой индустрии практически не используются, так как запись вершинных данных и генерация новой геометрии занимают слишком много времени. Некоторые платформы (например, Mac) вообще не поддерживают геометрические шейдеры, предлагая вместо этого использовать компут.

К сожалению, это недоверие к геометрическим шейдерам полностью оправдано.

На 1060 GTX разрушение города с типового изометрического ракурса занимает 10 мс на кадр. Для наиболее детализированных локаций со множеством высокополигональных зданий разрушение занимает уже порядка 20 мс, что вкупе с рендером остального кадра снижает FPS до 30. Учитывая зрелищность процесса разрушения и тот факт, что он происходит в момент прекращения игрового процесса, такая частота кадров видится нормальной (к тому же, сам шейдерный процесс разрушения исключает фризы, поэтому FPS будет стабильным). Но видеокарта 1060 ввиду этого является предельной, поэтому анимация разрушения с обломками отображается только на пресетах максимум и кино. Для всех остальных пресетов генерируются только остовы зданий и других объектов, что на порядок дешевле, так как остовы не требуют геометрического прохода.

Альтернативой геометрическим шейдерам в этом является использование компут шейдеров для генерации вершинных буферов. Но, учитывая объем генерируемой геометрии, потребуется существенное выделение памяти, а также серьезная переделка конвейера рендера объектов при этом не факт, что при таком подходе будет достигнуто ускорение по сравнению с геометрическим шейдером.

Итоги

Полученный визуальный эффект может быть воспроизведен с любого ракурса для любой локации, погоды и времени суток, и для этого не требуется никакой настройки со стороны художников. Любая новая локация или новый ассет будут легко переварены алгоритмом, переиграны и уничтожены. И любой игрок с достаточно мощной видеокартой сможет сам записать подобный ролик:

Тем не менее, эффект проигрывает по зрелищности и деталям эффектам, которые целиком сконструированы и отрисованы для одной сцены. Универсальный алгоритм никогда не даст того, что дает индивидуальная настройка и корректировка.

Суммарно эффект использует примерно 1 мб видеопамяти, та как практически все его элементы генерируются в реальном времени. Это особенно важно в условиях дефицита видеопамяти на ряде платформ.

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

Как я перестал бояться и полюбил бомбу

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

За время разработки эффекта было проведено примерно 10000 атомных испытаний, в самых разных частях мира, включая несуществующие города и территории. Даже сказочный летающий архипелаг из TailSpin не избежал тяжкой поступи атомной эры.

Ну а настроение в ходе разработки было примерно таким:

Подробнее..

Категории

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

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru