Unity нормаль к вектору

Математика в Gamedev по-простому. Векторы и интегралы

Всем привет! Сегодня хотелось бы поговорить о математике. Математика очень интересная наука и она может сильно пригодиться при разработке игр, да и в целом при работе с компьютерной графикой. Многие (особенно новички) просто не знают о том, как она применяется при разработке. Существует множество задач, не требующих глубокого понимания таких понятий как: интегралы, комплексные числа, группы, кольца и др, но благодаря математике вы можете решать многие интересные задачи. В этой статье мы рассмотрим векторы и интегралы. Если интересно, добро пожаловать под кат. Иллюстрирующий Unity проект, как всегда, прилагается.

Unity нормаль к вектору

Векторная математика.

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

Векторная арифметика и полезные функции

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

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

Гифки делались с помощью Unity, так что нужно было бы реализовывать класс, отвечающий за отрисовку стрелочек. Стрелка вектора состоит из трех основных компонент – линии, наконечника и текста с именем вектора. Для отрисовки линии и наконечника я воспользовался LineRenderer. Посмотрим на класс самого вектора:

Так как мы хотим, чтобы вектор был определённой длинны и точно соответствовал точкам, которые мы задаём, то длинна линии рассчитывается по формуле:

В данной формуле (_VectorEnd — _VectorStart).normalized – это направление вектора. Это можно понять из анимации с разницей векторов, приняв что _VectorEnd и _VectorStart – это вектора с началом в (0,0,0).

Дальше разберём две оставшиеся базовые операции:

Нахождение нормали (перпендикуляра) и середины вектора – это очень часто встречающиеся задачи при разработке игр. Разберём их на примере размещения подписи над вектором.

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

Вот мы и получили нормаль к отрезку.

normal = normal.y > 0? normal: -normal; — эта операция отвечает за то, чтобы текст всегда показывался над вектором.

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

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

Но это было про 2D, а что же с 3D?

В 3D плюс-минус всё тоже самое. Отличается только формула нормали, так как нормаль уже берётся не к отрезку, а к плоскости.

В данном примере контролла нормаль к плоскости используется, чтобы сместить конечную точку траектории право, чтобы планету не загораживал интерфейс. Нормаль в 3д графике – это нормализованное векторное произведение двух векторов. Что удобно, в Юнити есть обе эти операции и мы получаем красивую компактную запись:

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

Вообще у интегралов очень много применений, таких как: физические симуляции, VFX, аналитика и многое другое. Я не готов сейчас детально описывать все. Хочется описать простой и визуально понятный. Поговорим про физику.

Допустим есть задача – двигать объект в определённую точку. К примеру, чтобы при вхождении в определённый триггер, должны вылетать книги с полок. Если вы хотите двигать равномерно и без физики, то задача тривиальна и не требует интегралов, но когда книги выталкивает с полки призрак, такое распределение скорости будет смотреться совсем не так.

Что такое интеграл?

По сути это площадь под кривой. Но что это означает в контексте физики? Допустим у вас есть распределение скорости по времени. В данном случае площадь под кривой – это путь который пройдёт объект, а это как раз то, что нам и нужно.

Unity нормаль к вектору

Если перейти от теории к практике, то в Unity есть замечательный инструмент под названием AnimationCurve. С помощью него можно задать распределение скорости с течением времени. Создадим вот такой класс.

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

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

Видео:Unity/C# Векторы, модуль, направлениеСкачать

Unity/C# Векторы, модуль, направление

Идеальные карты нормалей для Unity (и других программ)

… и как разобраться, что не так с вашим конвейером создания контента.

Unity нормаль к вектору

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

Unity нормаль к вектору

Узрите величие этой традиционной модели для тестирования карт нормалей!

Затем художник помещает её в игру, и она выглядит… немного не так.

Unity нормаль к вектору

Разве эти грани не должны быть плоскими?

Поверхности, которые должны быть плоскими, всё равно немного скруглены, некоторые даже вдавлены, и хуже того, по ним идут странные полосы. Знакомая ситуация? Некоторые из вас вероятно с ней сталкивались, думали: «ну ладно, наверно, лучше уже не получится», и двигались дальше. Возможно, накладывали немного грязи, чтобы замаскировать самые тяжёлые случаи.

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

*Если считать, что в некоторых случаях вы можете вносить изменения в конвейер контента.

Видео:Unity C#. Vector3 Normalize. Справочник. Мусин Михаил.Скачать

