OpenGL / GLSL:渲染到立方体贴图?


13

我试图弄清楚如何将场景渲染为立方体贴图。我在这个问题上停留了一段时间,以为我会向你们寻求帮助。我是OpenGL的新手,这是我第一次使用FBO。

我目前有一个使用cubemap bmp文件的工作示例,片段着色器中的samplerCube样本类型附加到GL_TEXTURE1。我根本没有更改着色器代码。我只是改变了一个事实,即我不会调用正在加载cubemap bmp文件的函数,而是尝试使用以下代码来渲染到cubemap。

您可以在下面看到我还将纹理再次附加到GL_TEXTURE1。在设置制服时是这样的:

glUniform1i(getUniLoc(myProg, "Cubemap"), 1);

它可以通过在我的片段着色器中访问它uniform samplerCube Cubemap

我这样调用以下函数:

cubeMapTexture = renderToCubeMap(150, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE);

现在,我意识到在下面的绘制循环中,我没有更改视图方向来向下看+ x,-x,+ y,-y,+ z,-z轴。我真的只是想在实现之前先看到一些东西。我认为我至少应该像现在的代码一样在对象上看到一些东西。

我什么也没看见,只是黑色。我将背景设为白色,但物体仍为黑色。我删除了照明和着色,仅对立方体贴图纹理进行采样,并且仍然为黑色。

我认为问题可能是设置纹理时的格式类型,即GL_RGB8,GL_RGBA,但我也尝试过:

GL_RGBA,GL_RGBA GL_RGB,GL_RGB

我认为这是标准的,因为我们正在渲染附加到帧缓冲区的纹理,但是我看到了使用不同枚举值的不同示例。

我还尝试在每个要使用立方体贴图的绘制调用中绑定立方体贴图纹理:

glBindTexture(GL_TEXTURE_CUBE_MAP, cubeMapTexture);

另外,我没有为大多数示例中看到的FBO创建深度缓冲区,因为我只想为我的立方体贴图使用颜色缓冲区。我实际上添加了一个,看是否是问题所在,但仍然得到了相同的结果。当我尝试时,我可能会捏造出来。

可以为我指明正确方向的任何帮助将不胜感激。

GLuint renderToCubeMap(int size, GLenum InternalFormat, GLenum Format, GLenum Type)
    {

    // color cube map
    GLuint textureObject;
    int face;
    GLenum status;

    //glEnable(GL_TEXTURE_2D);
    glActiveTexture(GL_TEXTURE1);
    glGenTextures(1, &textureObject);
    glBindTexture(GL_TEXTURE_CUBE_MAP, textureObject);
    glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

    for (face = 0; face < 6; face++) {
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, InternalFormat, size, size, 0, Format, Type, NULL);
    }

    // framebuffer object
    glGenFramebuffers(1, &fbo);
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);

    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X, textureObject, 0);

    status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    printf("%d\"\n", status);
        printf("%d\n", GL_FRAMEBUFFER_COMPLETE);

    glViewport(0,0,size, size);

    for (face = 1; face < 6; face++) {

        drawSpheres();
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, textureObject, 0);

    }

     //Bind 0, which means render to back buffer, as a result, fb is unbound
       glBindFramebuffer(GL_FRAMEBUFFER, 0);

       return textureObject;
    }

您是否测试过您的drawSpheres函数实际上绘制了可见的东西?该函数是否实际绘制了东西?如果更改drawSpheres为仅清除帧缓冲区会怎样?
Nicol Bolas

是。我正在做两遍。上面的代码之一,实际上是上面的6个调用。然后我在渲染到帧缓冲区0时调用drawSpheres,它的确显示了。
乔伊·格林

另外,我将背景设置为白色。白色至少不会出现在纹理中吗?
乔伊·格林

您的代码对正常的FBO正常吗?我的理解是,一个立方体贴图应该仅仅是6个纹理,并且你必须使每个单独..
杰瑞Komppa

Answers:


10

好吧,我不能保证这会帮助您弄清楚发生了什么。您只是没有发布足够的信息以了解要追踪任何特定错误的操作。尽管我可以很快纠正您的一件事:

glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X, textureObject, 0);

...

for (face = 1; face < 6; face++) {
    drawSpheres();
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, textureObject, 0);
}

这只会调用drawSpheres 五次。我猜你想叫它6次。

但是我可以发布一个有效的答案。请注意,此代码旨在与我的教程系列一起运行,因此它引用了不存在的代码。但这主要是诸如创建网格之类的事情。没有什么真正重要的。

这是要点。主球体对象的着色器。

顶点着色器:

#version 330

layout(std140) uniform;

layout(location = 0) in vec4 position;
layout(location = 2) in vec3 normal;

out vec3 modelSpaceNormal;

uniform Projection
{
    mat4 cameraToClipMatrix;
};

uniform mat4 modelToCameraMatrix;

void main()
{
    gl_Position = cameraToClipMatrix * (modelToCameraMatrix * position);
    modelSpaceNormal = normal;
}

片段着色器:

#version 330

in vec3 modelSpaceNormal;

uniform samplerCube cubeTexture;

out vec4 outputColor;

void main()
{
    outputColor = texture(cubeTexture, modelSpaceNormal);
//  outputColor = vec4(normalize(modelSpaceNormal), 1.0);
}

创建将用作渲染目标的立方体贴图纹理:

