我一直在开发iOS应用程序,该应用程序在图像上应用手指触摸模糊。为此,我使用OpenGL。我已经写了顶点&片段着色器以应用高斯模糊。当我将整个图像的矩形坐标(0-1)传递到顶点着色器时,它会将模糊应用到整个图像,而不会出现任何问题。现在,我正试图在手指触摸上做同样的事情。我捕捉触摸点,转换为0-1范围,并将该点传递到着色器中。但它并没有模糊,反而干扰了原始图像。以下是在触摸移动时执行的主要代码:
-(void)setupVBOsBlur:(CGPoint)start For:(CGPoint)end
{
static GLfloat* vertexBuffer = NULL;
static NSUInteger vertexMax = 64;
NSUInteger vertexCount = 0,
count,
i;
// Convert locations from Points to Pixels
CGFloat scale = self.contentScaleFactor;
start.x *= scale;
start.y *= scale;
end.x *= scale;
end.y *= scale;
// Allocate vertex array buffer
if(vertexBuffer == NULL)
vertexBuffer = malloc(vertexMax * 2 * sizeof(GLfloat));
// Add points to the buffer so there are drawing points every X pixels
count = MAX(ceilf(sqrtf((end.x - start.x) * (end.x - start.x) + (end.y - start.y) * (end.y - start.y))) , 1);
for(i = 0; i < count; ++i) {
if(vertexCount == vertexMax) {
vertexMax = 2 * vertexMax;
vertexBuffer = realloc(vertexBuffer, vertexMax * 2 * sizeof(GLfloat));
}
vertexBuffer[2 * vertexCount + 0] = start.x + (end.x - start.x) * ((GLfloat)i / (GLfloat)count);
vertexBuffer[2 * vertexCount + 1] = start.y + (end.y - start.y) * ((GLfloat)i / (GLfloat)count);
vertexCount += 1;
}
GLuint vb;
glGenBuffers(1, &vb);
glBindBuffer(GL_ARRAY_BUFFER, vb);
glBufferData(GL_ARRAY_BUFFER, vertexCount*2*sizeof(GLfloat), vertexBuffer, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(ATTRIB_VERTEX);
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, GL_FALSE, 0, 0);
GLKMatrix4 projectionMatrix = GLKMatrix4MakeOrtho(0, backingWidth, 0, backingHeight, -1, 1);
GLKMatrix4 modelViewMatrix = GLKMatrix4Identity; // this sample uses a constant identity modelView matrix
mvpMatrix = GLKMatrix4Multiply(projectionMatrix, modelViewMatrix);
[self compileShadersForFingerBlur];
//set MVP
glUniformMatrix4fv(mvpMatrixSlot, 1, GL_FALSE, mvpMatrix.m);
//glUniform2f(myTextCoordSlot, start.x/320.0, ( self.bounds.size.height - start.y)/480.0);
glUniform2f(myTextCoordSlot, start.x/320.0, ( self.bounds.size.height - start.y)/480.0);
glUniform1i(amount, 0);
/*
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureHandle);
glUniform1i(textureUniformSlot, 0);
*/
glEnable(GL_POINT_SMOOTH);
glEnable(GL_BLEND);
glDrawArrays(GL_POINTS, 0, vertexCount);
[eaglContext presentRenderbuffer:GL_RENDERBUFFER];
}
下面是我的顶点和片段着色器:顶点着色器(水平模糊):
/* HBlurVertexShader.glsl */
attribute vec4 Position;
uniform mat4 MVP;
uniform vec2 myTextCoord;
varying vec2 v_texCoord;
varying vec2 v_blurTexCoords[14];
void main()
{
gl_PointSize = 5.0;
gl_Position = MVP * Position;
v_texCoord = myTextCoord;
v_blurTexCoords[ 0] = v_texCoord + vec2(-0.028, 0.0);
v_blurTexCoords[ 1] = v_texCoord + vec2(-0.024, 0.0);
v_blurTexCoords[ 2] = v_texCoord + vec2(-0.020, 0.0);
v_blurTexCoords[ 3] = v_texCoord + vec2(-0.016, 0.0);
v_blurTexCoords[ 4] = v_texCoord + vec2(-0.012, 0.0);
v_blurTexCoords[ 5] = v_texCoord + vec2(-0.008, 0.0);
v_blurTexCoords[ 6] = v_texCoord + vec2(-0.004, 0.0);
v_blurTexCoords[ 7] = v_texCoord + vec2( 0.004, 0.0);
v_blurTexCoords[ 8] = v_texCoord + vec2( 0.008, 0.0);
v_blurTexCoords[ 9] = v_texCoord + vec2( 0.012, 0.0);
v_blurTexCoords[10] = v_texCoord + vec2( 0.016, 0.0);
v_blurTexCoords[11] = v_texCoord + vec2( 0.020, 0.0);
v_blurTexCoords[12] = v_texCoord + vec2( 0.024, 0.0);
v_blurTexCoords[13] = v_texCoord + vec2( 0.028, 0.0);
}
顶点着色器(垂直模糊)
/* VBlurVertexShader.glsl */
varying vec2 v_texCoord;
varying vec2 v_blurTexCoords[14];
void main()
{
v_blurTexCoords[ 0] = v_texCoord + vec2(0.0, -0.028);
v_blurTexCoords[ 1] = v_texCoord + vec2(0.0, -0.024);
v_blurTexCoords[ 2] = v_texCoord + vec2(0.0, -0.020);
v_blurTexCoords[ 3] = v_texCoord + vec2(0.0, -0.016);
v_blurTexCoords[ 4] = v_texCoord + vec2(0.0, -0.012);
v_blurTexCoords[ 5] = v_texCoord + vec2(0.0, -0.008);
v_blurTexCoords[ 6] = v_texCoord + vec2(0.0, -0.004);
v_blurTexCoords[ 7] = v_texCoord + vec2(0.0, 0.004);
v_blurTexCoords[ 8] = v_texCoord + vec2(0.0, 0.008);
v_blurTexCoords[ 9] = v_texCoord + vec2(0.0, 0.012);
v_blurTexCoords[10] = v_texCoord + vec2(0.0, 0.016);
v_blurTexCoords[11] = v_texCoord + vec2(0.0, 0.020);
v_blurTexCoords[12] = v_texCoord + vec2(0.0, 0.024);
v_blurTexCoords[13] = v_texCoord + vec2(0.0, 0.028);
}
片段着色器:
precision mediump float;
uniform sampler2D texture;
uniform int amount;
varying vec2 v_texCoord;
varying vec2 v_blurTexCoords[14];
void main()
{
gl_FragColor = vec4(0.0);
if(amount > 6)
{
gl_FragColor += texture2D(texture, v_blurTexCoords[ 0])*0.0044299121055113265;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 1])*0.00895781211794;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 2])*0.0215963866053;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 3])*0.0443683338718;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 4])*0.0776744219933;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 5])*0.115876621105;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 6])*0.147308056121;
gl_FragColor += texture2D(texture, v_texCoord)*0.159576912161;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 7])*0.147308056121;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 8])*0.115876621105;
gl_FragColor += texture2D(texture, v_blurTexCoords[ 9])*0.0776744219933;
gl_FragColor += texture2D(texture, v_blurTexCoords[10])*0.0443683338718;
gl_FragColor += texture2D(texture, v_blurTexCoords[11])*0.0215963866053;
gl_FragColor += texture2D(texture, v_blurTexCoords[12])*0.00895781211794;
gl_FragColor += texture2D(texture, v_blurTexCoords[13])*0.0044299121055113265;
}
else
gl_FragColor = texture2D(texture, v_texCoord);
}
从注释继续:
你所采取的方法似乎一点也不好。从评论中修复这两件事仍然会产生其他问题。想想1,如果用户在同一个地方拖动两条线会发生什么?图像会在那个地方模糊两次吗?它可能会,但我可以向你保证,它将失去其功能。
为了更全面地了解你正在尝试做的事情:你希望用户能够选择图像中要模糊的部分,这正是你应该做的。这导致你需要另一个缓冲区,只存储这些数据,无论像素是否模糊(也可以存储模糊的强度)。所以一般来说,我建议创建一个带有附加纹理的FBO(帧缓冲区对象)。然后移动已有的图形代码(带点),在该缓冲区上绘制特定颜色。现在,每次FBO更改时,您都需要使用2个纹理(原始纹理和FBO纹理)重新绘制整个主缓冲区,绘制FBO纹理为空的原始纹理和纹理为满的模糊图像。此过程还建议您在加载时模糊整个图像,并使用3种纹理(原始、模糊、画布)。
FBO方法非常灵活,在上述情况下,您可以将其用于更多模糊位置。使用不同的颜色通道可以获得多种效果及其强度,并且与主缓冲区中使用的资源无关。另一方面,它可能会变得有点难以实现:FBO大小必须是2的幂,并且你只需要使用它的某一部分(不是问题),将其纹理应用于主缓冲区将迫使你计算一个新的纹理坐标缓冲区(中等数学问题),如果你应用一些缩放和移动,事情可能会变得非常困难(大问题)。根据你的情况猜测,你可以更容易地做到这一点。
不用在FBO上绘制一些颜色,你可以使用其他可以附加到主帧缓冲区的缓冲区:只绘制到深度缓冲区,或者更确切地说是模具缓冲区(小设置),因为你可能根本不使用它们。现在,对于grand,你可能甚至不使用缓冲区上的alpha通道:
- 当新图像设置为创建原始和模糊纹理时,将所有通道的缓冲区清除为零,禁用混合并使用仅RGB(
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE)
)的颜色遮罩,然后像以前一样绘制原始图像 - 在移动触摸时,禁用混合,仅启用alpha通道并使用alpha值为
1.0
的颜色,并重复使用现有的绘图代码,除非您应该只绘制颜色,而不是模糊的纹理 - 刷新时(移动触摸后)仅启用RGB通道,启用混合并使用
glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA)
并绘制全屏模糊纹理
所有这些都完成了,这里只需要一个小提示:如果你在某个时候想要一个图像快照,并且你将使用glReadPixels
,你需要明白你的alpha通道是一团糟,当你从这些数据创建CGImage时,你需要跳过它。