Как создавать и использовать текстуры очень большой палитры для использования в opengl?

Подробности: у меня есть шейдер фрагмента glsl с однородной текстурой "u_MapTexture" с несколькими тысячами цветов (максимум около 10-15 тысяч уникальных значений rgb). У меня также есть текстура однородной палитры («u_paletteTexture») размером 16384 × 1, которую я хочу использовать для индексации цветов на u_MapTexture. Моя проблема в том, что независимо от того, что я пытаюсь математически, я не могу правильно проиндексировать цвета от первой текстуры до текстуры палитры, используя значения RGB переданного цвета. Эми мысли или идеи относительно того, как я могу это сделать?

Не был уверен, размещать ли это здесь, на Gamedev SE или на Math SE.

Изменить: я думаю, что, возможно, я не добавил достаточно информации о проблеме, поэтому вот некоторые подробности.

Моя текущая идея для карты состоит в том, чтобы сохранить проиндексированную палитру цветов провинции и выполнить операцию замены палитры в моем шейдере фрагмента (как та, что описана в этом вопросе SO: Моделирование смены палитры с помощью шейдеров OpenGL (в LibGDX)). Мой шейдер практически полностью скопирован из связанной статьи.

Моя проблема: найти способ однозначно проиндексировать карту провинции (исходная текстура) -> цвета провинции (индексированная текстура палитры).

Сначала я решил, что текстура палитры будет настроена как текстура (255 + 255) × (255 + 255). Это дало бы большому максимальному количеству стран, из которых можно было бы выбирать, чего никогда не достичь на практике.

Я думал, что вы можете получить соответствующий индекс текстуры палитры цвета страны, получив его индекс в текстуре следующим образом: индекс каждой страны был бы расположен в этой палитре текстуры (x, y) -> (r + g ), (g + b)

Я провел несколько примеров цветов с помощью этого простого уравнения и натолкнулся на неприятный сценарий:

RGB (0, 0, 0) -> (0, 0);
RGB (1, 0, 1) -> (1, 1); ?
RGB (1, 3, 2) -> (4, 5);
RGB (0, 1, 0) -> (1, 1); ?
RGB (2, 5, 10) -> (7, 15);
RGB (255, 255, 255) -> (510, 510);

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

Затем я подумал добавить дополнительные параметры и сжать текстуру до одномерного массива.

Например, текстура палитры имела бы размер (r + g + b), (r, g, b).

При этом с ними такие же точки текстуры:

RGB(0, 0, 0) -> (0);
RGB(1, 0, 1) -> (2); ?
RGB(0, 1, 1) -> (2); ?
RGB(1, 3, 2) -> (6); ?
RGB(3, 2, 1) -> (6); ?
RGB(0, 1, 0) -> (1);
RGB(2, 5, 10) -> (17);
RGB(255, 255, 255) -> (1020);

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

Кроме того, для записи, я знаю, что координаты UV и значения цвета являются плавающими, но я использую стандартный формат 0–255, чтобы решить проблему.

TL; DR Мне нужно извлечь уникальный индекс из каждого значения RGB, и это не представляется возможным на основе моих наборов тестов.

По сути, MCVE будет создавать 2D-спрайт и передавать спрайту фрагментный шейдер принятого ответа связанного вопроса SO. Спрайт будет состоять из примерно 10 уникальных значений RGB, однако какая бы система ни использовалась, она должна поддерживать как минимум несколько тысяч различных уникальных цветов. У меня нет стабильного интернет-соединения, иначе я бы загрузил свои тестовые текстуры.


person JDSweetBeat    schedule 19.06.2018    source источник
comment
Трудно сказать без MCVE, но держу пари, что вы не используете GL_NEAREST фильтрацию для обеих текстур, поэтому ваши индексы, а также цвета палитры интерполировались с соседями. Используйте glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_NEAREST); для обеих текстур   -  person Spektre    schedule 19.06.2018
comment
Хорошая догадка @Spektre. Если это не решит проблему, вам необходимо предоставить дополнительную информацию о полученных результатах, задействованном шейдере и т. Д.   -  person bernie    schedule 19.06.2018
comment
@Spektre Нет, я думаю, вы неправильно поняли проблему (полностью моя вина), я добавил больше деталей, пожалуйста, еще раз посмотрите.   -  person JDSweetBeat    schedule 19.06.2018
comment
@bernie Да, я добавил еще несколько деталей и тестовый набор, поэтому, пожалуйста, еще раз взгляните на проблему. Простите за неудобства.   -  person JDSweetBeat    schedule 19.06.2018
comment
@the_OTHER_DJMethaneMan Я добавил ответ, но не уверен, что вы этого хотите ...   -  person Spektre    schedule 20.06.2018