Unity C#. Vector3 Normalize. Справочник. Мусин Михаил.

Предисловие

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

Видео:Векторы и Манипуляции с ними, Vector3 - Unity урокиСкачать

Векторы и Манипуляции с ними, Vector3 - Unity уроки

Базис касательного пространства

Проблема заключается в том, как работают карты нормалей касательного пространства. В частности, как работает касательное пространство (или касательный базис). Я не буду слишком вдаваться в технические аспекты того, что они делают. Я, как и другие авторы, уже рассказывал об этом, но если вкратце, то касательное пространство определяет, что должна представлять ориентация направления в карте нормали. Какое направление на карте нормалей на самом деле является «вперёд», «вверх» или «вправо». Грубо говоря, эта ориентация является ориентацией UV-координат текстуры и нормалей вершин.

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

Видео:Unity C#. Vector3 OrthoNormalize. Справочник. Мусин Михаил.Скачать

Unity C#. Vector3 OrthoNormalize. Справочник. Мусин Михаил.

Познаём своего врага

Видео:Unity C#. Vector3 ProjectOnPlane. Справочник. Мусин Михаил.Скачать

Unity C#. Vector3 ProjectOnPlane. Справочник. Мусин Михаил.

Переворот из-за очевидной ошибки

Самая распространённая ошибка — проблема «OpenGL против Direct3D». Также они называются картами нормалей Y+ и Y-. Я включил их сюда просто для полноты обзора. Вероятнее всего, большинство людей, сталкивавшихся с этой проблемой, уже знают о ней, или, по крайней мере, научились решать её. Так как карты нормалей сгенерированы в одной ориентации, они по сути бесполезны для использования в приложении, рассчитанном на использование другой.

Unity нормаль к вектору

Карты нормалей в ориентации Direct3D в движке Unity, который ожидает ориентации OpenGL

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

И с этой проблемой всё достаточно понятно. Большинство приложений, запекающих карты нормалей, имеют красивую большую кнопку или меню для переключения между OpenGL / Direct3D или параметр ориентации Y. Узнайте, какую ориентацию использует целевое приложение и запекайте карты в ней. Unity использует OpenGL и Y+. Unreal использует Direct3D и Y-. Это чётко можно понять, взглянув на саму текстуру карты нормалей. Если кажется, что в зелёном канале они освещены «сверху», то это ориентация OpenGL. Если снизу, то это Direct3D.

Видео:Вектора в Unity. Простое объяснение за 20 минут! Часть (1/2)Скачать

Вектора в Unity. Простое объяснение за 20 минут! Часть (1/2)

Странности с sRGB

Ещё одна распространённая ошибка — использование текстуры, помеченной как sRGB-текстура. Это изменяет способ сэмплирования текстуры GPU, заставляя его преобразовывать цветовые значения из пространства sRGB, из которого люди обычно выбирают значения цветов, в линейное цветовое пространство. Для карт нормалей этого делать не нужно. Если в Unity задать для текстуры тип «Normal map», то движок автоматически отключит эту опцию, но можно также отключить опцию «sRGB (Color Texture)» вручную, и текстура заработает как карта нормалей. По крайней мере, в Unity 2017.1 и более новых версиях.

Unity нормаль к вектору

Карта нормалей с типом текстуры Default и включенным sRGB

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

Видео:Как ПРАВИЛЬНО сделать передвижение в Unity? Нормали поверхностей + Чистый код на C#Скачать

Как ПРАВИЛЬНО сделать передвижение в Unity? Нормали поверхностей + Чистый код на C#

Слишком иной, чтобы быть нормальным

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

Unity нормаль к вектору

Карты нормалей, запечённые на сглаженном меше, отображаемые как на меше с резкими рёбрами

Unity нормаль к вектору

Карты нормалей, запечённые на меше с резкими рёбрами, отображаемые как на сглаженном меше

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

Видео:Unity C#. Vector3 Project. Справочник. Мусин Михаил.Скачать

Unity C#. Vector3 Project. Справочник. Мусин Михаил.

MikkTSpace против Autodesk

Менее заметная проблема — при запекании карт нормалей 3ds Max или Maya, в движке Unity они всегда будут неправильными. Они будут неправильными и в Unreal, да и практически в любой другой программе, за исключением той, в которой запекались. Карты нормалей 3ds Max не работают в Maya, и наоборот! Каждое из этих приложений вычисляет касательное пространство собственным уникальным способом, не соответствующим другим приложениям. При самостоятельном использовании они выглядят идеально, и если вы генерируете карты нормалей для использования в этих программах, то старайтесь запекать карты нормалей в них. Почти никакая другая программа не способна запекать правильные карты нормалей для этих приложений, поэтому у вас как бы нет выбора.

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

