首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何创建和使用非常大的调色板纹理在opengl中使用?

如何创建和使用非常大的调色板纹理在opengl中使用?
EN

Stack Overflow用户
提问于 2018-06-19 03:48:21
回答 2查看 1.5K关注 0票数 2

详细信息:我有一个具有统一纹理的glsl片段着色器,"u_MapTexture“上有几千种颜色(最大值约为10k-15k唯一的rgb值)。我还有一个统一的调色板纹理("u_paletteTexture"),它是16384×1,我想用它来索引u_MapTexture上的颜色。我的问题是,无论我在数学上尝试什么,我似乎都无法使用传递的颜色的RGB值正确地索引从第一个纹理到调色板纹理的颜色。艾米的想法或想法,我如何能做到这一点?

不确定是在此发布,还是在Gamedev SE或Math SE上发布。

编辑:我想我可能没有添加足够的有关问题的信息,所以这里有一些更多的细节。

我目前对地图的想法是保留一个省颜色的索引调色板,并在我的片段着色器中执行调色板-交换操作(就像在这个这样的问题:Simulating palette swaps with OpenGL Shaders (in LibGDX)中概述的那样)。我的着色器几乎完全是从链接的文章中复制的。

我的问题是:找到一种方法来唯一地索引省地图(原始纹理)、->省颜色(索引调色板纹理)。

首先,我决定将调色板纹理配置为(255+255)×(255+255)纹理。这将使数目最多的国家能够作出选择,这在实践中是永远不会达到的。

我认为你可以通过在纹理中得到一个国家颜色的调色板纹理的适当索引:每个国家的索引应该位于调色板纹理的(x,y)->(r+g),(g+b)

我在这个简单的公式中运行了一些颜色示例,并遇到了一个令人不安的场景:

代码语言:javascript
复制
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)。

这样,使用相同的纹理点:

代码语言:javascript
复制
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格式来解释问题。

我需要从每个RGB值中提取唯一的索引,而基于我的测试集,这似乎是不可能的。

基本上,MCVE将创建一个2D的精灵,并将链接所以问题的公认答案的片段着色器传递给sprite。雪碧将由大约10个唯一的RGB值组成,但是无论使用什么系统,都必须支持至少几千种不同的颜色。我没有稳定的互联网连接,否则我会上传我的测试纹理。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-06-19 21:51:55

不确定我是否做对了,让我们假设整数通道<0,255>,所以:

代码语言:javascript
复制
id = r + 256*g + 65536*b

这会给你id = <0,16777215>。现在只需重新映射到您的xs*ys纹理:

代码语言:javascript
复制
x = id%xs
y = id/xs

其中xs,ys是纹理的分辨率。一旦你意识到你可以使用2的力量对所有这一切,你可以使用比特操作代替。比如让xs=4096,ys=4096 ..。

代码语言:javascript
复制
id = r + g<<8 + b<<16
x = id&4095
y = id>>12

Edit1

因此,如果我使用此图像作为输入链接(txr_map):

并生成4096x4096纹理,除:

代码语言:javascript
复制
((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。那么,应用这些着色器就可以做到这一点:

代码语言:javascript
复制
//---------------------------------------------------------------------------
// 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;
    }
//---------------------------------------------------------------------------
代码语言:javascript
复制
//---------------------------------------------------------------------------
// 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的最有可能的内部纹理格式问题)。我的CPUGL代码如下所示:

代码语言:javascript
复制
//---------------------------------------------------------------------------
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
    }
//---------------------------------------------------------------------------

结果如下:

当我将结果和输入纹理(用于视觉检查)混合在一起时:

代码语言:javascript
复制
col=(0.9*texture2D(txr_pal,c.st))+(0.1*texture2D(txr_map,txr));

结果如下:

所以很明显它能像预期的那样起作用。

票数 2
EN

Stack Overflow用户

发布于 2018-06-19 20:54:31

我不知道你到底想做什么。

首先,将所有8位RGB颜色唯一映射到索引的唯一方法是有256^3索引。您可以对这些位进行调整,以获得一个非标识映射(如here),但您仍然需要大量的目标索引。

如果只使用所有颜色的子集,并且您希望少于256^3目标索引(如您所描述的那样),则需要设置一些机制以避免冲突。除非您在源颜色中有一些特殊的属性可以从数学上加以利用,否则这种机制将需要某种形式的存储(比如另一种纹理或SSBO)。

现在我不明白的是,你想把什么映射到索引。是否要将所有可能的颜色映射到唯一的索引?所有与映射相关的事情都必须在着色器内进行吗?你提到了国家和省份,但我不太明白它们与你想要的地图有什么关系。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50920357

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档