c++ - 缓冲区损坏?

标签 c++ opengl

我正在尝试在 OpenGL 中编写 UI,但在调整控件大小时遇到​​了问题。

Texture errors

当我缩小面板时,文本纹理似乎缩小或损坏,最终按钮也会这样做。这些按钮与窗口的大小无关,因此问题不在于计算大小。我打印了文本纹理的大小和按钮大小,它们在测试过程中保持一致。

所以每次我调整窗口大小时,都会发生以下情况:

onResize
    Delete TexturedRectangle object
        | Delete 9 sprites (including vertex data) used for the TexturedRectangle
        | Delete the RenderTexture
    New TexturedRectangle Object
        | Create 9 sprites (new vertex data) for the textured rectangle
        | Render the rectangle using the 9 sprites to a new RenderTexture the size of the window

我仔细检查了代码,并确保在创建新缓冲区之前删除 GPU 上的旧数据。我的缓冲区是否已损坏,或者我渲染 RenderTexture 的方式是否不正确?我检查了 glGetError() 并在运行时没有收到错误。问题可能出在 OpenGL 堆栈上吗?我无法判断问题出在哪里,因为当我调整窗口大小时按钮根本没有改变。

Sprite::Sprite() 
    : ISprite(), mVbo(0) {
    mDiffuse = ResourceCache::getSingleton().getTexture("Texture_NOTFOUND");
    createVbo();
}

Sprite::Sprite(ITexture *diffuse, FloatRect textureBounds)
    : ISprite(), mVbo(0) {
    mDiffuse = diffuse;

    if(textureBounds.x == 0 && textureBounds.y == 0) {
        mTextureBounds = diffuse->getBounds();
    } else {
        mTextureBounds = textureBounds;
    }

    createVbo();
}

Sprite::~Sprite() {
    glDeleteBuffers(1, &mVbo);
}

void Sprite::draw(IRenderTarget *target, RenderState *state) const {
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    glBindBuffer(GL_ARRAY_BUFFER, mVbo);

    switch(state->getRenderMode()) {
    default:
    case RenderState::DIFFUSE:
        mDiffuse->bindTexture();
        break;
    case RenderState::NORMAL_MAP:
        mNormalMap->bindTexture();
        break;
    case RenderState::HEIGHT_MAP:
        mHeightMap->bindTexture();
        break;
    };

    glPushMatrix();
    glTranslatef(mPosition.x, mPosition.y, mPosition.z);
    glColor4f(mColor.r, mColor.g, mColor.b, mColor.a);

    glVertexPointer(3, GL_FLOAT, sizeof(Vertex), (void*)offsetof(Vertex, x));
    glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (void*)offsetof(Vertex, tx));

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    glPopMatrix();

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);
}

void Sprite::createVbo() {
    if(mVbo != 0) {
        glDeleteBuffers(1, &mVbo);
    }

    // Generate the VBO
    glGenBuffers(1, &mVbo);
    Vector2f size = getSize();

    float texW = mDiffuse->getWidth();
    float texH = mDiffuse->getHeight();
    float srcW = size.x / texW;
    float srcH = size.y / texH;


    // Calculate the vertices
    Vertex verts[] = {{0.f, 0.f, 0.f, mTextureBounds.x / texW, mTextureBounds.y / texH},
                        {size.x, 0.f, 0.f, (mTextureBounds.x / texW) + srcW, mTextureBounds.y / texH},
                        {0.f, size.y, 0.f, mTextureBounds.x / texW, (mTextureBounds.y / texH ) + srcH},
                        {size.x, size.y, 0.f, (mTextureBounds.x / texW) + srcW, (mTextureBounds.y  / texH) + srcH}};

    int vertSize = sizeof(verts);

    // Bind the VBO
    glBindBuffer(GL_ARRAY_BUFFER, mVbo);

    // Submit the vertex data to the GPU
    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * 4, &verts[0].x, GL_STATIC_DRAW_ARB);

    // Unbind the VBO
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

重复 Sprite

RepeatingSprite::RepeatingSprite(Texture *diffuseTexture, FloatRect spriteBounds, int xRepeat, int yRepeat) 
    : ISprite(), mXRepeat(xRepeat), mYRepeat(yRepeat) {
    mVbo = 0;
    mDiffuse = diffuseTexture;
    mTextureBounds = spriteBounds;
    createVbo();
}

RepeatingSprite::~RepeatingSprite() {
    glDeleteBuffers(1, &mVbo);
}



void RepeatingSprite::draw(IRenderTarget *target, RenderState *state) const {
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    glBindBuffer(GL_ARRAY_BUFFER, mVbo);

    switch(state->getRenderMode()) {
    default:
    case RenderState::DIFFUSE:
        mDiffuse->bindTexture();
        break;
    case RenderState::NORMAL_MAP:
        mNormalMap->bindTexture();
        break;
    case RenderState::HEIGHT_MAP:
        mHeightMap->bindTexture();
        break;
    };

    glPushMatrix();
    glTranslatef(mPosition.x, mPosition.y, mPosition.z);
    glColor4f(mColor.r, mColor.g, mColor.b, mColor.a);

    glVertexPointer(3, GL_FLOAT, sizeof(Vertex), (void*)offsetof(Vertex, x));
    glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (void*)offsetof(Vertex, tx));

    glDrawArrays(GL_QUADS, 0, (mXRepeat * mYRepeat) * 4);

    glPopMatrix();

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);
}

