是否可以在OpenGL ES 1.0中进行去饱和以及如何进行去饱和?

时间:2011-08-03 10:51:14

标签: opengl-es effects textures grayscale

我想渲染一个灰色的颜色纹理。使用着色器在ES 2.0中执行它是件小事,但是可以在ES 1.x中进行吗?

更新

感谢@datenwolf,我这样做:

GLfloat weights_vector[4] = {0.2126, 0.7152, 0.0722, 1.0};
GLfloat additions_vector[4] = {0.5, 0.5, 0.5, 0.0};

glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

/* First part */
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, weights_vector);

/* Second part */
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, additions_vector);

第一部分渲染没问题,如果我自己留下,但如果我添加第二部分,它使用“黑色”作为前一种颜色,所以我只得到灰色像素。我在这做错了吗?

第二次更新

如果我尝试使用GL_TEXTURE0代替GL_PREVIOUS,我确实会得到与GL_TEXTURE相同的结果。但是,如果我使用GL_TEXTURE1,我甚至不会得到灰色像素,而是黑色。我迷路了...

第三次更新

第二部分现在正在运作。应该只使用@datenwolf建议的前一个纹理的名称!

然而,输出仍然不正确,因为它被倒置了。我通过添加:

来解决这个问题
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);

现在,它太黑了。我无法做对。尝试过但没有工作:

  • 将权重乘以2
  • 加0.5
  • 通过2
  • 调整整个图像
  • 将权重乘以2,然后减去0.5

第四次更新(抱歉有这么多)

由于DOT3_RGB中的偏见和乘法,我开始怀疑它是不可能的。在组合器中执行此操作的正确方法是:

  1. 将0.5添加到输入纹理
  2. 计算加权因子:weight_new =(weight_real + 2.0)/(4.0);
  3. 执行GL_DOT3_RGB
  4. 例如,而不是使用:

    GLfloat weights_vector[4] = {0.30, 0.59, 0.11, 0.0};
    

    使用:

    GLfloat weights_vector[4] = {0.575, 0.6475, 0.5275, 0.0};
    

    这确实让几乎得到了正确的结果,但是由于第一步以及数字被限制在[-1.0,1.0]范围内的事实,一些对比度会丢失

    为何进行计算?好吧,根据API:

    Dot calculation

    所以我没有看到任何其他方式与我所展示的不同,并且由于准确性的限制。当然,我可以先将输入除以2.0,然后加0.5,做一个dot3然后再乘以2,从而有效地使所有值都在[-1.0,0.0]范围内。但我担心由于分裂,它仍然会失去准确性。

    我怀疑DOT并非用于此目的,更可能只用于碰撞或其他东西。太糟糕了。我希望我错了,但我不明白。

3 个答案:

答案 0 :(得分:5)

您可以使用点(标量)产品纹理环境。请记住,矢量的点积是

dot(v1, v2) = v1[0]*v2[0] + v1[1]*v2[1] + ... + v1[n]*v2[n]

通过将通道与每个加权因子相加来实现去饱和

L{r,g,b} = w_r * R + w_g * G + w_b * B

但这只不过是带有加权矢量的颜色的点积。 OpenGL-1.5有一个名为 combiner 的纹理环境,这个组合环境具有点积模式:

GLfloat weighting_vector[4];
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, weighting_vector);

由于评论而编辑

您可以在 alpha合并器中指定GL_COMBINE_ALPHA中的Alpha通道上的操作。在您的情况下,您只想使用源alpha。添加这些配置:

glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE);

我忘了的是,点积模式引入了0.5偏差。但这没有问题,因为你提供了至少3个组合器阶段,所以你得到一个GL_SUBSTRACT阶段减去每个通道的0.5并将你的权重乘以2.0以弥补这一点。

查看glTexEnv联机帮助页http://www.opengl.org/sdk/docs/man/xhtml/glTexEnv.xml和原始扩展程序规范http://www.opengl.org/registry/specs/ARB/texture_env_combine.txt(这是此扩展程序的时间)。我承认,如果你是新手,纹理合成器会有点扭曲。从历史上看,它们是片段着色器的前身; NVidia开始使用他们所谓的“注册组合器”,后来成为纹理组合器。

EDIT2由于问题的附录

您必须在自己的合并器阶段(=纹理单元)中执行第二部分。您使用glActiveTexture切换纹理单位。像这样修改你的代码:

GLfloat weights_vector[4] = {0.2126, 0.7152, 0.0722, 1.0};
GLfloat additions_vector[4] = {0.5, 0.5, 0.5, 0.0};

glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texID);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

/* First part */
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, weights_vector);

glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texID); // we need some dummy texture active
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

/* Second part */
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_SUBTRACT);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, additions_vector);

另外我认为你打算减去,补偿0.5偏差而不是加0.5; FTFY。

答案 1 :(得分:5)

