使用帧缓冲区缩放纹理

Posted

技术标签:

【中文标题】使用帧缓冲区缩放纹理【英文标题】:Scaling a texture with a Framebuffer 【发布时间】:2011-02-09 06:59:55 【问题描述】:

我的目标是能够在加载纹理时缩放纹理,因此我不必在渲染精灵的每一帧上都这样做。我认为最好的方法是将缩放的纹理渲染到另一个纹理上,基本上是缓存它。但是,使用以下代码,我只得到红色四边形(由于 glClearColor)所以我知道 FBO 正在工作,而不是我渲染新纹理的方法

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

【问题讨论】:

嘘……还有glVertex2f ;) 你想获得什么好处? 【参考方案1】:

几个cmets:

当您执行 glLoadIdentity() 时,您知道要重置哪个矩阵吗?使用 glMatrixMode。 你的 glPushMatrix()/glPopMatrix() 没用,因为你没有修改中间的矩阵。 您确定启用了 2D 纹理吗?使用 glEnable(GL_TEXTURE_2D);

但是无论如何,清除两个矩阵,您应该会在屏幕的右上方看到您的纹理(或者您正在使用生成的纹理的任何多边形);你只需要修复 UVs et Graphics::loadTexture :

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

这应该可行,但我认为您应该在软件中进行缩放。

【讨论】:

【参考方案2】:

好的,我决定做的不是在加载纹理时实现缩放,而是在精灵对象中实现它。因此,当我将顶点数据提交给 GPU 时,它会通过它的顶点来缩放精灵,而不是缩放实际的纹理。

【讨论】:

【参考方案3】:

如果不是试图告诉你该怎么做,而是告诉你最简单的方法来做你缩进要做的事情(缩放纹理) - 答案是:

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

如果大小不匹配,它将自动缩放读取的图像 为了使其正常工作,您应该为它们中的每一个设置 GL_READ_FRAMEBUFFER、GL_DRAW_FRAMEBUFFER 和 glDrawBuffer()。

【讨论】:

以上是关于使用帧缓冲区缩放纹理的主要内容,如果未能解决你的问题,请参考以下文章

尝试使用帧缓冲区渲染到纹理总是会导致白色纹理

使用帧缓冲区将深度缓冲区渲染到纹理中

OpenGL 帧缓冲区 - 渲染到纹理创建带有主帧缓冲区内容的纹理

JOGL 和帧缓冲区渲染到纹理的问题:无效帧缓冲区操作错误

为啥从帧缓冲区创建的纹理没有正确映射

使用帧缓冲区对象作为纹理的视觉问题