【问题标题】:iPhone paint app (glPaint based). Blending with white backgroundiPhone 绘画应用程序(基于 glPaint)。与白色背景混合
【发布时间】:2010-07-06 10:44:46
【问题描述】:

我正在开发绘画应用程序。我试过用 CoreGraphics/Quartz 2D 来做,绘制曲线算法很慢。所以我们决定切换到 OpenGL ES。 我从来没有任何 OpenGL 经验,所以我从苹果找到了 glPaint 示例并开始使用它。

我改变了erase 方法做白色背景。 我是如何坚持使用画笔和混合的。在示例中,Apple 使用“黑底白字”纹理作为画笔(首先在下图中)。但这对我不起作用(我使用了不同的混合模式)。所以我决定使用不同的刷子,但我没有找到合适的方法。 我在stackoverflow上发现了几个问题,但都没有答案。这是一张图片(来自另一个问题,感谢Kevin Beimers)。
(来源:straandlooper.com

所以问题是如何实现图片中“期望”的笔触。以及如何将 2 个笔画融合到更接近现实生活中的体验(黄色上的蓝色 = 深绿色)。

谢谢。

画笔有当前代码(从 glPaint 修改的位)(来自initWithFrame 方法:

// Make sure the image exists
if(brushImage) {
  // Allocate  memory needed for the bitmap context
  brushData = (GLubyte *) calloc(width * height * 4, sizeof(GLubyte));
  // Use  the bitmatp creation function provided by the Core Graphics framework. 
  brushContext = CGBitmapContextCreate(brushData, width, width, 8, width * 4, CGImageGetColorSpace(brushImage), kCGImageAlphaPremultipliedLast);
  // After you create the context, you can draw the  image to the context.
  CGContextDrawImage(brushContext, CGRectMake(0.0, 0.0, (CGFloat)width, (CGFloat)height), brushImage);
  // You don't need the context at this point, so you need to release it to avoid memory leaks.
  CGContextRelease(brushContext);
  // Use OpenGL ES to generate a name for the texture.
  glGenTextures(1, &brushTexture);
  // Bind the texture name. 
  glBindTexture(GL_TEXTURE_2D, brushTexture);
  // Set the texture parameters to use a minifying filter and a linear filer (weighted average)
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  // Specify a 2D texture image, providing the a pointer to the image data in memory
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, brushData);
  // Release  the image data; it's no longer needed
        free(brushData);
  // Make the current material colour track the current color
  glEnable( GL_COLOR_MATERIAL );
  // Enable use of the texture
  glEnable(GL_TEXTURE_2D);
  // Set a blending function to use
  glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
  // Enable blending
  glEnable(GL_BLEND);
  // Multiply the texture colour by the material colour.
  glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

}

//Set up OpenGL states
glMatrixMode(GL_PROJECTION);
CGRect frame = self.bounds;
glOrthof(0, frame.size.width, 0, frame.size.height, -1, 1);
glViewport(0, 0, frame.size.width, frame.size.height);
glMatrixMode(GL_MODELVIEW);

glDisable(GL_DITHER);
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
  glEnable(GL_BLEND);




// Alpha blend each "dab" of paint onto background
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );


//glBlendFunc(GL_SRC_COLOR, GL_ONE);
glEnable(GL_POINT_SPRITE_OES);
glTexEnvf(GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_TRUE);

self.brushScale = 3;
self.brushStep = 3;
self.brushOpacity = (1.0 / 1.5);

glPointSize(width / brushScale);

//Make sure to start with a cleared buffer
needsErase = YES;
[self erase];