Unity нормаль к вектору

3ds Max запёк карту нормалей, используемую в Unity

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

Многие приложения, не принадлежащие Autodesk, перешли к способу вычисления касательного пространства, который называется MikkTSpace. И xNormal, и Substance, а также Blender, Unity, Unreal, Godot, Marmoset, Houdini, Modo и некоторые другие, по умолчанию используют MikkTSpace или, по крайней мере, каким-то образом поддерживают его, что сильно повышает их совместимость. По сути, за последний десяток лет он фактически стал отраслевым стандартом во всех случаях, когда используются карты нормалей касательного пространства. Предположительно, на каком-то этапе 3ds Max добавит их поддержку, но непонятно, планируют ли разработчики позволить запекать карты нормалей при помощи MikkTSpace или просто обеспечат поддержку просмотра/рендеринга с их помощью.

Видео:Скалярное умножение векторов (Dot Product) в UnityСкачать

Скалярное умножение векторов (Dot Product) в Unity

Экспорт мешей и Substance Painter

Ещё одна проблема с 3ds Max и Maya заключается в том, что при экспорте мешей из них есть опция экспорта с «касательными и бинормалями» (tangents and binormals). Они не будут находиться в настоящих касательных MikkTSpace, но будут основаны на них. Некоторые приложения по умолчанию принудительно пересчитывают правильное пространство MikkTSpace (например, xNormal, Unity и Unreal), но другие приложения используют касательные меша (при их наличии) «как они есть» и вычисляют MikkTSpace, только если меш не имеет касательных. Например, Substance Painter использует касательные меша при их наличии так, как они есть, то есть Painter создаёт карты нормалей, очень похожие на запечённые 3ds Max! Похожие, но не совсем такие, поэтому если импортировать их обратно в 3ds Max, то они не будут выглядеть так же хорошо, как запечённые в Max.

Видео:Unity 18 C# Урок 5 - Векторы и Манипуляции с ними, Vector3Скачать

Unity 18 C# Урок 5 - Векторы и Манипуляции с ними, Vector3

MikkTSpace против MikkTSpace?

Следующая проблема заключается в том, что существует не одна версия MikkTSpace! Да, это не был бы стандарт, если бы каким-то образом не вносил путаницу. MikkTSpace определяет и как данные должны храниться в меше для каждой вершины, и как данные используются при рендеринге. Все приложения с поддержкой MikkTSpace вычисляют одинаковые данные меша для вершин. Но существует два разных способа использования этих данных при рендеринге. MikkTSpace определяет, как вычислить вектор касательной и знак для каждой вершины на основании ориентации UV. Но при использовании касательного пространства касательная к двум точкам (bitangent) пересчитывается или для каждой вершины, или для каждого пикселя. Этот параметр тоже должен быть одинаковым и для запекания, и для просмотра.

xNormal и Substance имеют опцию для запекания карт нормалей при помощи обоих способов. Эти опции называются «compute tangent space per fragment» в Substance и «compute binormal in the pixel shader» в xNormal. Не нужно использовать её, если выполняете запекание для встроенных способов рендеринга Unity. Однако, возможно стоит делать это для SRP, в зависимости от выбранного типа!

Unity нормаль к вектору

«Pixel shader binormals» приложения xNormal со встроенным упреждающим рендерингом (forward renderer) Unity

Визуально это похоже на использование совершенно иного касательного пространства, но обычно гораздо менее выражено. Некоторые грани могут выглядеть так, как будто нормаль немного искажена, а не будут иметь большую вмятину. Решение здесь очень просто — не выбирайте эту опцию, если вы запекаете для встроенных способов рендеринга Unity. Ставьте флажок, если запекаете для HDRP движков Unreal или Unity.

Во многих источниках говорится, что Blender и Unity идентичны, но на самом деле это никогда не является правдой! Blender, как и Unity, использует MikkTSpace и ориентацию OpenGL, но, как и Unreal, использует бинормали для каждого фрагмента (fragment binomals).

