2013-07-18 2 views
1

J'ai développé l'application iOS qui applique le flou sur le toucher du doigt sur l'image. J'utilise OpenGL pour ça. J'ai écrit le vertex & Fragment shader pour appliquer le flou gaussien. Quand je passe des coordonnées entières de rectangle d'image (0-1) dans le vertex shader, il applique le flou à l'image entière sans aucun problème. Maintenant, la même chose que j'essaie de faire au toucher du doigt. Je capture des points de contact, les convertis en gamme 0-1 et passe ce point dans les shaders. Mais il ne brouille pas et perturbe plutôt l'image originale. est le code principal ci-dessous qui est exécuté @ contact déplacé:OpenGL iOS - appliquer le flou sur le doigt tactile sur l'image ne fonctionne pas

-(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]; 
} 

Et ci-dessous sont mes vertex et fragment shaders: Vertex Shader (flou horizontal):

/* 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); 

} 

Vertex Shader (vertical Blur)

 /* 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); 

} 

Fragment Shader:

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); 

}

+0

http://stackoverflow.com/questions/14107979/how-can--make-specific-part-blur-of-an-image-rectangular-circular – iPatel

+0

@David Merci! – engineer

+0

@iPatel - Merci de l'avoir signalé. Je l'ai visité plus tôt, mais je veux le faire avec OpenGLES seulement, pour des raisons de performance. – engineer

Répondre

0

Pour continuer des commentaires:

L'approche que vous prenez ne semble pas très bon du tout. Fixer les 2 choses à partir des commentaires produira toujours d'autres problèmes. Penser à 1 c'est ce qui se passe si l'utilisateur traîne 2 lignes au même endroit? Est-ce que l'image sera floue deux fois à cet endroit? Il pourrait mais je peux vous assurer qu'il va perdre sa fonctionnalité.

Pour que les choses soient un peu plus générales sur ce que vous essayez de faire: Vous voulez que l'utilisateur puisse sélectionner des parties de l'image à brouiller, ce qui est exactement ce que vous devez faire. Il en résulte que vous avez besoin d'un autre tampon qui stockera uniquement ces données, que le pixel soit flou ou non (peut également stocker la force du flou). Donc, en général, je suggère de créer un objet FBO (frame buffer object) avec une texture attachée. Puis déplacez le code de dessin que vous avez déjà (avec des points) pour dessiner une couleur spécifique sur ce tampon. Maintenant, chaque fois que le FBO change, vous devez redessiner votre tampon principal entier en utilisant les 2 textures (original et FBO) dessin original où la texture de FBO est vide et l'image floue où la texture est pleine. Cette procédure suggère également que vous flouez plutôt toute l'image en temps de chargement et utilisez 3 textures (original, flou, canvas).

L'approche FBO est très flexible et dans le cas mentionné, vous pouvez l'utiliser pour beaucoup plus de lieux de flou. Vous pouvez avoir plusieurs effets et leur force en utilisant des canaux de couleur différents et est indépendant des ressources que vous utilisez dans votre tampon principal. D'un autre côté, cela peut être un peu difficile à implémenter: la taille FBO devra être une puissance de 2 et vous devrez utiliser seulement une certaine partie de celle-ci (pas de problème), appliquer sa texture au buffer principal vous forcera pour calculer un nouveau tampon de coordonnées de texture (problème mathématique moyen), si vous appliquez un peu de zoom et que vous vous déplacez, les choses peuvent devenir très difficiles (gros problème). Devinez de votre cas, vous pouvez le faire beaucoup plus facile. Au lieu de dessiner quelques couleurs sur le FBO, vous pouvez simplement utiliser un autre tampon qui peut être attaché à votre frame buffer principal: Dessiner seulement dans le tampon de profondeur ou plutôt dans le stencil (peu de configuration) puisque vous ne les utilisez probablement pas du tout. Maintenant, pour le grand enfin, vous probablement utilisez même pas le canal alpha sur votre tampon:

  • quand une nouvelle image est définie créer une texture originale et floue, effacer la mémoire tampon à zéro pour tous les canaux, désactivez mélange et l'utilisation Masque couleur uniquement RVB (glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE)) et dessinez l'original comme vous l'avez fait auparavant.
  • On touche déplacé mélange désactivé, activé uniquement canal alpha et utiliser une couleur avec une valeur alpha 1.0 et réutiliser le code de dessin que vous avez déjà, sauf que vous ne devez dessiner la couleur, pas une texture floue
  • Le rafraîchissement (après touches déplacées) activer les canaux RVB uniquement, activer le fondu et utiliser glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA) et dessiner une texture floue en plein écran.

Tout est fait, juste une petite note ici: Si vous à un moment donné veulent un instantané de l'image et vous utiliserez glReadPixels vous devez comprendre que votre canal alpha est un gâchis et vous avez besoin de sauter lors de la création d'un CGImage à partir de ces données.

+0

Merci pour l'explication si détaillée. Mais étant débutant dans OpenGL ES, il me semble difficile à mettre en œuvre ci-dessus à première vue. Quoi qu'il en soit, laissez-moi essayer avec votre suggestion et je resterai au courant de mes progrès. – engineer

+0

J'ai essayé de mettre votre suggestion dans le code mais je n'ai pas réussi. Pouvez-vous pointer vers un exemple de code ou extrait de code? – engineer

Questions connexes