Безшовне відображення плиткових карт (суміжні зображення без полів)


18

У мене є 2D-ігровий двигун, який малює кавові карти, малюючи плитки із зображення набору плиток. Оскільки за замовчуванням OpenGL може обгортати лише всю текстуру ( GL_REPEAT), а не лише її частину, кожна плитка розбивається на окрему текстуру. Потім області однієї плитки виводяться поруч один з одним. Ось як це виглядає, коли працює за призначенням:

Каркасна карта без швів

Однак як тільки ви вводите дробове масштабування, з'являються шви:

Картина зі швами

Чому це відбувається? Я подумав, що це пов'язано з лінійною фільтрацією, яка змішує межі квадратиків, але це все одно відбувається при точковій фільтрації. Єдине рішення, яке я знайшов поки що, - це забезпечити, щоб усі позиціонування та масштабування відбувалося лише за цілими значеннями, і використовувати точну фільтрацію. Це може погіршити візуальну якість гри (особливо, якщо розміщення підпікселів більше не працює, тому рух не такий плавний).

Те, що я спробував / вважав:

  • антиалізинг зменшує, але не повністю усуває шви
  • вимкнення міповідтворення не впливає
  • візуалізуйте кожну плитку окремо і видавіть краї на 1px - але це деоптимізація, оскільки вона не може більше відтворювати області плиток за один раз, і створює інші артефакти по краях областей прозорості
  • додайте 1px рамку навколо вихідних зображень та повторіть останні пікселі - але вони вже не є потужністю двох, викликаючи проблеми сумісності із системами без підтримки NPOT
  • написання користувацького шейдера для обробки кахельних зображень - але що б ви зробили інакше? GL_REPEATмає захоплювати піксель з протилежного боку зображення на межах, а не вибирати прозорість.
  • геометрія точно суміжна, помилок округлення з плаваючою точкою немає.
  • якщо шейдер фрагмента важко закодований, щоб повернути той самий колір, шви зникають .
  • якщо текстури встановлені GL_CLAMPзамість GL_REPEAT, шви зникають (хоча візуалізація неправильна).
  • якщо текстури встановлені GL_MIRRORED_REPEAT, шви зникають (хоча рендерінг знову неправильний).
  • якщо я роблю фон червоним, шви все ще білі. Це дозволяє зробити вибірку непрозорого білого кудись, а не прозорість.

Тож шви з’являються лише тоді, коли GL_REPEATвстановлено. Чомусь лише в цьому режимі, на краях геометрії спостерігається деяка обдувка / протікання / прозорість. Як це може бути? Вся текстура непрозора.


Ви можете спробувати встановити пробник текстури для затискання або налаштувати колір межі, оскільки я підозрюю, що це може бути пов'язано з цим. Крім того, GL_Repeat просто загортає координати УФ, тому я особисто трохи розгублений, коли ти кажеш, що він може повторити лише всю текстуру, а не частину її.
Еван

1
Можливо, ти якось вводиш тріщини у свою сітку? Ви можете перевірити це, встановивши шейдер, щоб він просто повертав суцільний колір, а не відбирав текстуру. Якщо ви все ще бачите тріщини в цій точці, вони знаходяться в геометрії. Факт, що антиаліазія зменшує шви, говорить про це, можливо, це є проблемою, оскільки антиаліазінг не повинен впливати на вибірку текстури.
Натан Рід

Ви можете реалізувати повторення окремих плиток у текстурному атласі. Потрібно зробити додаткову математичну координацію текстури та вставити межі текстилю навколо кожної плитки. Ця стаття пояснює багато цього (хоча здебільшого з акцентом на набридливій конвенції координат текстури D3D9). Крім того, якщо ваша реалізація є достатньо новою, ви можете використовувати текстури масиву для карти плитки; якщо кожна плитка має однакові розміри, це буде чудово і не потребуватиме додаткової математики координат.
Андон М. Коулман

