Преобразование равноугольной кубической карты в равнопрямоугольную

Я делаю игру в ретро-стиле с OpenGL и хочу нарисовать для нее свои кубические карты. Вот пример одного из них:

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

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

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

#version 400 core

in vec3 cube_edge;

out vec3 color;

uniform samplerCube skybox_sampler;

void main(void) {
    color = texture(skybox_sampler, cube_edge).rgb;
}

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

vec3 sphere_edge = vec3(cube_edge.x, normalize(cube_edge).y, cube_edge.z);

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

Я также пробовал результаты с этого сайта: http://mathproofs.blogspot.com/2005/07/mapping-cube-to-sphere.html, но грани были недостаточно изогнуты наружу.

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

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

Yakov Galka 16.02.2023 01:03

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

Yakov Galka 16.02.2023 01:10

@Yakov Galka Этот код состоит из пары сотен строк, поэтому я не включил его сюда. По сути, результаты выглядели так, как будто каждая грань куба слегка выдвинута наружу (как каждая грань на сфере), с некоторым перекрытием между исходными гранями кубической карты. Это сработало, но это сложно сделать, и выходная текстура должна иметь гораздо большее разрешение, чтобы избежать неровных краев на моем пиксельном скайбоксе (что неудовлетворительно). Поэтому выполнение переназначения в моем обычном фрагментном шейдере кубической карты кажется идеальным.

Caspian Ahlberg 16.02.2023 01:24

@YakovGalka Меня устраивает некоторое искажение сферической перспективы, если я могу удалить неровные квадратные края.

Caspian Ahlberg 16.02.2023 01:26

@YakovGalka При дальнейшем рассмотрении кажется, что метод, который, как я думал, работает, работает не совсем так, как должен. Вот в основном шаги: 1. Сделайте единую подразделенную грань единичного куба. 2. Нарисуйте 6 его экземпляров, каждый повернутый экземпляр создаст единичный куб, а каждое положение нормализуется, чтобы сопоставить его с единичной сферой. 3. Используйте также создание экземпляров геометрических шейдеров, чтобы отрисовывать до 6 граней кубической карты. 4. Во фрагментном шейдере используйте положение на сфере для выборки из исходной кубической карты. Это не имеет значения для for (0, 0, 0), но для более низких y все работает нормально (за исключением углов).

Caspian Ahlberg 18.02.2023 22:51

@YakovGalka Я уже видел Google EAC, но мне так и не удалось его реализовать, так как я не смог найти правильное сопоставление куба со сферой (это то, что мне нужно).

Caspian Ahlberg 18.02.2023 22:52

@YakovGalka Да, я повозлюсь с этим и что-нибудь попробую. Я думаю, что сложная вещь заключается в том, чтобы, учитывая исходный луч кубической карты, найти 2d UV этого заданного лица, выполнить там переназначение через arctan, а затем снова получить из него луч кубической карты. Хм...

Caspian Ahlberg 19.02.2023 02:24

@YakovGalka Я пробовал; почти никакой разницы (я переназначил xy-компоненты gl_Position при использовании подробного метода в 4-этапном процессе, описанном выше). Смотрите здесь: desmos.com/calculator/n5lfdmtw5w, практически нет разницы для каждого переназначенного компонента.

Caspian Ahlberg 19.02.2023 03:33

@YakovGalka Обратите внимание, что я выполнял переназначение на уровне глаз (здесь куб и сфера выглядят совершенно одинаково; переназначение имело лишь небольшую разницу).

Caspian Ahlberg 19.02.2023 03:35

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

Yakov Galka 19.02.2023 18:35

@YakovGalka Ха, я думаю, что уровень моих глаз был на уровне y = 0 (y для меня); Вы основываетесь только на том, как выглядит скриншот в моем посте?

Caspian Ahlberg 19.02.2023 21:44

Я основываюсь на вашей текстуре кубической карты.

Yakov Galka 19.02.2023 23:14

@YakovGalka Ага - да, я немного сместил y вниз - определяемый некоторым процентом по направлению к верхней части коробки. Это было более полезно раньше по разным причинам, но не сейчас

