Ios GLSL-将纹理的特定颜色更改为其他颜色

Ios GLSL-将纹理的特定颜色更改为其他颜色,ios,opengl-es,glsl,Ios,Opengl Es,Glsl,我的纹理由4种不同的颜色组成。我想把每种颜色换成不同的颜色。我试过以下方法: precision mediump float; varying lowp vec4 vColor; varying highp vec2 vUv; uniform sampler2D texture; bool inRange( float c1, float c2 ) { return abs( c1 - c2 ) < 0.01; } void main() { vec4 c = textur

我的纹理由4种不同的颜色组成。我想把每种颜色换成不同的颜色。我试过以下方法:

precision mediump float;

varying lowp vec4 vColor;
varying highp vec2 vUv;

uniform sampler2D texture;

bool inRange( float c1, float c2 ) {
  return abs( c1 - c2 ) < 0.01;
}

void main() {
  vec4 c = texture2D(texture, vUv);

  if ( inRange( c.r, 238.0/255.0 ) && inRange( c.g, 255.0/255.0 ) && inRange( c.b, 84.0/255.0 ) )
    c = vec4( 254.0/255.0, 254.0/255.0, 247.0/255.0, 1.0 );

  else if ( inRange( c.r, 15.0/255.0 ) && inRange( c.g, 59.0/255.0 ) && inRange( c.b, 5.0/255.0 ) )
    c = vec4( 65.0/255.0, 65.0/255.0, 65.0/255.0, 1.0 );

  else if ( inRange( c.r, 157.0/255.0 ) && inRange( c.g, 184.0/255.0 ) && inRange( c.b, 55.0/255.0 ) )
    c = vec4( 254.0/255.0, 247.0/255.0, 192.0/255.0, 1.0 );

  else if ( inRange( c.r, 107.0/255.0 ) && inRange( c.g, 140.0/255.0 ) && inRange( c.b, 38.0/255.0 ) )
    c = vec4( 226.0/255.0, 148.0/255.0, 148.0/255.0, 1.0 );

  gl_FragColor = c;
}
这很有效。但是速度太慢了。我在iPhone上运行这个程序,但计算并没有那么困难,或者我遗漏了什么

有没有更快的方法可以做到这一点?

编辑:询问者确认,是任何分支的存在导致了难以置信的减速。我将尝试一种无分支的解决方案

如果分支包括使用三元数?运算符不可用,只能使用算术

从维护的角度来看,一个可能的解决方案很可怕,但可能适合您的需要,就是使用多项式将输入颜色映射到输出颜色,为您关心的4种颜色提供所需的输出。我分别处理了3个RGB颜色通道,并将输入/输出点插入wolfram alpha,红色通道的立方拟合示例如下:。为此,您可以使用任何多项式拟合程序

红色通道的代码为:

float redResult = 20.6606 + 3.15457 * c.r - 0.0135167 * c.r*c.r + 0.0000184102 c.r*c.r*c.r
冲洗并使用绿色和蓝色通道重复此过程,您就拥有了着色器。请注意,您可能希望在科学记数法中指定非常小的系数以保持准确性。。。我不知道您的特定驱动程序如何处理浮点文本

即使这样,您也可能会遇到精度问题,但值得一试

另一种可能是使用近似值,我说近似值,因为您实际上并不关心平滑度约束。您只需要在所关心的颜色处设置一个值,该值为1,在足够远的地方设置为0。假设您有一个三分量凹凸函数:bump3,它使用vec3作为凹凸的位置,使用vec3作为计算函数的位置。然后,您可以从以下内容重写第一个条件:

  if ( inRange( c.r, 238.0/255.0 ) && inRange( c.g, 255.0/255.0 ) && inRange( c.b, 84.0/255.0 ) )
    c = vec4( 254.0/255.0, 254.0/255.0, 247.0/255.0, 1.0 );
致:

如果硬件上的max/min速度很快,那么它们可能是引擎盖下的完整分支:,可能的快速脏bump3实现可能是:

float bump3(vec3 b, vec3 p) {
   vec3 diff = abs(b-p);
   return max(0.0, 1.0 - 255.0*(diff.x + diff.y + diff.z));
}
bump3的其他可能性可能是再次滥用smoothstep,如果它在您的硬件上速度很快,或者使用指数

多项式方法还有一个附带的好处,即将地图概括为不仅仅是四种颜色,但需要许多算术运算,是维护的噩梦,并且可能会遇到精度问题。另一方面,凹凸函数方法应产生与当前着色器相同的结果,即使输入不是这四种颜色中的一种,并且可读性和可维护性更高。与多项式方法相比,添加另一种颜色对非常简单。然而,在我给出的实现中,它使用了一个max,我希望它不是引擎盖下的一个分支

原始答案如下

这将是很好的,知道你是如何获得时间信息,所以我们可以确保它的这个着色器是缓慢的,你可以测试这一点,只是让这一个通过着色器作为一个快速的黑客。。。不过,我建议您习惯使用探查器。这样一个简单的着色器速度很慢,这似乎非常奇怪

否则,如果您的纹理确实只有这4种颜色,并且它是有保证的,那么您可以通过从最后一个分支中删除if,使其成为else,然后只测试c的r值,将inRange调用的数量从12减少到3。我不知道iPhone的glsl优化器是如何工作的,但是你可以进一步尝试用三元运算符替换if语句,看看这是否有什么不同。这些是我能想到的唯一的改变,不幸的是,如果你的纹理不能保证只有这4种颜色,你就不能做明确的优化

我想再次指出,在尝试优化之前,您应该确保该着色器导致了减速。

编辑:询问者确认,正是存在任何分支导致了难以置信的减速。我将尝试一种无分支的解决方案

如果分支包括使用三元数?运算符不可用,只能使用算术

从维护的角度来看,一个可能的解决方案很可怕,但可能适合您的需要,就是使用多项式将输入颜色映射到输出颜色,为您关心的4种颜色提供所需的输出。我分别处理了3个RGB颜色通道,并将输入/输出点插入wolfram alpha,红色通道的立方拟合示例如下:。为此,您可以使用任何多项式拟合程序

红色通道的代码为:

float redResult = 20.6606 + 3.15457 * c.r - 0.0135167 * c.r*c.r + 0.0000184102 c.r*c.r*c.r
冲洗并使用绿色和蓝色通道重复此过程,您就拥有了着色器。请注意,您可能希望在科学记数法中指定非常小的系数以保持准确性。。。我不知道您的特定驱动程序如何处理浮点文本

即使这样,你也可能有prec 伊森问题,但值得一试

另一种可能是使用近似值,我说近似值,因为您实际上并不关心平滑度约束。您只需要在所关心的颜色处设置一个值,该值为1,在足够远的地方设置为0。假设您有一个三分量凹凸函数:bump3,它使用vec3作为凹凸的位置,使用vec3作为计算函数的位置。然后,您可以从以下内容重写第一个条件:

  if ( inRange( c.r, 238.0/255.0 ) && inRange( c.g, 255.0/255.0 ) && inRange( c.b, 84.0/255.0 ) )
    c = vec4( 254.0/255.0, 254.0/255.0, 247.0/255.0, 1.0 );
致:

如果硬件上的max/min速度很快,那么它们可能是引擎盖下的完整分支:,可能的快速脏bump3实现可能是:

float bump3(vec3 b, vec3 p) {
   vec3 diff = abs(b-p);
   return max(0.0, 1.0 - 255.0*(diff.x + diff.y + diff.z));
}
bump3的其他可能性可能是再次滥用smoothstep,如果它在您的硬件上速度很快,或者使用指数

多项式方法还有一个附带的好处,即将地图概括为不仅仅是四种颜色,但需要许多算术运算,是维护的噩梦,并且可能会遇到精度问题。另一方面,凹凸函数方法应产生与当前着色器相同的结果,即使输入不是这四种颜色中的一种,并且可读性和可维护性更高。与多项式方法相比,添加另一种颜色对非常简单。然而,在我给出的实现中,它使用了一个max,我希望它不是引擎盖下的一个分支

原始答案如下

这将是很好的,知道你是如何获得时间信息,所以我们可以确保它的这个着色器是缓慢的,你可以测试这一点,只是让这一个通过着色器作为一个快速的黑客。。。不过,我建议您习惯使用探查器。这样一个简单的着色器速度很慢,这似乎非常奇怪

否则,如果您的纹理确实只有这4种颜色,并且它是有保证的,那么您可以通过从最后一个分支中删除if,使其成为else,然后只测试c的r值,将inRange调用的数量从12减少到3。我不知道iPhone的glsl优化器是如何工作的,但是你可以进一步尝试用三元运算符替换if语句,看看这是否有什么不同。这些是我能想到的唯一的改变,不幸的是,如果你的纹理不能保证只有这4种颜色,你就不能做明确的优化


我想再次指出,在尝试优化之前,您应该确保此着色器导致了速度减慢。

分支对着色器性能有害。通常,GPU一次为各自的片段执行多个片段着色器。它们都在lockstep中运行——SIMD处理意味着实际上所有并行片段处理器都在运行相同的代码,但在不同的数据上运行。当您有条件时,不同的片段可能位于不同的代码路径上,因此您将失去SIMD并行性

这类应用程序的最佳性能技巧之一是使用。在查找表中提供3D纹理,并使用GLSL texture3D函数进行查找-输入坐标是原始颜色的R、G和B值,输出是替换颜色

这是非常快的,即使在移动硬件上-片段着色器不必进行任何计算,并且纹理查找通常在片段着色器运行之前缓存


构造查找表纹理很容易。从概念上讲,它是对每个可能的RGB值进行编码的立方体x轴是R从0.0到1.0,y轴是G,z轴是B。如果将其组织为二维图像,则可以在您喜欢的图像编辑器中打开它,并对其应用任何颜色变换过滤器。过滤后的图像是您的转换查找表。关于这项技术有一篇不错的文章,还有另外一篇。对于使用核心图像过滤器应用的技术,更一般的讨论是。

分支对着色器性能有害。通常,GPU一次为各自的片段执行多个片段着色器。它们都在lockstep中运行——SIMD处理意味着实际上所有并行片段处理器都在运行相同的代码,但在不同的数据上运行。当您有条件时,不同的片段可能位于不同的代码路径上,因此您将失去SIMD并行性

这类应用程序的最佳性能技巧之一是使用。在查找表中提供3D纹理,并使用GLSL texture3D函数进行查找-输入坐标是原始颜色的R、G和B值,输出是替换颜色

这是非常快的,即使在移动硬件上-片段着色器不必进行任何计算,并且纹理查找通常在片段着色器运行之前缓存

构造查找表纹理很容易。从概念上讲,它是对每个可能的RGB值进行编码的立方体x轴是R从0.0到1.0,y轴是G,z轴是B。如果将其组织为二维图像,则可以
n然后在您喜爱的图像编辑器中打开它,并对其应用您喜欢的任何颜色变换过滤器。过滤后的图像是您的转换查找表。关于这项技术有一篇不错的文章,还有另外一篇。关于使用核心图像过滤器应用的技术的更一般性的讨论是。

您是否分析了您的代码或使用OpenGL profiler工具来查看着色器是否减慢了速度?它看起来不像是一个会导致缓慢运行的着色器,所以我想知道你的设置中是否有其他东西导致它运行缓慢?是的,是着色器。如果我只使用gl_FragColor=texture2Dtexture,vUv;它以每秒60帧的速度运行。使用上面的着色器,速度会下降到2 fps。您是否分析过代码或使用OpenGL profiler工具查看着色器是否会减速?它看起来不像是一个会导致缓慢运行的着色器,所以我想知道你的设置中是否有其他东西导致它运行缓慢?是的,是着色器。如果我只使用gl_FragColor=texture2Dtexture,vUv;它以每秒60帧的速度运行。使用上面的着色器,它将下降到2 fps。如上面的评论所述,如果我只使用gl_FragColor=texture2Dtexture,vUv;我得到了正常的60 fps。使用着色器时,它将下降到2 fps。Xcode的分析工具警告我分支。因此,即使将条件减少到只有一个颜色值,也会给我留下5 fps。更好,但还不够好。我现在正在寻找一种不用if的方法来实现这一点。好的,更新了一些看似合理的无分支解决方案。谢谢,这有点帮助。但它仍然很慢。所以问题一定在别的地方。我认为问题在于,这是每个纹理的着色器。我现在正在研究将整个东西渲染成纹理,并在上面使用着色器;我得到了正常的60 fps。使用着色器时,它将下降到2 fps。Xcode的分析工具警告我分支。因此,即使将条件减少到只有一个颜色值,也会给我留下5 fps。更好,但还不够好。我现在正在寻找一种不用if的方法来实现这一点。好的,更新了一些看似合理的无分支解决方案。谢谢,这有点帮助。但它仍然很慢。所以问题一定在别的地方。我认为问题在于,这是每个纹理的着色器。我现在正在研究将整个东西渲染成纹理,并在上面使用着色器。