iPhone 绘画应用程序(基于 glPaint)。与白色背景混合

标签 iphone objective-c ipad opengl-es

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

我改了erase方法做白色背景。
我是如何坚持使用画笔和混合的。在示例中,Apple 使用“黑底白字”纹理作为画笔(下图的第一个)。但它对我不起作用(我玩过不同的混合模式)。所以我决定使用不同的画笔,但我没有找到正确的方法。
我在 stackoverflow 上发现了几个问题,但所有问题都没有得到解答。这是一张图片(来自另一个问题,感谢 Kevin Beimers )。
Results
(来源: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];

最佳答案

让我们从定义您正在寻找的混合类型开始。听起来您希望缓冲区从白色开始并让您的颜色混合遵守 subtractive color model .最简单的方法是将 Cbrush 与 Cdst 混合的结果定义为:

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 = Abrush

S = Cdst

D = (1 - Abrush)



现在让我们看看在 OpenGL ES 中设置它需要什么。这里有4个步骤:
  • 将背景颜色更改为白色。
  • 将笔刷纹理更改为 alpha 纹理。
    默认情况下,GLPaint 将其画笔纹理创建为 RGBA 纹理,并在 RGB channel 中绘制画笔形状,这有点不直观。出于您稍后将看到的原因,在 Alpha channel 中使用画笔形状会很有用。最好的方法是使用 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);        
    
  • 设置 Csrc、Asrc、S 和 D。
    切换到 alpha 纹理后,假设您的画笔颜色仍在通过 glColor4f 指定,你会发现默认的 OpenGL ES 纹理环境会给你这样的:

    Csrc = Cbrush

    Asrc = Abrush



    为了通过 Abrush 获得 Csrc 的额外乘法,您需要在纹理环境中设置一个自定义组合器函数,如下所示(您可以在 PaintingView 的初始化函数中执行此操作):
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
    glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA);
    

    GL_TEXTURE_ENV_MODEGL_COMBINE为您提供 Cbrush × 0(要了解为什么会这样,请阅读 OpenGL ES 1.1 specification 中的第 3.7.12 节)。换 GL_OPERAND0_RGBGL_SRC_ALPHA将乘法中的第二项更改为我们想要的。

    要设置 S 和 D,您需要做的就是更改混合因子(这可以在之前设置混合因子的地方完成):
    glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); 
    
  • 确保在笔刷纹理之外对 Abrush 的任何修改都反射(reflect)在其他 channel 上。
    上述对纹理环境的修改只考虑了来自笔刷纹理的部分笔刷不透明度。如果您在其他地方修改 alpha channel 中的画笔不透明度(即通过缩放它,如 AppController ),您必须确保对其他三个 channel 进行相同的修改:
    glColor4f(components[0] * kBrushOpacity, components[1] * kBrushOpacity, components[2] * kBrushOpacity, kBrushOpacity);
    

  • 请注意,使用减色模型实现画笔的缺点是颜色只会变暗,如果它不是主要减色(青色、洋红色、或黄色)。如果在执行此操作后发现颜色偏移 Not Acceptable ,请尝试将画笔纹理更改为第 2 步中的 alpha 纹理,并按如下方式更改混合因子:
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    

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

    关于iPhone 绘画应用程序(基于 glPaint)。与白色背景混合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3185511/

    相关文章:

    iphone - 应用 CIFIlter 后设置 UIImageView 内容模式

    objective-c - Xcode 是否有导航到文件/类的快捷方式?

    ios - 内部带有 UIWebview 的滞后 ScrollView

    ipad - iPad 和 Safari 桌面上的 cache.manifest 不起作用

    objective-c - 位掩码的递增部分

    iphone - 线程崩溃问题

    iphone - Apple 如何支付应用程序中的 iAd 费用?

    iphone - iPhone 中 GLSL ES 2.0 上来自多个点源的 2D 照明

    iphone - 如何舍入 CGFloat 值

    iphone - 警告 - 在协议(protocol)中找不到版本