Caspian Ahlberg 20.02.2023 00:38
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
13
96
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ваша проблема в том, что размер геометрии, на которой размещено окружение, слишком мал. Вы смотрите не на окружающую среду, а внутрь маленького куба, в котором сидите. Карта окружения должна вести себя так, как будто вы всегда находитесь в центре карты, а окружение бесконечно далеко. Предлагаю нарисовать карту окружения на дальней плоскости усеченного обзора. Вы можете сделать это, установив z-компонент позиции отсечения равным w-компоненту в вершинном шейдере. Если вы установите z на w, вы гарантируете, что окончательное z значение позиции будет равно 1,0. Это значение z дальней плоскости. (Вы можете сделать это с помощью Swizzlinggl_Position = clipPos.xyww). Вполне достаточно нарисовать куб и обернуть окружение, отыскав карту с интерполированными вершинами куба. В случае samplerCube трехмерная координата текстуры рассматривается как вектор направления. Вы можете использовать координату вершины куба для поиска текстуры.

Вершинный шейдер:

cube_edge = inVertex.xyz;

vec4 clipPos = projection * view * vec4(inVertex.xyz, 1.0);
gl_Position = clipPos.xyww;

Фрагментный шейдер:

color = texture(skybox_sampler, cube_edge).rgb;

Решение также подробно описано на странице LearnOpenGL — Cubemap.

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

Caspian Ahlberg 18.02.2023 22:43

Не уверен, что ясно дал понять — моя проблема в том, что я создаю кубические карты вручную и хочу применить к ним некоторое сопоставление, чтобы при рендеринге они выглядели как сфера (не куб!).

Caspian Ahlberg 18.02.2023 22:55

@CaspianAhlberg Да, я знаю, это именно то, что я ответил. См. также LearnOpenGL — Кубические карты. (Кстати, код в ответе проверен и работает). Вы хоть пробовали? Решение clipPos.xyww

Rabbid76 18.02.2023 22:58

Да, именно этот туториал я использовал для разработки кода кубической карты в своей игре. У меня есть именно этот код с .xyww там; это не относится к моему вопросу. Перефразируя мой вопрос, я спрашиваю, как преобразовать равноугольную кубическую карту в равнопрямоугольную (поскольку те, которые я рисую, равноугольные). См. здесь blender.community/c/rightclickselect/3Sfbbc изображение, показывающее разницу; по сути, в кубических картах, которые я рисую, нет сферического искажения, и я хочу применить это к ним. Код .xyww не имеет к этому никакого отношения.

Caspian Ahlberg 19.02.2023 02:05
Ответ принят как подходящий

Как вы можете заметить, искажения перспективы нигде нет; каждая грань полностью равноугольна.

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

«Равноугольная кубическая карта» (EAC) конкретно означает кубическую карту, переназначенную по этой формуле (раздел 2.4):

u = 4/pi * atan(u)
v = 4/pi * atan(v)

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

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

d /= max(abs(d.x), max(abs(d.y), abs(d.z));
d = atan(d)/atan(1);

дает следующий результат:

Сравните это с неисправленным d:

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

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

d /= length(d.xy);
d.xy /= max(abs(d.x), abs(d.y));
d.xy = atan(d.xy)/atan(1);

И это дает следующий результат:

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

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

Например, используя следующую 2D-текстуру:

И следующая формула:

float tau = 6.283185;
float u = atan(d.y, d.x);
float v = atan(d.z, length(d.xy));
OUT = texture(TEX, vec2(u/tau, 2*v/tau + 0.5));

Я могу получить следующие результаты:

Я думаю, что это даст вам больше, чем любые настройки, основанные на кубических картах.

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

Caspian Ahlberg 19.02.2023 06:29

@CaspianAhlberg Я использую z-up, вы, вероятно, используете y-up.

Yakov Galka 19.02.2023 07:07

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

Caspian Ahlberg 20.02.2023 23:37

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

Yakov Galka 21.02.2023 02:50

Другие вопросы по теме