开发者

Scaling a texture with a Framebuffer

My goal is to be able to scale textures when they are loaded, so I don't have to do it on every frame the sprite gets rendered. I figured the best method would be to render the scaled texture onto another texture, basically caching it. However, with the following code, I only get red quads (due to the glClearColor) so I know that the FBO is working, just not my method for rendering the new texture

Texture *Graphics::loadTexture(const std::string& filename, int scale = 0) {
SDL_Surface *surface;
GLuint texture;

if((surface = IMG_Load(filename.c_str()))) {
    // Get the number of colors
    GLint numberOfColors = surface->format->BytesPerPixel;
    GLenum format;

    // Set the format of the texture based on the number of channels
    if(numberOfColors == 4) {
            if(surface->format-&g开发者_如何学运维t;Rmask == 0x000000ff) {
                format = GL_RGBA;
            } else {
                format = GL_BGRA;
            }
    } else if(numberOfColors == 3) {
        if(surface->format->Rmask == 0x000000FF) {
            format = GL_RGB;
        } else {
            format = GL_BGR;
        }
    } else {
        throw Exception("Invalid image type for image  " + filename); 
    }

    // Generate texture id
    glGenTextures(1, &texture);

    // Bind the texture
    glBindTexture(GL_TEXTURE_2D, texture);

    // Texture stretching properties
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    // Create the image
    glTexImage2D(GL_TEXTURE_2D, 0, 4, surface->w, surface->h, 
                0, format, GL_UNSIGNED_BYTE, surface->pixels);

    glBindTexture(GL_TEXTURE_2D, 0);

} else {
    return NULL;
}
Texture *result;

if(scale > 1) {
    GLuint scaledTexture;
    GLuint fbo;
    GLuint fbod;

    // First we setup the depth buffer //
    // Create the framebuffer
    glGenRenderbuffersEXT(1, &fbod);

    // Bind the render buffer
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbod);

    // Set the render buffer storage to be a depth component
    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, surface->w*scale, surface->h*scale);

    // Set the render buffer of this buffer to the depth buffer
    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbod);

    // Unbind the render buffer
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);

    // Next we setup the texture //
    glGenTextures(1, &scaledTexture);
    glBindTexture(GL_TEXTURE_2D, scaledTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surface->w*scale, surface->h*scale, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    glBindTexture(GL_TEXTURE_2D, 0);

    // Setup the frame buffer //
    glGenFramebuffersEXT(1, &fbo);
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);

    // Attach the texture and render buffer to the frame buffer
    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D,  scaledTexture, 0);

    // Attach the depth buffer
    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbod);

    glPushAttrib(GL_VIEWPORT_BIT | GL_ENABLE_BIT);
    glViewport(0, 0, surface->w*scale, surface->h*scale);

    glLoadIdentity();

    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glBindTexture(GL_TEXTURE_2D, texture);

    glPushMatrix();

    glBegin(GL_QUADS);
        glTexCoord2i( 0, 0 );
        glVertex3f( 0.f, 0.f, 0.0f );

        glTexCoord2i( 1, 0 );
        glVertex3f( (GLfloat)surface->w*scale, 0.0f, 0.0f );

        glTexCoord2i( 1, 1 );
        glVertex3f( (GLfloat)surface->w*scale, (GLfloat)surface->h*scale, 0.f );

        glTexCoord2i( 0, 1 );
        glVertex3f( 0.0f, (GLfloat)surface->h*scale, 0.f );
    glEnd();

    glPopMatrix();


    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

    glPopAttrib();
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

    result = new Texture(scaledTexture, surface->w, surface->h);
} else {
    result = new Texture(texture, surface->w, surface->h);
}

//Texture *result = new Texture(texture, surface->w, surface->h);

if(surface) {
    SDL_FreeSurface(surface);
}

return result;
}


Several comments :

  • When you do your glLoadIdentity(), do you know which matrix you are resetting ? Use glMatrixMode.
  • Your glPushMatrix()/glPopMatrix() are useless since you don't modify the matrix inbetween.
  • Are you sure 2D textures are enabled ? Use glEnable(GL_TEXTURE_2D);

But anyway, clear both matrices and you should see your texture in the upper right part of the screen (or whatever polygon on which you're using the generated texture) ; you'll just have to fix the UVs et Graphics::loadTexture :

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

This should work, but I think you should do the scaling in software.


Okay, what I decided to do was instead of implementing scaling at the time of loading the texture I implemented it in the sprite object. So when I submit the vertex data to the GPU, it scales the sprite by it's verts instead of scaling the actual texture.


If not trying to tell you what to do, but instead to tell you the simplest way to do what you indent to do (scale the texture) - the answer is:

glBlitFramebuffer(sx0,sy0,sx1,sy1,dx0,dy0,dx1,dy1, GL_COLOR_BUFFER_BIT, GL_LINEAR)

It will automatically scale the read image if the size doesn't match In order to make it work you should setup GL_READ_FRAMEBUFFER, GL_DRAW_FRAMEBUFFER and glDrawBuffer() for each of them.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