Ответы (2)


Не уверен, что я все равно правильно понял, допустим, целочисленные каналы <0,255>, поэтому:

id = r + 256*g + 65536*b

что даст вам id = <0,16777215>. Теперь просто переназначьте вашу xs*ys текстуру:

x = id%xs
y = id/xs

где xs,ys - разрешение текстуры. Как только вы поймете, что для всего этого можно использовать степень двойки, вы можете вместо этого использовать битовые операции. Например, пусть xs=4096,ys=4096 ...

id = r + g<<8 + b<<16
x = id&4095
y = id>>12

[Edit1]

Итак, если я использую это изображение, которое вы связали в качестве ввода (txr_map):

map

И сгенерируйте текстуру 4096x4096 с полями 0x00404040 серого цвета, за исключением:

((DWORD*)(scr.txrs.txr.txr))[0x4A3020]=0x00FF0000;
((DWORD*)(scr.txrs.txr.txr))[0x49247E]=0x0000FF00;
((DWORD*)(scr.txrs.txr.txr))[0xCB3EAD]=0x000000FF;
((DWORD*)(scr.txrs.txr.txr))[0xC78A4F]=0x0000FFFF;
((DWORD*)(scr.txrs.txr.txr))[0x593D4E]=0x00FF00FF;
((DWORD*)(scr.txrs.txr.txr))[0x4B3C7E]=0x00FFFF00;

где scr.txrs.txr.txr - это линейно распределенный массив текстур, поэтому адрес также является вашим _13 _... Он выбирает несколько регионов, которые я выбрал с помощью палитры цветов, и устанавливает для них определенные цвета (красный, зеленый, синий, ...).

Не забудьте установить GL_LINEAR для фильтра min и mag. Затем применение этих шейдеров должно помочь:

//---------------------------------------------------------------------------
// Vertex
//---------------------------------------------------------------------------
#version 120
varying vec2 pos;   // screen position <-1,+1>
varying vec2 txr;   // texture position <0,1>
void main()
    {
    pos=gl_Vertex.xy;
    txr=gl_MultiTexCoord0.st;
    gl_Position=gl_Vertex;
    }
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// Fragment
//---------------------------------------------------------------------------
#version 130
in vec2 pos;    // screen position <-1,+1>
in vec2 txr;    // texture position <0,1>
out vec4 col;
uniform sampler2D txr_map;
uniform sampler2D txr_pal;
//---------------------------------------------------------------------------
void main()
    {
    vec3 c;
    int id,x,y;

    c=texture2D(txr_map,txr).rgb;
    x=int(float(c.b*255.0f)); id =x;
    x=int(float(c.g*255.0f)); id|=x<<8;
    x=int(float(c.r*255.0f)); id|=x<<16;

    x= id     &4095;
    y=(id>>12)&4095;
    c.s=(float(x)+0.5f)/4096.0f;
    c.t=(float(y)+0.5f)/4096.0f;
    col=texture2D(txr_pal,c.st);
    }
//---------------------------------------------------------------------------

К сожалению, usampler2D не работает в моем движке в старом API (поэтому я использую floats, скорее всего, проблема с внутренним форматом текстуры). Мой код GL со стороны ЦП выглядит следующим образом:

//---------------------------------------------------------------------------
OpenGLscreen scr;   // my GL engine
GLSLprogram shd;    // shaders
GLint txr_map=-1;   // map
GLint txr_pal=-1;   // palette
//---------------------------------------------------------------------------
void TForm1::draw()
    {
    scr.cls();  // glClear

    glDisable(GL_CULL_FACE);
    glDisable(GL_DEPTH_TEST);
    shd.bind(); // use shader program
    int unit=0;
    scr.txrs.bind(txr_map,unit); shd.set1i("txr_map",unit); unit++; // bind textures and set uniforms
    scr.txrs.bind(txr_pal,unit); shd.set1i("txr_pal",unit); unit++;
    float a=5632.0/8192.0;  // handle texture power of 2 size correction

    glActiveTexture(GL_TEXTURE0);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0,1.0); glVertex2f(-1.0,-1.0);
    glTexCoord2f(0.0,0.0); glVertex2f(-1.0,+1.0);
    glTexCoord2f( a ,0.0); glVertex2f(+1.0,+1.0);
    glTexCoord2f( a ,1.0); glVertex2f(+1.0,-1.0);
    glEnd();

    for (unit--;unit>=0;unit--) scr.txrs.unbind(unit);  // unbind textures
    shd.unbind();   // unbind shaders

    // just prints the GLSL logs for debug
    scr.text_init_pix(1.0);
    glColor4f(1.0,1.0,1.0,0.75);
    scr.text(0.0,0.0,shd.log);
    scr.text_exit_pixel();

    scr.exe();  // glFlush
    scr.rfs();  // swap buffers
    }