void CreateCubeTexture()
{
    glGenTextures(1, &g_cubeTexture);
    glBindTexture(GL_TEXTURE_CUBE_MAP, g_cubeTexture);

    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 0);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, 0);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    std::vector<GLubyte> testData(CUBE_TEXTURE_SIZE * CUBE_TEXTURE_SIZE * 256, 128);
    std::vector<GLubyte> xData(CUBE_TEXTURE_SIZE * CUBE_TEXTURE_SIZE * 256, 255);

    for(int loop = 0; loop < 6; ++loop)
    {
        if(loop)
        {
            glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + loop, 0, GL_RGBA8,
                CUBE_TEXTURE_SIZE, CUBE_TEXTURE_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, &testData[0]);
        }
        else
        {
            glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + loop, 0, GL_RGBA8,
                CUBE_TEXTURE_SIZE, CUBE_TEXTURE_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, &xData[0]);
        }
    }

    glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}

我实际上是用数据填充纹理(而不是将NULL传递给glTexImage2D)作为调试辅助。它可以确保在开始使用纹理作为渲染目标之前一切正常。

另外,请注意,我提供了BASE_LEVEL和MAX_LEVEL。创建后,我总是立即用我的纹理进行处理。这是一个好习惯,因为OpenGL有时可能对纹理完整性和mipmap金字塔有些挑剔。我没有记住规则,而是虔诚地将它们设置为正确的值。

这是主要的绘图功能:

void display()
{
    //Draw the cubemap.
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, g_framebuffer);
    glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, g_depthbuffer);

    for(int loop = 0; loop < 6; ++loop)
        DrawFace(loop);

    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);

    //Draw the main scene.
    //The projection matrix is in a uniform buffer.
    ProjectionBlock projData;
    projData.cameraToClipMatrix = glm::perspective(90.0f,
        (g_viewportSize.x / (float)g_viewportSize.y), g_fzNear, g_fzFar);

    glBindBuffer(GL_UNIFORM_BUFFER, g_projectionUniformBuffer);
    glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(ProjectionBlock), &projData);
    glBindBuffer(GL_UNIFORM_BUFFER, 0);

    glViewport(0, 0, (GLsizei)g_viewportSize.x, (GLsizei)g_viewportSize.y);

    glClearColor(0.75f, 0.75f, 1.0f, 1.0f);
    glClearDepth(1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glutil::MatrixStack modelMatrix;
    modelMatrix.ApplyMatrix(g_viewPole.CalcMatrix());

    if(g_pSphere)
    {
        glutil::PushStack push(modelMatrix);

        glUseProgram(g_progMain.theProgram);
        glUniformMatrix4fv(g_progMain.modelToCameraMatrixUnif, 1, GL_FALSE,
            glm::value_ptr(modelMatrix.Top()));

        glActiveTexture(GL_TEXTURE0 + g_cubeTexUnit);
        glBindTexture(GL_TEXTURE_CUBE_MAP, g_cubeTexture);

        g_pSphere->Render("lit");

        glBindTexture(GL_TEXTURE_CUBE_MAP, 0);

        glUseProgram(0);
    }

    glutPostRedisplay();
    glutSwapBuffers();
}

这参考了DrawFace,它绘制了立方体贴图的给定面。实现如下:

void DrawFace(int iFace)
{
    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
        GL_TEXTURE_CUBE_MAP_POSITIVE_X + iFace, g_cubeTexture, 0);

    GLenum status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
    if(status != GL_FRAMEBUFFER_COMPLETE)
        printf("Status error: %08x\n", status);

    //The projection matrix is in a uniform buffer.
    ProjectionBlock projData;
    projData.cameraToClipMatrix = glm::perspective(90.0f, 1.0f, g_fzNear, g_fzFar);

    glBindBuffer(GL_UNIFORM_BUFFER, g_projectionUniformBuffer);
    glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(ProjectionBlock), &projData);
    glBindBuffer(GL_UNIFORM_BUFFER, 0);

    glViewport(0, 0, (GLsizei)CUBE_TEXTURE_SIZE, (GLsizei)CUBE_TEXTURE_SIZE);

    const glm::vec4 &faceColor = g_faceColors[iFace];
    glClearColor(faceColor.x, faceColor.y, faceColor.z, faceColor.w);
    glClearDepth(1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    if(g_pSphere)
    {
        glutil::MatrixStack modelMatrix;
        modelMatrix.Translate(g_faceSphereLocs[iFace]);

        glUseProgram(g_progUnlit.theProgram);
        glUniformMatrix4fv(g_progUnlit.modelToCameraMatrixUnif, 1, GL_FALSE,
            glm::value_ptr(modelMatrix.Top()));

        const glm::vec4 &sphereColor = g_faceSphereColors[iFace];
        glUniform4fv(g_progUnlit.objectColorUnif, 1, glm::value_ptr(sphereColor));

        glActiveTexture(GL_TEXTURE0 + g_cubeTexUnit);
        glBindTexture(GL_TEXTURE_CUBE_MAP, g_cubeTexture);

        g_pSphere->Render("flat");

        glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
        glUseProgram(0);
    }
}

此函数引用了一组全局表,我使用这些全局表为每张脸赋予不同的背景色,不同的球体颜色,并为该脸部正确地放置球体(在摄影机空间中)。

这些的要点DrawFace是这些。

通常,除非我对状态已设置有一定的了解,否则我将设置该状态。每次调用时,都会设置视口DrawFace。我每次都设置投影矩阵。这些都是多余的;我可以将它们重新设置在display调用循环之前DrawFace,就像对当前FBO和深度渲染缓冲区所做的那样。

但是我还要清除缓冲区,每个缓冲区都不同(因为每个面都有不同的颜色)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.