代码之家  ›  专栏  ›  技术社区  ›  OgreSwamp

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

  •  5
  • OgreSwamp  · 技术社区  · 14 年前

    我正在开发绘画应用程序。我试过用Coregraphics/Quartz 2D来实现,绘制曲线的算法非常慢。所以我们决定切换到OpenGLES。 我从未有过任何OpenGL体验,所以我从苹果公司找到了GLPAINT示例并开始使用它。

    我变了 erase 方法生成白色背景。 我如何坚持刷和混合。在这个示例中,Apple使用“白色与黑色”纹理作为画笔(下图中的第一个)。但它对我不起作用(我玩不同的混合模式)。所以我决定用不同的刷子,但我没有找到合适的方法。 我在StackOverflow上发现了一些问题,但都没有回答。这是一张照片(来自另一个问题,谢谢 Kevin Beimers ) Results http://www.straandlooper.com/GLPaint.png

    所以问题是如何在图片中实现“所需”的笔画。以及如何将两种笔触融合到更接近真实生活的体验中(蓝色超过黄色=深绿色)。

    谢谢。

    有当前代码(位从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];
    
    1 回复  |  直到 14 年前
        1
  •  18
  •   Pivot    14 年前

    让我们从定义您要寻找的混合类型开始。听起来你想让你的缓冲器从白色开始,让你的颜色混合服从 subtractive color model . 最简单的方法是定义混合的结果 C 结束 C DST AS:

    C = C γ C DST

    注意,使用这个方程,混合黄色(1,1,0)和青色(0,1,1)的结果是绿色(0,1,0),这是您所期望的。

    拥有一把边缘会褪色的刷子会使事情稍微复杂一些。假设您现在有一个画笔不透明度值 在哪里 是1,你希望你的画笔颜色充分混合,在哪里 为0,您希望保留原始颜色。现在你要找的是:

    C =( C γ C DST ) + C DST (1) )

    因为在OpenGL ES中混合计算结果 C = C SRC γ S + C DST γ D 如果我们进行以下替换,我们可以得到我们想要的:

    C SRC = C γ

    SRC =

    S = C DST

    D =(1) )

    现在让我们来看看在OpenGLES中设置它需要什么。这里有4个步骤:

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

    2. 将画笔纹理更改为alpha纹理。
      默认情况下,glpaint使用在rgb通道中绘制的画笔形状将其画笔纹理创建为rgba纹理,这有点不直观。出于您稍后将看到的原因,将画笔形状改为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. 设置 C SRC , SRC , S D .
      切换到alpha纹理后,假设仍通过 glColor4f ,您将发现默认的OpenGL ES纹理环境将为您提供:

      C SRC = C

      SRC =

      为了得到额外的乘法 对于 C SRC ,您需要在纹理环境中设置自定义组合器函数,如下所示(您可以在 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 给你 C _0(要了解为什么会出现这种情况,请阅读 OpenGL ES 1.1 specification )改变 GL_OPERAND0_RGB GL_SRC_ALPHA 把乘法中的第二项改成我们想要的。

      设立 S D ,您只需更改混合因子(可以在之前设置了混合因子的情况下进行此操作):

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

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

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

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    

    这将给你简单的绘画你的画笔颜色超过白色,但没有实际的颜色混合(画笔颜色最终会覆盖背景)。