使用2个或3个纹理合成器可以实现这一点。您很可能希望支持仅支持2个纹理合并器的旧设备(例如iPhone 3G)。您可以使用以下代码确定设备支持的纹理合成器数量:

int maxTextureUnits;
glGetIntegerv(GL_MAX_TEXTURE_UNITS, &maxTextureUnits);

在没有着色器的情况下实现渲染灰度纹理的基本步骤是:

  1. 将所有像素RGB值除以2
  2. 从所有像素RGB值中减去.5
  3. 使用自定义加权值计算每个RGB值的GL_DOT3_RGB值
  4. 现在,可以使用Texture Combiner执行每个步骤。或者,您也可以只使用两个纹理合并器,将第1步替换为自定义代码,以便在读取纹理时调整像素值。如果您选择仅使用两个纹理合成器,您仍然可以使用颜色渲染调整后的纹理,则只需要一个纹理合并器,在渲染之前将RGB值加倍。

    我们为所有像素值添加.5的原因是因为我们用来计算亮度的GL_DOT3_RGB方程将从每个像素值中减去.5。

    我们将所有像素值除以2的原因是,当从步骤2移动到步骤3时,我们的值不会被钳制。如果我们的RGB值为(.5,.6,.7)并且我们添加了.5对于每个RGB值,我们得到的RGB值进入步骤3将是(1.0,1.0,1.0)。在DOT3方程从每个值中减去.5后,它将根据(.5,.5,.5)计算亮度。

    以下是使用3个纹理单元渲染纹理灰度的示例代码:

    //Enable texture unit 0 to divide RGB values in our texture by 2
    glActiveTexture(GL_TEXTURE0);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, m_textureId);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
    glClientActiveTexture(GL_TEXTURE0);
    
    //GL_MODULATE is Arg0 * Arg1    
    glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
    
    //Configure Arg0
    glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
    glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
    
    //Configure Arg1
    float multipliers[4] = {.5, .5, .5, 0.0};
    glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
    glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
    glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, (GLfloat*)&multipliers);
    
    //Remember to set your texture coordinates if you need them
    //glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    //glTexCoordPointer...
    
    //Enable texture unit 1 to increase RGB values by .5
    glActiveTexture(GL_TEXTURE1);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, m_textureId);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
    glClientActiveTexture(GL_TEXTURE1);
    
    //GL_ADD is Arg0 + Arg1
    glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD);
    
    //Configure Arg0
    glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
    glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
    
    //Configure Arg1
    GLfloat additions[4] = {.5, .5, .5, 0.0};
    glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
    glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
    glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, (GLfloat*)&additions);
    
    //Set your texture coordinates if you need them
    //glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    //glTexCoordPointer...
    
    //Enable texture combiner 2 to get a DOT3_RGB product of your RGB values
    glActiveTexture(GL_TEXTURE2);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, m_textureId);
    glClientActiveTexture(GL_TEXTURE2);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
    
    //GL_DOT3_RGB is 4*((Arg0r - 0.5) * (Arg1r - 0.5) + (Arg0g - 0.5) * (Arg1g - 0.5) + (Arg0b - 0.5) * (Arg1b - 0.5))
    glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB);   
    
    //Configure Arg0
    glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
    glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
    
    //Configure Arg1
    //We want this to adjust our DOT3 by R*0.3 + G*0.59 + B*0.11
    //So, our actual adjustment will need to take into consideration
    //the fact that OpenGL will subtract .5 from our Arg1
    //and we need to also take into consideration that we have divided 
    //our RGB values by 2 and we are multiplying the entire
    //DOT3 product by 4
    //So, for Red adjustment you will get :
    //   .65 = (4*(0.3))/2 + 0.5  = (0.3/2) + 0.5
    GLfloat weights[4] = {.65, .795, .555, 1.};
    glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
    glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
    glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, (GLfloat*)&weights);
    
    //Set your texture coordinates if you need them
    //glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    //glTexCoordPointer...
    
    //Render your objects or sprite
    
    //Clean up by disabling your texture combiners or texture units. 
    glActiveTexture(GL_TEXTURE2);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glDisable(GL_TEXTURE_2D);
    
    glActiveTexture(GL_TEXTURE1);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glDisable(GL_TEXTURE_2D);
    
    glActiveTexture(GL_TEXTURE0);
    glClientActiveTexture(GL_TEXTURE0);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    

答案 2 :(得分:1)

在将纹理上传到OpenGL之前,使用ColorMatrix

对其进行去饱和处理
Paint paint = new Paint();
ColorMatrix matrix = new ColorMatrix();
matrix.setSaturation(0);
paint.setColorFilter(new ColorMatrixColorFilter(matrix));

Bitmap bmp = Bitmap.createBitmap(resource.getWidth(), resource.getHeight(), resource.getConfig());
Canvas canvas = new Canvas(bmp);
canvas.drawBitmap(resource, null, paint);