1
votes

I use OpenGL shader for apply median filter to image. Input image I copy to in_fbo buffer. All work fine.

QGLFramebufferObject *in_fbo, *out_fbo;
painter.begin(in_fbo); //Copy QImage to  QGLFramebufferObject
painter.drawImage(0,0,image_in,0,0,width,height);
painter.end();

out_fbo->bind();
glViewport( 0, 0, nWidth, nHeight );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();

glOrtho( 0.0, nWidth, 0.0, nHeight, -1.0, 1.0 );

glMatrixMode( GL_MODELVIEW );
glLoadIdentity( );

glEnable( GL_TEXTURE_2D );

out_fbo->drawTexture( QPointF(0.0,0.0), in_fbo->texture( ), GL_TEXTURE_2D );

But in shader code I need divide position of vertex by width and height of image, because texture coordinates are normalized to a range between 0 and 1.

How correctly calculate texture coordinates?

//vertex shader
varying vec2 pos;
void main( void )
{
    pos = gl_Vertex.xy;
    gl_Position = ftransform( );
}
//fragment shader
#extension GL_ARB_texture_rectangle : enable

uniform sampler2D texture0;
uniform int imgWidth;
uniform int imgHeight;
uniform int len;
varying vec2 pos;

#define MAX_LEN (100)

void main(){
    float v[ MAX_LEN ];

    for (int i = 0; i < len; i++) {
         vec2 posi = pos + float(i);
         posi.x = posi.x / float( imgWidth );
         posi.y = posi.y / float( imgHeight );
         v[i] = texture2D(texture0, posi).r;
    }
    //
    //.... Calculating new value
    //

    gl_FragColor = vec4( m, m, m, 1.0 );
}

Before I did it in OpenFrameworks. But shader for texture in OF does not work for texture in Qt. I suppose because OF create textures with textureTarget = GL_TEXTURE_RECTANGLE_ARB. Now the result of applying shader above isn't correct. It isn't identical with result of the old shader (there are few pixels with different colors). I don't know how modify shader above :(.

Old shaders:

//vertex
#version 120
#extension GL_ARB_texture_rectangle : enable

void main() {
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    gl_TexCoord[0] = gl_MultiTexCoord0;
    gl_FrontColor = gl_Color;
}


//fragment
#version 120
#extension GL_ARB_texture_rectangle : enable

uniform sampler2D texture0;
uniform int len; 

void main(){
    vec2 pos = gl_TexCoord[0].xy;

    pos.x = int( pos.x );       
    pos.y = int( pos.y );

    float v[ MAX_LEN ];

    for (int i=0; i<len; i++) {
        vec2 posi = pos +  i;       
        posi.x = int( posi.x + 0.5 ) + 0.5;
        posi.y = int( posi.y + 0.5 ) + 0.5;
        v[i] = texture2D(texture0, posi).r;
    }
    //
    //.... Calculating new value
    //       
    gl_FragColor = vec4( m, m, m, 1.0 );
}

OpenGL code from OpenFrameworks lib

texData.width = w;
texData.height = h;

texData.tex_w = w;
texData.tex_h = h;
texData.textureTarget = GL_TEXTURE_RECTANGLE_ARB;

texData.bFlipTexture = true;
texData.glType = GL_RGBA;

// create & setup FBO
glGenFramebuffersEXT(1, &fbo);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);

// Create the render buffer for depth
glGenRenderbuffersEXT(1, &depthBuffer);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthBuffer);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, texData.tex_w, texData.tex_h);

// create & setup texture
glGenTextures(1, (GLuint *)(&texData.textureID));   // could be more then one, but for now, just one
glBindTexture(texData.textureTarget, (GLuint)(texData.textureID));
glTexParameterf(texData.textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(texData.textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameterf(texData.textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(texData.textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(texData.textureTarget, 0, texData.glType, texData.tex_w, texData.tex_h, 0, texData.glType, GL_UNSIGNED_BYTE, 0);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

// attach it to the FBO so we can render to it
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, texData.textureTarget, (GLuint)texData.textureID, 0);
1

1 Answers

0
votes

I do not think you actually want to use the texture's dimensions to do this. From the sounds of things this is a simple fullscreen image filter and you really just want fragment coordinates mapped into the range [0.0,1.0]. If this is the case, then gl_FragCoord.xy / viewport.xy, where viewport is a 2D uniform that defines the width and height of your viewport ought to work for your texture coordinates (in the fragment shader).

vec2 texCoord = vec2 (transformed_pos.x, transformed_pos.y) / transformed_pos.w * vec2 (0.5, 0.5) + vec2 (1.0, 1.0) may also work using the same principle -- clip-space coordinates transformed into NDC and then mapped to texture-space. This approach will not properly account for texel centers ((0.5, 0.5) rather than (0.0, 0.0)), however and can present problems when texture filtering is enabled and the wrap mode is not GL_CLAMP_TO_EDGE.