//---------------------------------------------------------------------------

Результат выглядит так:

предварительный просмотр

Когда я смешиваю результат и текстуру ввода (для визуальной проверки) с:

col=(0.9*texture2D(txr_pal,c.st))+(0.1*texture2D(txr_map,txr));

Результат выглядит так:

смесь

Итак, он явно работает, как ожидалось ...

person Spektre    schedule 19.06.2018
comment
Эти вопросы могут показаться глупыми, но: где вы взяли 65536? Для целочисленных каналов r == 0 и g == 255 или r == 0 и b == 255? И 4096 × 4096 - это разрешение исходной текстуры или палитры? - person JDSweetBeat; 20.06.2018
comment
@the_OTHER_DJMethaneMan 0-255 означает 8 бит (2^8=256) на канал, поэтому для 2 каналов требуется 16 бит (2^16=65536), а для 3 каналов требуется 24 бита (2^24=16.7M). Да, 4096 - это целевое разрешение, такое как 16.7M^0.5=4096, поэтому оно имеет такое же количество пикселей / текселей, какое вам нужно. Уравнение id просто загружает каналы в их двоичную битовую позицию, поэтому я умножаю каналы на 2^(8*i), где i={0,1,2}. Уравнение x,y делает противоположное x= low 12 bit и y =high 12 bit id. В конце концов, это всего лишь 1: 1 отображение 256^3 -> 4096^2 - person Spektre; 20.06.2018
comment
@the_OTHER_DJMethaneMan после того, как я увидел изображения, которые вы связали, было любопытно, поэтому я попытался закодировать это ... см. [Edit1] с результатом ... - person Spektre; 20.06.2018
comment
Хорошо, спасибо за пояснение, думаю, теперь я понял. - person JDSweetBeat; 20.06.2018

Не уверен, что я точно понимаю, что вы хотите сделать.

Прежде всего, единственный способ однозначно сопоставить все 8-битные цвета RGB с индексами - это иметь индексы 256 ^ 3. Вы можете перетасовать биты, чтобы получить неидентификационное сопоставление (например, здесь), но вам все равно нужно столько индексов назначения.

Если используется только подмножество всех цветов и вам нужно меньше 256 ^ 3 целевых индексов (как вы, кажется, описываете), необходимо внедрить какой-то механизм, чтобы избежать коллизий. Если у вас нет каких-либо особых свойств в исходных цветах, которые можно использовать математически, этот механизм потребует некоторой формы хранения (например, другой текстуры или SSBO).

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

person bernie    schedule 19.06.2018
comment
Я пытаюсь создать политическую карту, как в сериях Europa Universalis или Total War. Каждое уникальное значение RGB, представленное на палитре, представляет провинцию. Я хотел создать сетку ландшафта и наложить цветовую карту на ландшафт, а также использовать смену палитры для изменения цвета провинции (например, если провинция меняет владельца). Идея заключалась в том, чтобы сохранить отдельную текстуру палитры, и каждая провинция будет иметь собственный уникальный индекс в палитре, но у меня нет возможности отправлять данные в шейдер для каждой провинции, поэтому мне нужно найти другой способ для достижения индексации. - person JDSweetBeat; 20.06.2018
comment
Что-то вроде цветовой карты, найденной здесь: eu4.paradoxwikis.com/Map_modding для создания чего-то вроде изображений здесь: google.com/ - person JDSweetBeat; 20.06.2018
comment
@the_OTHER_DJMethaneMan В этом случае, я думаю, Спектр описал простейшее доступное уникальное отображение. Дайте нам знать, если вам нужно что-то еще. - person bernie; 20.06.2018