3D текстури з GL_NEARESTвибіркою в Rкоординатному напрямку також працюють так само добре, як і масиви текстур для більшості речей цього сценарію. Mipmapping не спрацює, але, судячи з вашої програми, вам, мабуть, не потрібні карти.
Андон М. Коулман

1
Яким чином відображення "неправильне", якщо встановлено значення CLAMP?
Мартійн Курто

Відповіді:


1

Шви - це правильна поведінка для відбору проб GL_REPEAT. Розглянемо наступну плитку:

бордюрна плитка

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

масштабована плитка

антиалізинг зменшує, але не повністю усуває шви

MSAA працює, беручи більше зразків по краях полігону. Зразки, взяті ліворуч або праворуч від краю, будуть "зеленими" (знову ж таки, враховуючи лівий край першої картинки), і лише один зразок буде "на" краю, частково відбираючи вибірку з "бежевої" області. При змішуванні зразків ефект зменшиться.

Можливі рішення:

У будь-якому випадку слід переключитися на GL_CLAMPзапобігання кровотечі з пікселя з протилежного боку текстури. Тоді у вас є три варіанти:

  1. Увімкніть антизгладжування, щоб трохи згладити перехід між плитками.

  2. Встановіть для фільтрування текстури значення GL_NEAREST. Це дає всім пікселям жорсткі краї, тому краї полігонів / спрайтів стають невідрізними, але це, очевидно, досить сильно змінює стиль гри.

  3. Додайте вже обговорювану межу 1px, просто переконайтесь, що рамка має колір сусідньої плитки (а не колір протилежного краю).

    • Це також може бути вдалий час для переходу на текстурний атлас (просто зробіть його більшим, якщо вас турбує підтримка NPOT).
    • Це єдине "ідеальне" рішення, що дозволяє GL_LINEARфільтрувати подібну форму по краях багатокутників / спрайтів.

О, дякую - обгортання текстури це пояснює. Я розгляну ці рішення!
AshleysBrain

6

Оскільки за замовчуванням OpenGL може обгортати лише всю текстуру (GL_REPEAT), а не лише її частину, кожна плитка розбивається на окрему текстуру. Потім області однієї плитки виводяться поруч один з одним.

Розглянемо відображення єдиного, звичайного текстурованого квадрата в OpenGL. Чи є шви в будь-якому масштабі? Ні ніколи. Ваша мета полягає в тому, щоб усі ваші сцена сцени були щільно упаковані на одну текстуру, а потім надішліть її на екран. EDIT Для подальшого уточнення: Якщо у вас є окремі обмежувальні вершини на кожному плитці, у більшості випадків у вас будуть , здавалося б, невиправдані шви. Це так, як працює апаратне забезпечення GPU ... Помилки з плаваючою комою створюють ці прогалини на основі поточної точки зору ... помилок, яких можна уникнути в уніфікованому колекторі, оскільки якщо два грані суміжні в одному колекторі(субмеш), апаратне забезпечення відобразить їх без швів, гарантовано. Ви бачили це в незліченних іграх і програмах. У вас повинна бути одна щільно упакована текстура на одному підмеші без подвоєних вершин, щоб уникнути цього раз і назавжди. Це питання, яке виникає раз і знову на цьому веб-сайті та в інших місцях: якщо ви не з’єднаєте кутові вершини вашої плитки (кожні 4 плитки поділяють кутову вершину), очікуйте, що шви.

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

Щоб вирішити: візуалізуйте (при 1: 1) всі ваші текстури плитки у FBO / RBO без пропусків, а потім надішліть цей FBO до типового фреймбуфера (екран). Оскільки сам ФБО в основному є однією текстурою, ви не можете закінчити прогалини в масштабуванні. Всі текстові межі, які не потрапляють на межу пікселя екрана, будуть змішані, якщо ви використовуєте GL_LINEAR .... що саме ви хочете. Це стандартний підхід.

Це також відкриває ряд різних шляхів масштабування:

  • масштабування розміру квадрата, на який ви будете надавати FBO
  • зміна УФ-променів на цьому квадратику
  • співпадає з налаштуваннями камери

