Як уникнути кровоточивості текстури в атласі текстури?


46

У моїй грі є місцевість, схожа на Minecraft, зроблену з кубів. Я генерую вершинний буфер з даних вокселів і використовую атлас текстури для вигляду різних блоків:

текстурний атлас для місцевості на основі вокселів

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

скріншот місцевості з смугами неправильного кольору

Наразі я використовую ці параметри інтерполяції, але я намагався будь-яку комбінацію і навіть GL_NEARESTбез міповідтворення не дає кращих результатів.

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

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

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


4
Проблема полягає в міфографічному відображенні - ваші координати текстури можуть працювати добре для найбільшого рівня мип, але, коли все відходить далі, окантовка плиток поєднується між собою. Ви можете боротися з цим, не використовуючи mipmaps (мабуть, це не гарна ідея), збільшуючи ваші охоронні зони (область між плитками), динамічно збільшуючи охоронні зони в шейдері на основі рівня mip (хитро), робити щось дивне під час створення mipmaps (може Я нічого не думаю про це) .. Хоча я ніколи не вирішував цю проблему, але все, з чого слід почати .. =)
Jari Komppa

Як я вже говорив, на жаль, вимкнення карти карти не вирішує проблеми. Без mipmaps він або буде інтерполювати лінійний, GL_LINEARщо дає аналогічний результат, або вибере найближчий піксель, GL_NEARESTщо так само призводить до смуг між блоками. Остання згадана опція зменшується, але не усуває недолік пікселя.
danijar

7
Одним з варіантів є використання формату, який генерує mipmaps, тоді ви можете створити Thees mipmaps власноруч і виправити помилку. Ще одне рішення - примусити певний рівень міпмапи у вашому фрагментограді, коли ви відбираєте текстуру. Іншим рішенням є заповнення mipmaps першою mipmap. Іншими словами, коли ви створюєте свою текстуру, ви можете просто додати підміри до конкретного зображення, що містять ті самі дані.
Тордін

1
У мене була ця проблема раніше, і вона була вирішена шляхом додавання 1-2 пікселів накладки у ваш атлас між кожною різною текстурою.
Чак Д

1
@Kylotan. Проблема полягає в зміні розміру текстури незалежно від того, чи це зроблено за допомогою mipmap, лінійної інтерполяції або найближчого вибору пікселів.
danijar

Відповіді:


34

Гаразд, я думаю, у вас тут виникають дві проблеми.

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

Як правило, для 2D, де ви зазвичай займаєтеся атласіровкою, ви не міпмапу; тоді як для 3D, куди ви робите міпмапу, ви не атласуєте (насправді у вас шкіра таким чином, що кровотеча не буде проблемою)

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

Припустимо, текстура 8х8. Ви, ймовірно, отримуєте верхню ліву чверть, використовуючи uv координати від (0,0) до (0,5, 0,5):

Неправильне відображення

Проблема полягає в тому, що при u-координаті 0,5 пробовідбір буде інтерполювати цільовий фрагмент, використовуючи половину лівого текселя та половину правого текселя. Те саме відбудеться при u-координаті 0, а те саме для v-координат. Це тому, що ви вирішуєте межі між текселями, а не центрами.

Замість цього слід звернутися до центру кожного текселя. У цьому прикладі це координати, які ви хочете використовувати:

Правильне відображення

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

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

function get_texel_coords(x, y, tex_width, tex_height)
    u = (x + 0.5) / tex_width
    v = (y + 0.5) / tex_height
    return u, v
end

Це називається "корекція півпікселя". Там є більш докладне пояснення в тут , якщо ви зацікавлені.


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

@sharethis Якщо вам точно потрібно атлас, переконайтеся, що ваші підтексти мають розміри 2, щоб ви могли обмежити кровотечу до найнижчого рівня мип. Якщо ви це зробите, вам не потрібно вручну розробляти власні карти.
Піжама Panda

Навіть текстури розміру PoT можуть знебарвити артефакти, оскільки білінеарне фільтрування може проводити вибірку через ці межі. (Принаймні, у реалізаціях, які я бачив.) Що стосується корекції напівпікселя, чи слід це застосовувати в цьому випадку? Якби я хотів, наприклад, зобразити його текстуру гірської породи, напевно це завжди буде від 0,0 до 0,25 уздовж координат U і V?
Килотан

1
@Kylotan Якщо розмір текстури є рівним, а всі підтексти мають рівні розміри і розташовані за рівними координатами, білінеарне фільтрування при вдвічі меншому розмірі текстури не буде змішуватися між підтекстами. Узагальнюйте для потужностей двох (якщо ви бачите артефакти, ви, ймовірно, використовуєте бікубічну, а не білінеарну фільтрацію). Що стосується корекції півпікселя, то це необхідно, тому що 0,25 - це не правий крайній тексель, а середня точка між обома підтекстами. Для того, щоб поставити це в перспективу, уявіть, що ви растризатор: який колір текстури при (0,00, 0,25)?
Піжама Panda

1
@sharethis перевірити цю іншу відповідь, яку я написав, яка може допомогти вам gamedev.stackexchange.com/questions/50023/…
Panda

19

Одним із варіантів, який буде набагато простіше, ніж обробка міпмап та додавання нечітких факторів координати текстури, - використовувати масив текстур. Масиви текстур схожі на тривимірні текстури, але без відображення у 3-му вимірі, тому вони ідеально підходять для текстурних атласів, де "підтексти" мають однаковий розмір.