Хорошая новость заключается в том, что сейчас шейдеры LWRP, URP и Shader Graph используют бинормали для каждой вершины, а встроенные шейдеры HDRP — для каждого пикселя. В будущем обновлении Shader Graph (7.2.0) будет выполнен переход на попиксельное вычисление, а спустя какое-то время на него перейдут и встроенные шейдеры URP. А стандартные способы рендеринга можно изменять при помощи собственных шейдеров, если вы выберете такой маршрут.

Видео:Физика в Unity - 9. RaycastСкачать

Физика в Unity - 9. Raycast

Триангулируй, триангулируй, триангулируй!

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

Unity нормаль к вектору

Запечённые в Substance Painter карты нормалей, перенесённые в Unity на меше, экспортированном из 3ds Max как четырёхугольники (quads)

Это проявляется очень похоже на совершенно неправильное касательное пространство, как будто карты нормалей были запечены в 3ds Max. Но в общем случае они немного резче, почти сгибаются, а не выглядят как плавная вогнутость. В этом меше видна X-образная форма на внешних гранях. Эта проблема может оказаться сложной и раздражающей, когда в конвейере производства контента с моделью работает несколько человек в разных приложениях, потому что часто бывает полезно не триангулировать меш и сохранять красивые полигоны. В таком случае приходится или смириться с неудобствами, или сделать так, чтобы последний инструмент в конвейере перед экспортом модели в игру также использовался для генерации меша, применяемого для запекания нормалей. Как и касательное пространство с нормалями вершин (которые являются частью касательного пространства), триангуляция тоже должна точно совпадать.

Видео:Unity C#. Vector3 Reflect. Справочник. Мусин Михаил.Скачать

Unity C#. Vector3 Reflect. Справочник. Мусин Михаил.

Список для создания идеальных карт нормалей для Unity

Итак, вот советы по генерации карт нормалей касательного пространства для lowpoly-мешей из исходных highpoly-моделей для использования в Unity:

  • Не нужно запекать карты нормалей с помощью 3ds Max, Maya или любого другого приложения, не имеющего опции касательных MikkTSpace.
  • Экспортируйте меши как треугольники, с нормалями, и без касательных.
  • Выбирайте OpenGL или X+Y+Z+ в качестве ориентации карты нормалей при запекании (а также при создании проекта для Substance Painter).
  • Не нужновключать опции касательных к двум точкам «per pixel» / «per fragment» при запекании для встроенных способов рендеринга (упреждающего или отложенного) или LWRP.
  • Включайте опции касательных к двум точкам «per pixel» / «per fragment» при запекании для HDRP, а скоро и для URP.
  • Оставьте параметры импорта модели для Unity по умолчанию, с нормалями, установленными на «Import», а касательными — на «Calculate Mikktspace».

Вот и всё! У вас каждый раз будут получаться идеально запечённые карты нормалей!

Unity нормаль к вектору

Ура! Идеальная карта нормалей!

Unity нормаль к вектору

… С отражениями? Кхм..

Ну, последнее, мы просто проигнорируем. Настолько «идеальная», насколько можно получить со сжатыми форматами текстур с 8 битами на канал. Карта нормалей с дизерингом может выглядеть немного лучше, чем полосы, но, к сожалению, эту опцию предоставляет не так много инструментов. В противном случае можно использовать 16-битные карты нормалей. Но это уже тема для другой статьи.

Видео:Вектор нормали к поверхности поля в точкеСкачать

Вектор нормали к поверхности поля в точке

Подробнее о MikkTSpace

На полуофициальном сайте mikktspace.com хорошо объясняются основные преимущества MikkTSpace. В частности, это пространство хорошо работает и для пересчёта, и для переноса данных касательных между программами с разной обработкой мешей, сохраняя при этом целостность результатов. Текст сайта — это копия уже несуществующей страницы вики Blender, написанной самим Мортеном С. Миккельсеном. К сожалению, в нём упоминается только версия с касательными к двум точкам для каждого пикселя, но не версия для каждой вершины, по умолчанию используемая Unity и xNormal. Так как в Blender используются производные от попиксельных касательных, логично, что упомянуты только они.

Вот комментарии об этих двух опциях из оригинального файла mikktspace.h:

Для карт нормалей достаточно использовать следующую упрощённую версию касательной к двум точкам, генерируемой на уровне пикселей/вершин.

bitangent = fSign * cross(vN, tangent);

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

Вероятно, наиболее эффективным преобразованием, которое мы можем выполнить в пиксельном шейдере, достигается прямым использованием «разнормализованной» интерполированной касательной, касательной к двум точкам и нормали вершины: vT, vB и vN.