Я не думаю, що це буде добре для нас. Це здається, що ви пропонуєте, що при 10-кратному масштабі ми зробимо площу в 10 разів більше. Це не забезпечує користь для пристроїв з обмеженою швидкістю заповнення. Також я все ще загадую, чому конкретно GL_REPEAT викликає лише шви, а жоден інший режим не робить. Як це могло бути?
AshleysBrain

@AshleysBrain Не послідовно. Я загадкований вашим твердженням. Поясніть, будь ласка, як збільшення масштабу на 10% збільшує частоту заповнення на 10 разів? Або як збільшує масштаб на 10 разів - оскільки відсікання у вікні перегляду запобіжить більш ніж 100% швидкості заповнення за один прохід? Я пропоную вам показати саме те, що ви вже маєте намір - ні більше, ні менше - таким, що, ймовірно, є більш ефективним І безпроблемним способом, об'єднавши свій кінцевий результат, використовуючи звичайну техніку RTT. Обладнання буде працювати з відсіканням, масштабуванням, змішуванням та інтерполяцією практично у будь-яку ціну, враховуючи, що це лише 2D двигун.
Інженер

@AshleysBrain Я відредагував своє питання, щоб зробити проблеми з вашим поточним підходом чітко зрозумілими.
Інженер

@ArcaneEngineer Привіт, я спробував ваш підхід, який ідеально вирішує проблему швів. Однак зараз замість швів у мене є помилки пікселів. Ви можете мати уявлення про проблему, про яку я зазначаю тут . Чи маєте ви якесь уявлення, що може бути причиною цього? Зауважте, що я все роблю в WebGL.
Немиколх

1
@ArcaneEngineer Я поставлю нове запитання, пояснюючи, що це занадто громіздко. Мені шкода роздратування.
Немиколх

1

Якщо зображення мають виглядати як одна поверхня, надайте їх як одну поверхневу текстуру (масштаб 1x) на одній 3D-сітці / площині. Якщо ви візуалізуєте кожен як окремий 3D-об’єкт, він завжди матиме шви через помилки округлення.

Відповідь @ Nick-Wiggill правильна, я думаю, ви неправильно зрозуміли це.


0

2 можливості, які, можливо, варто спробувати:

  1. Використовуйте GL_NEARESTзамість GL_LINEARфільтрування текстур. (Як вказував Андон М. Коулман)

    GL_LINEAR(фактично) розмиває зображення настільки незначно (інтерполяція між найближчими пікселями), що дозволяє плавно переходити в кольорі від одного пікселя до іншого. Це може допомогти зробити текстури краще виглядати у багатьох випадках, оскільки це заважає вашим текстурам не мати цих блокуючих пікселів по всьому. Це також робить так, що трохи коричневого кольору від одного пікселя однієї плитки може бути розмитим до сусіднього зеленого пікселя сусідньої плитки.

    GL_NEARESTпросто знаходить найближчий піксель і використовує цей колір. Інтерполяції немає. Як результат, це насправді трохи швидше.

  2. Трохи зменшіть координати текстури для кожної плитки.

    Начебто додавання додаткового пікселя навколо кожної плитки як буфера, за винятком того, що замість розширення плитки на зображенні ви зменшуєте плитку в програмному забезпеченні. 14x14px плитки під час візуалізації замість плиток 16x16px.

    Можливо, вам навіть вдасться піти з плитками розміром 14,5х14,5, не надто багато коричневого кольору.

--edit--

Візуалізація пояснення у варіанті №2

Як показано на зображенні вище, ви все одно можете легко використовувати потужність двох текстур. Таким чином, ви можете підтримувати обладнання, яке не підтримує текстури NPOT. Те, що змінюється, - це координати текстури, замість того, щоб переходити (0.0f, 0.0f)до пункту « (1.0f, 1.0f)Ви б переходити звідти (0.03125f, 0.03125f)до» (0.96875f, 0.96875f).

