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


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

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

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

Моя текущая идея для карты состоит в том, чтобы сохранить индексированную палитру цветов провинций и выполнить операцию подкачки палитры в моем шейдере фрагментов (как описано в этом вопросе 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);

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

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

Например, текстура палитра была бы размером (р+г+б),(р, г, Б).

С этим, с ними же текстура точек:

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, одна и та же проблема возникнет из-за законов математики. Это приводит к актуальной проблеме: как я могу однозначно и процедурно индексировать цвета страны в текстуре палитры и получить к ним доступ через мой шейдер? Это кажется наиболее эффективным методом, но его реализация ускользает от меня.

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

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

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

2 2

2 ответа:

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

id = r + 256*g + 65536*b

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

x = id%xs
y = id/xs

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

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

[Edit1]

Итак, если я использую это изображение, которое вы связали в качестве входного сигнала (txr_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 - линейно выделенный текстурный массив, поэтому адрес также является вашим id... Это позволяет выбрать несколько областей, которые я выбрал с помощью палитры цветов, и установить их с определенными цветами (красный,зеленый,синий,...).

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

//---------------------------------------------------------------------------
// 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 (именно поэтому я использую float s, скорее всего, какая-то внутренняя проблема формата текстуры). Мой CPU side 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));

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

смесь

Таким образом, он четко работает как ожидаемый...

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

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

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

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