пиксельный шейдер (быстрое преобразование обратно)

vNout = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN );

где vNt — это нормаль касательного пространства. Сэмплер карты нормалей должен аналогично использовать интерполированную и «разнормализованную» касательную, касательную к двум точкам и нормаль вершины, чтобы соответствовать пиксельному шейдеру.

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

Итак, в исходном примере касательная к двум точкам воссоздаётся в вершинном шейдере и передаётся в пиксельный шейдер как ещё одно интерполированное значение. Именно так в настоящее время обрабатывают карты нормалей встроенные шейдеры Unity. Пример с выполнением этой задачи в пиксельном шейдере — это скорее экспромт, а не «основной» пример. Кроме того, поскольку реализация для Unity 5.3 (и xNormal?) была создана самим Мортеном С. Миккельсеном, и в обоих используются касательные для каждой вершины, это является ещё одним доказательством нашей теории. Но тогда в Blender использовались попиксельные касательные, и Мортен тоже имел к этому отношение. Вероятно, на самом деле это была просто опция, вызвавшая небольшие изменения в готовых шейдерах Unity, и в то время для графики реального времени ни один из способов не считался намного лучше другого.

Преимущество попиксельной реализации заключается в устранении двух интерполированных значений ценой небольших лишних затрат АЛУ (математики пиксельного шейдера). Это значит, что вместо трёх значений Vector3 (касательной, касательной к двум точкам и нормали) нам нужны только Vector4 и Vector3 (касательная со знаком и нормаль). На современных GPU это немного быстрее. И это определённо быстрее при выполнении в ЦП, так как это можно использовать для рендереров не в реальном времени. С этой точки зрения логично, что бОльшая часть индустрии решила воссоздавать касательную к двум точкам в пикселе, а не в вершине.

Забавно, что когда я писал эту статью, Мортен продвигал переход в Unity SRP на попиксельные касательные к двум точкам! Это просто неудачное стечение обстоятельств, что у встроенных способов рендеринга Unity осталось наследие неудобного выбора.

Видео:КОМПОНЕНТ TRANSFORM В UNITY, ЗАЧЕМ НУЖЕН?Скачать

КОМПОНЕНТ TRANSFORM В UNITY, ЗАЧЕМ НУЖЕН?

Касательное пространство в разных приложениях

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

Видео:C# Интерфейсы простым языком с примерами в Unity! Уроки по C# (ч.8)Скачать

C# Интерфейсы простым языком с примерами в Unity! Уроки по C# (ч.8)

Unity (5.3+)

Ориентация: OpenGL Y+
Касательное пространство: MikkTSpace
Вычисление касательных к двум точкам для MikkTSpace: для вершин или для пикселей*

Примечания: по умолчанию движок принудительно переопределяет касательные меша при импорте, чтобы использовать MikkTSpace. Для опытных пользователей он имеет опции для использования касательных меша, но это не гарантирует совместимости с картами нормалей, запечёнными в других инструментах, особенно при использовании встроенных шейдеров. Могут быть заменены на полностью собственные шейдеры. А для своих встроенных шейдеров HDRP использует попиксельные касательные к двум точкам. На момент написания статьи было намечено, что шейдеры URP и Shader Graph тоже перейдут на попиксельные касательные к двум точкам. До Unity 5.3 движок использовал касательные Lengyel, которые по-прежнему можно использовать, выбрав в импортере мешей опцию Legacy Tangents, но встроенные шейдеры больше не поддерживают это касательное пространство.

Дополнение: до URP 7.2.0 обработка карт нормалей в Shader Graph была совершенно поломана! В ней не используется правильно ни MikkTSpace, ни даже касательное пространство Lengyel, и исправить это внутри Shader Graph невозможно. По возможности обновитесь до 7.2.0.

Видео:Unity - Лучший курс | С Нуля до Junior за ОДНО видеоСкачать

Unity - Лучший курс | С Нуля до Junior за ОДНО видео

Unreal Engine 4

Ориентация: Direct3D Y-
Касательное пространство: MikkTSpace
Вычисление касательных к двум точкам для MikkTSpace: попиксельное
Примечания: по умолчанию движок при импорте переопределяет касательные меша, используя пространство MikkTSpace. Для опытных пользователей он имеет опции использования касательных меша, но это не гарантирует совместимости с картами нормалей, запечёнными в других инструментах.