Це поміщає ваші текстові координати трохи всередину плитки, що зменшує ефективну роздільну здатність в грі (хоча не на апаратному рівні, щоб у вас все-таки були текстури потужності двох), однак має бути такий же ефект відтворення, як і розширення текстури.


Я вже згадував, що спробував GL_NEAREST (він же фільтр точок), і він не виправляє. Я не можу зробити №2, оскільки мені потрібно підтримувати NPOT-пристрої.
AshleysBrain

Зробив редагування, яке може прояснити деякі речі. (Я припускаю, що "потреба в підтримці пристроїв NPOT [(без потужності двох]]" мала означати щось, що потрібно для того, щоб текстури були тривалістю 2?)
Вольфганг Скайлер

0

Ось що я думаю, що це може статися: Там, де дві плитки стикаються, х компонент їх країв повинен бути рівним. Якщо це неправда, вони можуть округнутись у зворотному напрямку. Тому переконайтеся, що вони мають точно однакове значення x. Як ви забезпечите це? Ви можете спробувати, скажімо, помножити на 10, округлити, а потім розділити на 10 (зробіть це лише на процесорі, перш ніж ваші вершини перейдуть у вершину шейдера). Це має дати правильні результати. Крім того, не малюйте плитку за плиткою, використовуючи матриці перетворення, а помістіть їх у пакетне VBO, щоб переконатися, що неправильні результати не надходять від втраченої системи плаваючої крапки IEEE у поєднанні з множенням матриць.

Чому я пропоную це? Тому що, з мого досвіду, якщо вершини мають точно такі ж координати, коли вони виходять з вершинного шейдера, заповнення відповідних трикутників створить безперебійні результати. Майте на увазі, що щось, що є математично правильним, може бути трохи відключено через IEEE. Чим більше обчислень ви будете робити на своїх цифрах, тим менш точним буде результат. І так, матричне множення вимагає певних операцій, які можуть бути виконані лише умноженням та доповненням при створенні вашого VBO, що дасть більш точні результати.

Проблема також може полягати в тому, що ви використовуєте лист аркуша (також його називають атласом) і що при вибірці текстури пікселі сусідньої текстури плитки вибираються. Щоб цього не сталося - це створити крихітні рамки у УФ-картах. Отже, якщо у вас плитка розміром 64х64, ваше УФ-зіставлення має охоплювати трохи менше. Скільки? Я думаю, що я використовував у своїх іграх по 1 чверті пікселя на кожній стороні прямокутника. Тому компенсуйте ультрафіолетові випромінювання на 1 / (4 * ширинаOfTheAtlas) для x компонентів та 1 / (4 * висотаOfTheAtlas) для y компонентів.


Дякую, але, як я вже зазначав, геометрія, безумовно, суміжна - насправді всі координати призводять до точних цілих чисел, тому це легко сказати. Тут не використовуються атласи.
AshleysBrain

0

Щоб вирішити це в 3D-просторі, я змішав масиви текстур із пропозицією Вольфганга Скайлера. Я поставив 8-піксельну межу навколо моєї реальної текстури для кожної плитки (120х120, 128х128), яка для кожної сторони, яку я могла заповнити "загорнутим" зображенням або стороною, щойно розширеною. Пробовідбірник зчитує цю область під час інтерполяції зображення.

Тепер, використовуючи фільтрацію та карту, пробовідбірник все ще може легко прочитати повну межу 8 пікселів. Щоб вирішити цю невелику проблему (я кажу невелику, оскільки це відбувається лише на кілька пікселів, коли геометрія справді перекошена або далеко), я розділив плитки на текстурний масив, тому кожна плитка має власний простір текстури і може використовувати затиск на краї.

У вашому випадку (2D / плоский), я б, звичайно, пішов із наданням піксельної ідеальної сцени, а потім масштабуванням потрібної частини результату у вікно перегляду, як запропонував Нік Віггіл.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.