void RepeatingSprite::createVbo() {
    int totalRepeats = mXRepeat * mYRepeat;
    float textureWidth = mDiffuse->getWidth();
    float textureHeight = mDiffuse->getHeight();
    Vertex *vertices = new Vertex[totalRepeats*4];

    int counter = 0;
    // For each sprite count, create a quad
    for(float y = 0; y < mYRepeat; y++) {
        for(float x = 0; x < mXRepeat; x++) {   

            Vertex v1 = {x * mTextureBounds.w, 
                         y * mTextureBounds.h, 0.f, 
                         mTextureBounds.x / textureWidth, 
                         mTextureBounds.y / textureHeight};

            Vertex v2 = {x * mTextureBounds.w + mTextureBounds.w, 
                         y * mTextureBounds.h, 0.f, 
                         (mTextureBounds.x / textureWidth) + (mTextureBounds.w / textureWidth), 
                         mTextureBounds.y / textureHeight};

            Vertex v3 = {x * mTextureBounds.w, 
                         y * mTextureBounds.h + mTextureBounds.h, 0.f, 
                         mTextureBounds.x / textureWidth, 
                         (mTextureBounds.y / textureHeight) + (mTextureBounds.h / textureHeight)};

            Vertex v4 = {x * mTextureBounds.w + mTextureBounds.w, 
                         y * mTextureBounds.h + mTextureBounds.h, 0.f, 
                         (mTextureBounds.x / textureWidth) + (mTextureBounds.w / textureWidth), 
                         (mTextureBounds.y / textureHeight) + (mTextureBounds.h / textureHeight)};


            vertices[counter] = v1;
            counter++;
            vertices[counter] = v2;
            counter++;
            vertices[counter] = v4;
            counter++;
            vertices[counter] = v3;
            counter++;
        }
    }

    glGenBuffers(1, &mVbo);

    glBindBuffer(GL_ARRAY_BUFFER, mVbo);

    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * (totalRepeats*4), &vertices[0].x, GL_STATIC_DRAW_ARB);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    delete[] vertices;
}

渲染纹理

RenderTexture::RenderTexture(float width, float height) {
    mWidth = width;
    mHeight = height;

    // Create the color buffer
    glGenTextures(1, &mId);
    glBindTexture(GL_TEXTURE_2D, mId);
    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_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)mWidth, (int)mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    glBindTexture(GL_TEXTURE_2D, 0);

    // Create the framebuffer
    glGenFramebuffers(1, &mFbo);
    glBindFramebuffer(GL_FRAMEBUFFER, mFbo);

    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mId, 0);

    GLenum err = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    assert(err == GL_FRAMEBUFFER_COMPLETE); // Make sure texture is valid

    glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

RenderTexture::~RenderTexture() {
    glDeleteBuffers(1, &mFbo);
    glDeleteTextures(1, &mId);
    mFbo = 0;
}


void RenderTexture::preDraw() {
    // store the glViewport and glEnable states
    glPushAttrib(GL_VIEWPORT_BIT);

    // Bind the frame buffer
    //glBindTexture(GL_TEXTURE_2D, 0);
    glBindFramebuffer(GL_FRAMEBUFFER, mFbo);

    // Save the current matrix
    glPushMatrix();
    glLoadIdentity();

    // Setup the projection matrix for the render target
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    glViewport(0, 0, (int)mWidth, (int)mHeight);
    glOrtho(0, mWidth, 0.f, mHeight, 0.f, 100.f);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    glDrawBuffer(GL_COLOR_ATTACHMENT0);
}

void RenderTexture::postDraw() {
    // Pop the render target's projection matrix off the stack
    glPopMatrix();
    // Restore previouse projection matrix
    glPopMatrix();
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    // Restore the previous viewport settings
    glPopAttrib();
}

最佳答案

在 OpenGL 中,当您对某个对象应用某种变换并看到其他对象受到影响时,最好从变换和堆栈操作逻辑开始寻找。

所以在 RenderTexture::preDraw() 中你有:

glPushMatrix();
// ...
glMatrixMode(GL_PROJECTION);
glPushMatrix();

以及在RenderTexture::postDraw()中:

glPopMatrix();
// Restore previouse projection matrix
glPopMatrix();

它们之间没有任何对 glMatrixMode() 的调用。

这样就无法正常工作了。每个矩阵模式都有自己的堆栈,因此第二个 glPopMatrix() 从错误的堆栈中弹出。

您需要执行以下操作:

glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();

关于c++ - 缓冲区损坏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10606902/

相关文章:

c++ - 在工厂中返回 unique_ptr

c++ - 在 Visual Studio 2013 for C++ 中调试时的错误代码

c++ - 开罗矩阵与 GlOrtho 矩阵等效?

opengl - GLSL 从 Sampler3d 纹理读取

c++ - begin() 和 end() 函数不应该是模板类 Vector 的成员函数吗?

c++ - 是否存在两次包含相同 header 实际上有帮助的情况?

c++ - 浮点运算

java - OpenGL 中透明对象中的三角形/线条

java - GLSL 顶点着色器未编译

c++ - glReadPixels 返回不正确的值