Видео:Вектора в Unity на практике. Просто объяснение за 20 минут! Часть (2/2)Скачать

Вектора в Unity на практике. Просто объяснение за 20 минут! Часть (2/2)

Godot

Ориентация: OpenGL Y+
Касательное пространство: MikkTSpace
Вычисление касательных к двум точкам для MikkTSpace: для каждой вершины
Примечания: если данных о касательных не существует, может при импорте вычислять касательные меша в MikkTSpace. Документация движка предлагает экспортировать меши с данными о касательных, но поскольку большинство инструментов не экспортирует правильные касательные мешей, я бы игнорировал этот совет. Даже если вы используете исходные касательные меша, то нет гарантии, что карты нормалей будут выглядеть правильно.

Видео:Физика в Unity - 8. OnCollisionEnter, OnTriggerEnterСкачать

Физика в Unity - 8. OnCollisionEnter, OnTriggerEnter

PlayCanvas

Ориентация: OpenGL Y+
Касательное пространство: Lengyel/Schüler*
Вычисление касательных к двум точкам: для каждой вершины/на основе производных
Примечания: предоставляет несколько способов обработки касательного пространства. Сгенерированные кодом касательные не используют MikkTSpace. Имеет опции использования касательной к двум точкам для каждой вершины, которые или нормализуют векторы касательного пространства в пиксельном шейдере «fastTbn», не нормализуя касательное пространство во фрагментном шейдере (что нам нужно, поскольку это соответствует реализации MikkTSpace с вычислением для каждой вершины), или используют основанную на производных TBN, позаимствованную у Кристиана Шулера; она полностью игнорирует касательные меша и вычисляет касательное пространство в пиксельном шейдере. Сейчас по умолчанию используются карты нормалей на основе производных, для которых, насколько я знаю, нет инструментов запекания точных карт нормалей. Возможно, можно импортировать меши с существующими нормалями и данными касательных MikkTSpace, а затем использовать опцию материала «fastTbn», чтобы это соответствовало встроенным способам рендеринга Unity, но я считаю, что такое должно делаться в коде, а не через интерфейс редактора.

Видео:Shader graph в Unity! Проще чем кажется! Шейдер графСкачать

Shader graph в Unity! Проще чем кажется! Шейдер граф

Filament

Ориентация: OpenGL Y+
Касательное пространство: MikkTSpace
Вычисление касательных к двум точкам для MikkTSpace: для каждой вершины
Примечания: в зависимости от производительности в будущем движок может перейти к попиксельным касательным к двум точкам.

Видео:Unity/C# Вектора: скалярное произведение и векторное произведение векторовСкачать

Unity/C# Вектора: скалярное произведение и векторное произведение векторов

3ds Max

Ориентация: Direct3D Y-*
Касательное пространство: собственное
Опции экспорта мешей: экспортируйте файлы FBX со включенным «Triangulate», но с отключенным «Tangents and Binormals». Нормали нужно экспортировать всегда. «Split per-vertex Normals» можно оставить отключенным.
Примечания: по умолчанию 3ds Max предполагает, что в картах нормалей используется ориентация Direct3D, но имеет опции для переворота ориентации X и Y при использовании текстур карт нормалей на материалах. Запекание всегда использует ориентацию Direct3D. Поддержка MikkTSpace появится в будущих версиях.

Ориентация: OpenGL Y+
Касательное пространство: собственное
Опции экспорта мешей: экспортируйте файлы FBX со включенным «Triangulate», но с отключенным «Tangents and Binormals». Нормали экспортируются всегда. «Split per-vertex Normals» можно оставить отключенным.
Примечания: некоторые люди говорили мне, что Maya в частично уже поддерживает MikkTSpace, но я не знаю, в какой степени.

Blender (2.57+)

Ориентация: OpenGL Y+
Касательное пространство: MikkTSpace
Вычисление касательных к двум точкам для MikkTSpace: для каждого пикселя
Опции экспорта мешей: экспортируйте файлы FBX со значением Smoothing «Normals Only». Можно экспортировать с «Tangent Space» или без него, если целевое приложение тоже использует MikkTSpace, например, как Unity или Unreal.
Примечания: в Blender нет опции триангуляции при экспорте в FBX, поэтому предварительно используйте модификатор Triangulate. Запечённые в Blender карты нормалей не будут на 100% совместимыми с Unity, но если перевернуть зелёный канал в стороннем приложении или в настроенном материале, то они идеально работают в Unreal.