【问题讨论】:

    标签: iphone objective-c ipad opengl-es


    【解决方案1】:

    让我们从定义您要寻找的混合类型开始。听起来您希望缓冲区以白色开始并让您的颜色混合服从subtractive color model。最简单的方法是将 CbrushCdst 混合的结果定义为:

    C = Cbrush × Cdst

    请注意,使用此等式,混合黄色 (1, 1, 0) 和青色 (0, 1, 1) 的结果是绿色 (0, 1, 0),这是您所期望的。

    使用在边缘褪色的刷子会使事情稍微复杂化。假设您现在有一个画笔不透明度值 Abrush——其中 Abrush 为 1,您想要您的画笔以完全强度混合的颜色,并且 Abrush 为 0,您希望保留原始颜色。现在您正在寻找的是:

    C = (Cbrush × Cdst) × Abrush + Cdst × (1 - Abrush)

    由于 OpenGL ES 结果中的混合计算 C = Csrc × S + Cdst × D,如果我们进行以下替换,我们可以得到我们想要的:

    Csrc = Cbrush × Abrush

    Asrc = A画笔

    S = Cdst

    D = (1 - A刷子)

    现在让我们看看如何在 OpenGL ES 中进行设置。这里有 4 个步骤:

    1. 将背景颜色更改为白色。

    2. 将画笔纹理更改为 alpha 纹理。
      默认情况下,GLPaint 将其画笔纹理创建为 RGBA 纹理,画笔形状在 RGB 通道中绘制,这有点不直观。由于您稍后会看到的原因,将画笔形状设置在 Alpha 通道中会很有用。最好的方法是使用 CG 绘制灰度笔刷形状并将纹理创建为GL_ALPHA

      CGColorSpaceRef brushColorSpace = CGColorSpaceCreateDeviceGray();
      brushData = (GLubyte *) calloc(width * height, sizeof(GLubyte));
      brushContext = CGBitmapContextCreate(brushData, width, width, 8, width, brushColorSpace, kCGImageAlphaNone);
      CGColorSpaceRelease(brushColorSpace);
      

      glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, brushData);        
      
    3. 设置CsrcAsrcSD
      切换到 alpha 纹理后,假设您的画笔颜色仍然通过 glColor4f 指定,您会发现默认的 OpenGL ES 纹理环境会为您提供:

      Csrc = Cbrush

      Asrc = A画笔

      为了获得 AbrushCsrc 的额外乘法,您需要在纹理环境中设置自定义组合器函数如下(可以在PaintingView的初始化函数中进行):

      glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
      glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA);
      

      GL_TEXTURE_ENV_MODE 更改为GL_COMBINE 会给您Cbrush × 0(要了解为什么会这样,请阅读OpenGL ES 1.1 specification 中的第 3.7.12 节)。将 GL_OPERAND0_RGB 更改为 GL_SRC_ALPHA 会将乘法中的第二项更改为我们想要的。

      要设置SD,你需要做的就是改变混合因子(这可以在之前设置混合因子的地方完成):

      glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); 
      
    4. 确保在画笔纹理之外对A画笔的任何修改都会反映到其他通道。
      以上对纹理环境的修改只考虑了来自画笔纹理的部分画笔不透明度。如果您在其他地方修改 Alpha 通道中的画笔不透明度(即通过缩放它,如在 AppController 中),您必须确保对其他三个通道进行相同的修改:

      glColor4f(components[0] * kBrushOpacity, components[1] * kBrushOpacity, components[2] * kBrushOpacity, kBrushOpacity);
      

    请注意,使用减色模型实现画笔的缺点是颜色只会变得更暗,如果它不是主要的减色(青色)之一,重复绘制相同的颜色最终会导致颜色偏移、洋红色或黄色)。如果在执行此操作后,您发现颜色偏移不可接受,请尝试将笔刷纹理更改为第 2 步中的 alpha 纹理,并按如下方式更改混合因子:

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    

    这将使您可以在白色上简单地绘制画笔颜色,但不会实际混合颜色(画笔颜色最终会覆盖背景)。

    【讨论】:

    • 非常感谢您非常详细的回答!我已经尝试过您的解决方案,它有效。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-14
    • 1970-01-01
    • 2018-10-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多