Render equirectangular environment map to cubemap

Hello!
I am try to render equirectangular environment map to cubemap, following the learnopengl in my project., but something wired happened.
At first I created a framebuffer only with a depth attachment and using

glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + index, renderID, 0);

to set each face of cubemap as color attachment. The use shader to render. But failed, cubemap is still black.

The I add a color attachment as color attachment0 for framebuffer, then again using

glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + index, renderID, 0);

to render. Now only first face of cubemap has right result, other faces is still black.


I use Nsight to check the render process, find each face had a render process but only one has right result. (For debug, I render cubemap in every frame. But alought i only render in init fuction, the result is same.)
I wonder konw why this happened? Thank you.

Some of your question is a little confusing, partly due to copy/paste errors with the code, and partly due to language translation issues. So let me ask a few questions:

Case #1

Ok. So if the code is correct (glFrameBufferTexture2D()), you’re trying to do 6 separate render passes, 1 pass per cubemap face, rendering into MIP level 0 of each face. renderID is the GL handle of that cubemap texture, and index is the loop variable that iterates from 0…5`.

(Had you used glFramebufferTexture() to attach the cubemap, you would be trying to render all of the faces of the cubemap simultaneously with Layered Rendering. But you’re not doing that, which is fine.)

Case #2

This is the copy/paste error. Your code line here is exactly the same code line as quoted above.

So which section does this code line go to? The 1st or the 2nd? And what’s the code snippet for the other section then?

I’m curious too. But we’re going to need more info on what you’re doing between case #1 and case #2.

If your cubemap+FBO setup and bind code is pure GL calls (no wrappers), then just post that in a code block, and we’ll start from there.

Thanks your kindly help and I apologize for my English as it is not my native language.
The only difference between case 1 and case 2 is whether I have created a color attachment. Beause my code is encapsulated,I can only provide this workflow as much as possible.

You can see my code in https://github.com/ProphecyQAQ/Calibur

  1. Create a framebuffer
    I create framebuffer with this
glCreateFramebuffers(1, &m_RendererID);
glBindFramebuffer(GL_FRAMEBUFFER, m_RendererID);
  1. Create Attachment

I create attachment with

glCreateTextures(GL_TEXTURE_2D, 1, id);
glBindTexture(GL_TEXTURE_2D, id);

glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, nullptr);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

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

The creation of depth attachment has same process.

  1. Create cubemap
glCreateTextures(GL_TEXTURE_CUBE_MAP, 1, &cubemapID);
glTextureStorage2D(cubemapID, 1, GL_RGB16F, width, height);

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
  1. Render to cubemap
    First binding framebuffer, binding shader. And I use a loop to render each face of cube. In loop I used
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + index, cubemapID, 0);

I think all the necessary code has been listed.

Ok.

So you’re trying to render to DEPTH cubemap texture faces via the DEPTH attachment? But:

  • Case #1: Rendering with “no” COLOR attachment, or
  • Case #2: Rendering with a “2D” texture as the COLOR attachment.

Is that right?

Questions:

On that last, see this text in:

  • Section “9.4 Framebuffer Completeness” of the OpenGL 4.6 Spec:

and of course table 8.21, at the end of:

Wait. Under “creation of depth attachment”, you just created a “cubemap”. So are you:

  1. Trying to render to a DEPTH cubemap, OR
  2. Trying to render to a COLOR cubemap?

I’m going to guess that your wording and code above is confused, and what you “really” want to do is, with each pass:

  1. Render to a COLOR cubemap texture face via the COLOR0_ATTACHMENT, and simultaneously
  2. Render to a DEPTH 2D texture via the DEPTH_ATTACHMENT.

Yes?

You asked me

Render to a COLOR cubemap texture face via the COLOR0_ATTACHMENT, and simultaneously
Render to a DEPTH 2D texture via the DEPTH_ATTACHMENT.

First point is right, I want to render to cubemap via COLOR_ATTACHMENT0. Second point is not my want to do, the depth attachment is created only bacause the completeness for framebuffer.

And I checked fbo completeness, and this is the right code for depth attachment. Apologies for the misunderstanding caused by my omission of certain var. Color and depth attachment have different format.

glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width, height, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr);

There are indeed some differences in our understanding. Let me describe my purpose. I want to render a equirectangular map to cube map, following https://learnopengl.com/PBR/IBL/Diffuse-irradiance

According to the tutorial, I need to create a framebuffer with a depth attachment because a framebuffer requires at least one attachment to ensure its completeness.

In the problem description, I mentioned that if I create a color attachment, only one face is rendered when rendering to the cubemap. But just now I found the keypoint is glDrawBuffers. If I want to create a framebuffer with color_attachment, I will call this function at the end of craetion code.

Now I create a framebuffer only with depth attachment, and in render loop I add glDrawBuffers

SetViewport();
BindVao();
BindShader();
for (index =1 ... 6) 
{
    SetViewProjection();
...
    glBindFramebuffer(GL_FRAMEBUFFER, m_RendererID);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + index, cubemap, 0);
    GLenum buffers[1] = { GL_COLOR_ATTACHMENT0 };
    glDrawBuffers(1, buffers);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    RenderFace();
}

Then I can get the same result that only one face of cubemap is rendered.

Edit:Now I finally fix this. Method is simple, I only move equirectangular texture bind operation into loop function.

Actually it’s wired, it seems like texture binding is invalid after each shader execution.

Finally I am grateful for your assistance. And layered rendering you mentioned is really a good method which I never leaned before. Thank you.