Я делаю игру в ретро-стиле с 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 Этот код состоит из пары сотен строк, поэтому я не включил его сюда. По сути, результаты выглядели так, как будто каждая грань куба слегка выдвинута наружу (как каждая грань на сфере), с некоторым перекрытием между исходными гранями кубической карты. Это сработало, но это сложно сделать, и выходная текстура должна иметь гораздо большее разрешение, чтобы избежать неровных краев на моем пиксельном скайбоксе (что неудовлетворительно). Поэтому выполнение переназначения в моем обычном фрагментном шейдере кубической карты кажется идеальным.
@YakovGalka Меня устраивает некоторое искажение сферической перспективы, если я могу удалить неровные квадратные края.
@YakovGalka При дальнейшем рассмотрении кажется, что метод, который, как я думал, работает, работает не совсем так, как должен. Вот в основном шаги: 1. Сделайте единую подразделенную грань единичного куба. 2. Нарисуйте 6 его экземпляров, каждый повернутый экземпляр создаст единичный куб, а каждое положение нормализуется, чтобы сопоставить его с единичной сферой. 3. Используйте также создание экземпляров геометрических шейдеров, чтобы отрисовывать до 6 граней кубической карты. 4. Во фрагментном шейдере используйте положение на сфере для выборки из исходной кубической карты. Это не имеет значения для for (0, 0, 0), но для более низких y все работает нормально (за исключением углов).
@YakovGalka Я уже видел Google EAC, но мне так и не удалось его реализовать, так как я не смог найти правильное сопоставление куба со сферой (это то, что мне нужно).
@YakovGalka Да, я повозлюсь с этим и что-нибудь попробую. Я думаю, что сложная вещь заключается в том, чтобы, учитывая исходный луч кубической карты, найти 2d UV этого заданного лица, выполнить там переназначение через arctan, а затем снова получить из него луч кубической карты. Хм...
@YakovGalka Я пробовал; почти никакой разницы (я переназначил xy-компоненты gl_Position при использовании подробного метода в 4-этапном процессе, описанном выше). Смотрите здесь: desmos.com/calculator/n5lfdmtw5w, практически нет разницы для каждого переназначенного компонента.
@YakovGalka Обратите внимание, что я выполнял переназначение на уровне глаз (здесь куб и сфера выглядят совершенно одинаково; переназначение имело лишь небольшую разницу).
Кстати, если вы стоите на земле достаточно большой планеты, горизонт разделит вашу карту окружения ровно пополам. Вы, с другой стороны, нарисовали его слишком низко, что является одним из факторов, почему это выглядит странно.
@YakovGalka Ха, я думаю, что уровень моих глаз был на уровне y = 0
(y
для меня); Вы основываетесь только на том, как выглядит скриншот в моем посте?
Я основываюсь на вашей текстуре кубической карты.
@YakovGalka Ага - да, я немного сместил y вниз - определяемый некоторым процентом по направлению к верхней части коробки. Это было более полезно раньше по разным причинам, но не сейчас
Ваша проблема в том, что размер геометрии, на которой размещено окружение, слишком мал. Вы смотрите не на окружающую среду, а внутрь маленького куба, в котором сидите. Карта окружения должна вести себя так, как будто вы всегда находитесь в центре карты, а окружение бесконечно далеко. Предлагаю нарисовать карту окружения на дальней плоскости усеченного обзора. Вы можете сделать это, установив 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.
Да, я уже делаю это. Я забыл показать свой код вершинного шейдера! Куб остается статичным, когда я двигаюсь, как вы описываете. Однако это не решает проблему квадратности моей кубической карты.
Не уверен, что ясно дал понять — моя проблема в том, что я создаю кубические карты вручную и хочу применить к ним некоторое сопоставление, чтобы при рендеринге они выглядели как сфера (не куб!).
@CaspianAhlberg Да, я знаю, это именно то, что я ответил. См. также LearnOpenGL — Кубические карты. (Кстати, код в ответе проверен и работает). Вы хоть пробовали? Решение clipPos.xyww
Да, именно этот туториал я использовал для разработки кода кубической карты в своей игре. У меня есть именно этот код с .xyww
там; это не относится к моему вопросу. Перефразируя мой вопрос, я спрашиваю, как преобразовать равноугольную кубическую карту в равнопрямоугольную (поскольку те, которые я рисую, равноугольные). См. здесь blender.community/c/rightclickselect/3Sfbbc изображение, показывающее разницу; по сути, в кубических картах, которые я рисую, нет сферического искажения, и я хочу применить это к ним. Код .xyww
не имеет к этому никакого отношения.
Как вы можете заметить, искажения перспективы нигде нет; каждая грань полностью равноугольна.
Это предположение неверно. Вы нарисовали несколько изображений вручную; это не делает их равноугольными.
«Равноугольная кубическая карта» (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
- не уверен, что это проблема с вашей или моей стороны.
@CaspianAhlberg Я использую z-up, вы, вероятно, используете y-up.
действительно ли я не могу отобразить верхние/нижние заглавные буквы, используя мою текущую иллюстрацию? Я согласен с небольшим искажением - или, возможно, есть способ сохранить большую часть верхних / нижних заглавных букв, отбросив некоторые детали текстуры?
@CaspianAhlberg в миллионный раз нет. cubemap здесь не то, что вам нужно. Посмотрите мое редактирование с тем, что вы можете получить без кубической карты.
Похоже, вам нужна цилиндрическая проекция для четырех граней вдоль горизонта, но она конфликтует с верхней и нижней гранями. Так что я бы сказал, что никакая проекция не оставит вас удовлетворенными.