我正在开发绘画应用程序。我试过用CoreGraphics / Quartz 2D和绘图曲线算法做的很慢。所以我们决定改用OpenGL ES。 我从未有过任何OpenGL经验,所以我从苹果中找到了glPaint示例并开始玩它。
我已经改变erase
方法做白色背景。
我如何坚持刷和混合。在示例中,Apple使用“白色黑色”纹理作为画笔(首先在下面的图片中)。但它对我不起作用(我使用不同的混合模式)。所以我决定使用不同的画笔,但我找不到合适的方法。
我在stackoverflow上发现了一些问题,但是所有问题都没有得到答复。这是一张图片(来自另一个问题,感谢Kevin Beimers)。
Results http://www.straandlooper.com/GLPaint.png
所以问题是如何在图片中实现类似“所需”的笔画。如何将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];
答案 0 :(得分:18)
让我们首先定义您正在寻找的混合类型。听起来你希望你的缓冲区开始变白并让你的颜色混合服从subtractive color model。最简单的方法是在 C dst 上定义混合 C brush 的结果:
C = C brush × C dst
请注意,使用此公式,混合黄色(1,1,0)和青色(0,1,1)的结果为绿色(0,1,0),这是您所期望的。
有一个在边缘渐弱的画笔使事情略微复杂化。假设你现在有一个画笔不透明度值一个画笔 - 其中一个画笔 是1,你想要你的画笔要以全强度混合的颜色,并且 A brush 为0,您希望保留原始颜色。现在你要找的是:
C =( C brush × C dst )× 画笔 + C dst ×(1 - A 画笔 )
由于在OpenGL ES结果中混合计算 C = C src × S + C < sub> dst × D ,如果我们进行以下替换,我们可以得到我们想要的结果:
C src = C brush × A brush < / em>的
A src = A brush
S = C dst
D =(1 - A brush )
现在让我们看看在OpenGL ES中设置它需要什么。这里有4个步骤:
将背景颜色更改为白色。
将画笔纹理更改为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);
设置 C src , A src , S < / em>和 D 。
切换到alpha纹理后,假设您的画笔颜色仍然是通过glColor4f
指定的,您会发现默认的OpenGL ES纹理环境会为您提供:
C src = C brush
A src = A brush
为了获得 C src 的 A brush 的额外乘法,你需要在纹理环境中设置自定义组合器函数,如下所示(您可以在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(了解为何会出现这种情况,请参阅第3.7.12节) OpenGL ES 1.1 specification)。将GL_OPERAND0_RGB
更改为GL_SRC_ALPHA
会将乘法中的第二项更改为我们想要的值。
要设置 S 和 D ,您需要做的就是更改混合因子(这可以在之前设置混合因子的地方完成):< / p>
glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA);
确保在画笔纹理之外对 A 画笔 的任何修改都会反映在其他频道上。
对纹理环境的上述修改仅考虑了来自画笔纹理的画笔不透明度的部分。如果您在其他地方修改Alpha通道中的画笔不透明度(即通过缩放它,如AppController
),则必须确保对其他三个通道进行相同的修改:
glColor4f(components[0] * kBrushOpacity, components[1] * kBrushOpacity, components[2] * kBrushOpacity, kBrushOpacity);
请注意,使用减色模型实现画笔的缺点是颜色只会变暗,如果不是主要的减色(青色),则重复在自身上重复绘制相同的颜色最终会导致颜色偏移,洋红色或黄色)。如果在执行此操作后,您发现颜色偏移是不可接受的,请尝试将画笔纹理更改为alpha纹理,如步骤2所示,并按如下方式更改混合因子:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
这将使您可以在白色上简单地绘制画笔颜色,但不会实际混合颜色(画笔颜色最终会覆盖背景)。