http://www.opengl.org/wiki/Array_Texture


Якщо вони доступні, вони є набагато кращим рішенням - ви також отримуєте інші функції, як обгортання текстур, які вам не вистачає з атласами.
Jari Komppa

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

8
@sharethis Однозначно відмовляти від вищих технічних рішень, оскільки ви хочете бути "загальними" - це рецепт відмови. Якщо ви пишете гру, не пишіть двигун.
sam hocevar

@SamHocevar. Я пишу двигун, і мій проект стосується конкретної архітектури, коли компоненти повністю роз’єднані. Тому компонент форми не повинен дбати про місцевий компонент, який повинен забезпечувати лише стандартні буфери та стандартну текстуру.
danijar

1
@sharethis Гаразд. Мене якось ввели в оману частиною питання "У моїй грі".
sam hocevar

6

Після сильної боротьби з цим питанням я нарешті придумав рішення.

Щоб використовувати як атлас текстури, так і міпмапінг, мені потрібно виконати зменшення кемпінгу самостійно, оскільки OpenGL інакше буде інтерполювати за межі плиток в атласі. Крім того, мені потрібно було встановити правильні параметри текстури, оскільки інтерполяція між картами також спричинить кровоточивість текстури.

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

При цих параметрах кровотеча не виникає.

Є одне, що ви повинні помітити, надаючи спеціальні карти. Оскільки не має сенсу зменшувати атлас навіть у тому випадку, якщо кожна плитка вже є 1x1, максимальний розмір карти повинен бути встановлений відповідно до наданих вами.

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 7); // pick mipmap level 7 or lower

Дякуємо за всі інші відповіді та дуже корисні коментарі! До речі, я все ще не знаю, як використовувати лінійне масштабування вгору, але я думаю, немає способу.


приємно знати, що ти це вирішив. Ви повинні позначити цю відповідь правильною замість моєї.
Піжама Panda

mipmapping - це техніка виключно для пониження лінійки, яка не має сенсу для покращення вибору. Ідея полягає в тому, що якщо ви збираєтеся намалювати невеликий трикутник, вам знадобиться багато часу, щоб відібрати велику текстуру, щоб вийти з неї кілька пікселів, тому ви попередньо проаналізуйте її та використаєте відповідний рівень мип, коли малювання невеликих трикутників. Для великих трикутників використання нічого іншого, ніж більший рівень мип, не має сенсу, тому помилка робити щось на кшталтglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_FILTER, GL_NEAREST_MIPMAP_LINEAR);
піжама Panda

@PandaPajama. Ви маєте рацію, я виправив свою відповідь. Для того, щоб встановити GL_TEXTURE_MAX_FILTERна GL_NEAREST_MIPMAP_LINEARпричини кровотечі не через множинного відображення, але через інтерполяції. Так що в цьому випадку він еквівалентний GL_LINEARтому, що все одно використовується найнижчий рівень mipmap-карти.
danijar

@danijar - Чи можете ви пояснити більш докладно, як ви самі виконуєте програму пониження, і як ви скажете OpenGL, де (і коли) використовувати нижній зразок атласу?
Тім Кейн

1
@TimKane Сплит атлас на плитки. Для кожного рівня mipmap зменшіть зразок плиток білінеарно, комбінуйте їх та завантажте в GPU, вказавши рівень mipmap як параметр glTexImage2D(). Графічний процесор автоматично вибирає правильний рівень на основі розміру об'єктів на екрані.
danijar

4

Схоже, проблему може викликати MSAA. Дивіться цю відповідь та пов’язану статтю :

"коли ви вмикаєте MSAA, тоді стає можливим виконання шейдера для зразків, які знаходяться всередині піксельної області, але поза зоною трикутника"

Рішення полягає у використанні відбору центроїдів. Якщо ви обчислюєте обгортання координат текстури у вершинній шейдері, переміщення цієї логіки до фрагмента шейдера також може вирішити проблему.


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

Я відключив апаратне антиалізію, але кровотеча все ще залишається. Я вже роблю текстуру, дивлячись у фрагмент шейдера.
danijar

1

Це стара тема, і у мене була подібна проблема.

Я мав свої координати текстури просто чудово, але лерпуючи положення камери (що змінило позиції мого елемента) так:

public static void tween(Camera cam, Direction dir, Vec2 buffer, Vec2 start, Vec2 end) {
    if(step > steps) {
        tweening = false;
        return;
    }

    final float alpha = step/steps;

    switch(dir) {
    case UP:
    case DOWN:
        buffer = new Vec2(start.getX(), Util.lerp(start.getY(), (float)end.getY(), alpha));
        cam.getPosition().set(buffer);
        break;
    case LEFT:
    case RIGHT:
        buffer = new Vec2(Util.lerp(start.getX(), end.getX(), alpha), start.getY());
        cam.getPosition().set(buffer);
        break;
    }

    step += 1;
}

Вся проблема полягала в тому, що Util.lerp () повертає float або double. Це було погано, оскільки камера перекладала мережу та спричиняла деякі дивні проблеми із фрагментами текстури, де> 0 у X та> 0 у Y.

TLDR: не тюнінгуйте або використовуйте плавці

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