xNormal (3.17.5+)

Ориентация: настраиваемая
Касательное пространство: MikkTSpace*
Вычисление касательных к двум точкам для MikkTSpace: настраиваемое (для каждой вершины или пикселя)
Опции запекания карт нормалей: имеет опции переворота всех трёх осей карты нормалей при экспорте. Для Unity нужны X+ Y+ Z+. Также есть опция использования касательных к двум точкам для каждой вершины или пикселя (то есть фрагмента). По умолчанию xNormal использует касательные к двум точкам для каждой вершины, но это можно изменить в меню Plugins Manager (значок вилки питания в левом нижнем углу), нажав на «Tangent basis calculator», «Mikk — TSpace» и щёлкнув на Configure. При этом появится всплывающее окно с единственной опцией «Compute binormal in the pixel shader». Она должна быть выключена для Unity и включена для Unreal.
Примечания: хотя xNormal имеет поддержку только MikkTSpace, её система плагинов теоретически поддерживает любое касательное пространство. Пока единственный знакомый мне плагин касательного пространства предназначен для запекания касательного пространства Unity 4 и уже бесполезен. То есть если подходить реалистично, приложение поддерживает только MikkTSpace.

Substance Painter

Ориентация: настраиваемая (OpenGL или Direct3D)
Касательное пространство: MikkTSpace
Вычисление касательных к двум точкам для MikkTSpace: настраиваемое (для вершин или для пикселей)
Опции запекания карт нормалей: имеет опции запекания в ориентации OpenGL или Direct3D. И опцию для использования касательных к двум точкам для каждой вершины или пикселя (фрагмента), что настраивается параметром «Compute tangent space per fragment». При экспорте для встроенных способов рендеринга Unity нужно выбрать OpenGL и отключить Compute tangent space per fragment в проекте и параметрах экспорта, а при экспорте в Unreal или Unity HDRP сделать обратное.
Примечания: Substance Painter по умолчанию использует те данные касательных, которые использует импортированный меш. Если только вы не выполняете экспорт из Blender, вам это не нужно! Но для отключения этой функции нет никаких опций. Перед импортом в Substance Painter экспортируйте меши без касательных. Ранее в документации Substance ошибочно утверждалось, что вычисления касательных к двум точкам
в Blender соответствуют Unity, но теперь ошибку исправили!

Marmoset Toolbag 3

Ориентация: настраиваемая
Касательное пространство: настраиваемое
Вычисление касательных к двум точкам для MikkTSpace: попиксельное
Примечания: похоже, что программа имеет опции для просмотра или запекания карт нормалей практически в любой существующей конфигурации. В том числе и готовые настройки для касательных пространств 3ds Max и Maya! Однако опция касательного пространства «Mikk / xNormal» использует попиксельные касательные к двум точкам, и это невозможно изменить. То есть Marmoset Toolbag 3 не может правильно просматривать или запекать карты нормалей для встроенных способов рендеринга Unity. Возможно в будущих версиях ситуация изменится, потому что разработчики знают о проблеме.

3DCoat

Ориентация: настраиваемая
Касательное пространство: настраиваемое
Вычисление касательных к двум точкам для MikkTSpace: ?
Примечания: имеет множество опций для просмотра и запекания карт нормалей. Совершенно точно способна соответствовать Blender и UE4, поэтому поддерживает попиксельные касательные к двум точкам, но я не знаю, используются ли в пресете Unity вершинные касательные к двум точкам.

Houdini (16.0.514+)

Ориентация: настраиваемая
Касательное пространство: настраиваемое*
Вычисление касательных к двум точкам для MikkTSpace: попиксельное
Примечания: поддерживает свой собственный базис касательных или MikkTSpace, как для просмотра, так и для запекания, но только в попиксельной версии. Похоже, по умолчанию не использует MikkTSpace для рендеринга, но начиная с Houdini 17.5. меши можно модифицировать встроенными нодами для использования касательных MikkTSpace.

Modo (10+?)

Ориентация: настраиваемая
Касательное пространство: настраиваемое
Вычисление касательных к двум точкам для MikkTSpace: настраиваемое (вершинное или попиксельное)*
Примечания: похоже, что поддерживает рендеринг и запекания в различных касательных пространствах, а также опции попиксельных касательных к двум точкам для экспорта. Я не тестировал это.

Knald

Ориентация: настраиваемая
Касательное пространство: MikkTSpace
Вычисление касательных к двум точкам для MikkTSpace: попиксельное
Примечания: по умолчанию использует MikkTSpace и переопределяет касательные импортированных мешей, но при необходимости есть опция использования существующих касательных меша. Ориентацию карт нормалей можно переворачивать параметром, применяемым ко всему проекту.

В заключение

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

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

Почему Unity OnCollisionEnter не дает мне поверхностные нормали, и какой самый надежный способ их получить?

Событие Unity по столкновению дает вам объект Collision, который дает вам некоторую информацию о произошедшем столкновении (включая список ContactPoints с нормалью попадания).

Но то, что вы не получаете, это поверхностные нормали для коллайдера, который вы ударили. Вот скриншот для иллюстрации. Красная линия от ContactPoint.normal и синяя линия от RaycastHit.normal .

Unity нормаль к вектору

Является ли это примером того, как Unity скрывает информацию для предоставления упрощенного API? Или стандартные трехмерные методы обнаружения столкновений в реальном времени просто не собирают эту информацию?

И для второй части вопроса, каков верный и относительно эффективный способ получить нормальную поверхность для столкновения?

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

Мой текущий метод:

Резервное копирование Collision.contacts[0].point вдоль его удара нормально

Raycast вниз отрицательный удар нормальный для float.MaxValue , на Collision.collider

Если это не удается, повторите шаги 1 и 2 с неотрицательным нормальным

Если это не помогло, попробуйте шаги 1–3 с Collision.contacts[1]

Повторите 4 до успешного или пока все точки контакта не исчерпаны.

Сдавайся, возвращайся Vector3.zero .

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

РЕДАКТИРОВАТЬ Если это действительно так, как обстоят дела с 3D-столкновением, обзор того, почему в общем случае будет так же приветствоваться, как что-то специфическое для Unity.

Это действительно так, как обстоят дела со столкновением. Не только 3D, но и 2D. Возьмите следующий пример:

Unity нормаль к вектору

Зеленые и красные AABB сталкиваются, а контактный коллектор — синяя область. Точки соприкосновения будут где-то в синей области (именно там, где может меняться алгоритм, но углы, где встречаются синий / красный / зеленый, идеальны).

Какая нормальная поверхность должна быть возвращена? Верхний край красного AABB или левый край? Если зеленый прямоугольник падает, возможно, мы можем разумно угадать верхний край. если он движется вправо, может быть, мы сможем угадать левый край. Что, если он двигался вниз и вправо? Берем ли мы ось наименьшего проникновения? Ось скорости наибольшей скорости? Некоторая эвристика обоих? Что если бы ящики столкнулись именно по углам?

Расширьте это до сложной трехмерной поверхности, потенциально состоящей из сотен трис / граней. У вас все еще будет небольшое количество контактных точек с идеальными контактами. Какая нормальная поверхность должна быть возвращена? Средняя поверхность, нормальная по всей три-сетке (что не имеет смысла для большинства объектов)? Точки непосредственно «под» углами сталкивающегося прямоугольника (которые не очень хорошо определены для большинства других фигур)? Пытаетесь ли вы найти ближайшую грань к сгенерированным контактным точкам (что потребует второго прохода, поскольку контактные точки не рассчитываются напрямую из каких-либо граней сетки)? Если вы находите ближайшее лицо, берете ли вы нормаль лица или интерполируете вершины лица в точке контакта, чтобы получить правильную нормаль для «гладких» объектов?

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

Конечно, с более конкретными ограничениями и ограничениями на ваши объекты, мир и движение вы можете создавать альтернативные алгоритмы столкновения, которые могут рассказать вам о нормальной поверхности. В приведенном выше 2D-случае, если мы предположим, что ящики никогда не вращаются и что мы знаем относительную скорость и последнюю позицию каждого из них, можно было бы использовать непрерывное обнаружение столкновений, чтобы точно определить, когда они сталкиваются и какие объекты столкнулись, предоставляя нам точный объект, где произошло столкновение, которое затем может быть использовано как нормальный контакт / столкновение / поверхность. Платформерные игры построены исключительно на таких предположениях и специальных хитростях (вот почему использование общей библиотеки физики, такой как Box2D или Havok, или свет никогда не дает такого жесткого, точного управления, как у классических платформеров, таких как Mario или Sonic; я бы не стал хочу сказать это

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

Поделиться или сохранить к себе: