-4

Here is a a minimal example causing GL to render a black screen:

// ... all the usual GL init code etc. & GLAM includes

const int SHADOW_SIZE = 1024;
const int SWIDTH = 1200, SHEIGHT = 900;
unsigned int shadowMap, albedoSpec;

// Begin snip 1
glGenTextures(1, &shadowMap);
glBindTexture(GL_TEXTURE_CUBE_MAP, shadowMap);
for (unsigned int i = 0; i < 6; ++i)
    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT, SHADOW_SIZE, SHADOW_SIZE, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
// End snip 1

glGenTextures(1, &gAlbedoSpec);
glBindTexture(GL_TEXTURE_2D, gAlbedoSpec);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SWIDTH, SHEIGHT, 0, GL_RGBA, GL_FLOAT, NULL);


// ... some time later, in the draw loop
// The active shader is called `shader`

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, gAlbedoSpec);
// sets the `uniform sampler2D gAlbedoSpec` to use texture unit `0`
shader.setInt("gAlbedoSpec", 0);

// ... code which draws the scene using `shader`

Now, in my shader I have something like this:

uniform sampler2D gAlbedoSpec;
uniform samplerCube shadowMap;

// ... some code which uses gAlbedoSpec

if (valueWhichIsFalseInThisExample) {
    // ... some code which uses shadowMap
} 

If I remove the bit of code marked snip 1, then GL will render whatever I tell it to. Otherwise, the screen will be black without exception. To me it seems that the act of simply generating a cubemap has lead to GL to refuse to render. So, my question is, why does generating a cubemap lead GL to refuse to render anything to the screen?

(This is all the relevant code and does minimally reproduce the problem.)

  • 1
    My crystal ball says the cubemap is still bound when you render stuff, instead of the texture that you actually want to render stuff with. – user253751 Jul 26 '22 at 13:49
  • 7
    Your problem is not with this code. Your problem is that your draw code isn't drawing any pixels (or only black pixels). Something in this code causes your draw code to do that. But the draw code is not posted, so we have to gaze into crystal balls to see it. – user253751 Jul 26 '22 at 14:13
  • 2
    Possibly because it seems like you haven't narrowed down your problem as much as you could have. You found out your problem has something to do with this code... okay... surely the logical next step is to remove or comment out some of the lines here and see which part causes the problem? For example, I suspect you'd still have the problem if you removed all the calls to glTexParameteri and glTexImage2D and the loop. – user253751 Jul 26 '22 at 14:20
  • @user253751 funnily enough, I did test this before asking the question. Removing the calls to `glTexImage2D` _did_ fix the problem, weirdly. I was against posting my drawing code because it is a little long, and I like to try to keep the amount of pasted code in my questions to a minimum, because no one likes to dig through hundreds of lines of unfamiliar and incomplete code. That said, I will update my question in a moment. –  Jul 26 '22 at 14:23
  • 3
    The fact that it's long means you're supposed to pare it down to some code that isn't long - maybe one triangle that's supposed to be on screen but disappears if you enable the cube map. – user253751 Jul 26 '22 at 14:24
  • well, the "texture stays bound" theory goes right out the window, since you do bind your real textures in the draw code – user253751 Jul 26 '22 at 14:57
  • does `shaderConfigureLights` create any PointLight objects? – user253751 Jul 26 '22 at 15:36
  • @user253751 no, it doesn't create any PointLight objects. I have updated my question with the relevant contents of this function. Would you like to move this to chat, by the way? –  Jul 26 '22 at 15:43
  • what's this extra i parameter that pointlight has and dirlight doesn't? – user253751 Jul 26 '22 at 15:50
  • @user253751 oh, sorry, the renderer stores multiple point lights in an vector but just has a single directional light. `i` is the index of the point light that will be configured. –  Jul 26 '22 at 15:51
  • is it possible that `i` is used as the texture index by mistake, so point light 0 is bound to texture unit 0? – user253751 Jul 26 '22 at 15:52
  • @user253751 This isn't the case, but I've updated my question with the code used in the relevant function. Worth noting that because the point light in my test code has `_castsShadow = false`, there is no texture binding occurring in the point light `bind` function. –  Jul 26 '22 at 15:54
  • just as a silly test, what happens if you call `shaderConfigureDeferred` right before glDrawElements? That will rule out the possibility that `shaderConfigureLights` or `glBindVertexArray` somehow messes something up. – user253751 Jul 26 '22 at 15:57
  • @user253751 No luck, I'm afraid. –  Jul 26 '22 at 15:59
  • @user253751 I have just found another fix, though - calling `glBindTexture(GL_TEXTURE_CUBE_MAP, 0);` right at the end of the `PointLight` constructor fixes the black screen problem and all works as expected. Any idea why this might be fixing things? Thanks for your help, btw. –  Jul 26 '22 at 16:01
  • It doesn't make sense to me - I thought binding any type of texture would unbind the previous type. Actually [the wiki](https://www.khronos.org/opengl/wiki/Texture#Texture_image_units) says you can have more than one bound, and the one that's used for rendering depends on the sampler. So what kind of sampler is gAlbedoSpec? – user253751 Jul 26 '22 at 16:24
  • That's what I thought too... I'm going to try something quickly which I have a hunch might have a hand in causing this, will report back. (Also, I don't understand why people are still downvoting this..) –  Jul 26 '22 at 16:29
  • Nope, wasn't that. I tried moving bits of my initialization code around to change the order in which I was generating the various textures I use, but no luck. Do you see now why I struggled initially to work out which bits of my code I needed to post? This is one of those bugs that is rather tricky to narrow down. –  Jul 26 '22 at 16:37
  • @user253751 `gAlbedoSpec` is a `sampler2D`, as are all the other gBuffer samplers. –  Jul 26 '22 at 16:38
  • That said, this line from the wiki caught my attention: "if two different GLSL samplers have different texture types, but are associated with the same texture image unit, then rendering will fail. Give each sampler a different texture image unit." So perhaps I'm setting a samplerCube to have the same texture image unit as a sampler2D at some point? –  Jul 26 '22 at 16:43
  • oh good idea, especially since unused samplers are probably bound to texture unit 0 by default. I think you've solved it – user253751 Jul 26 '22 at 16:47
  • @user253751 Aha! Turns out this was exactly it! When I initialize the point light, I am binding `_shadowMap` (a samplerCube) to the active texture unit, which happens to be `GL_TEXTURE0`. Later, before I render using the gBuffer, I bind `_gAlbedoSpec` (a sampler2D) explicitly to `GL_TEXTURE0`. I now have two different texture types bound to the same image unit. I then explicitly set a `sampler2D` uniform in the shader to `0` so I can sample `_gAlbedoSpec`. But, as you mentioned, I also have a `samplerCube` uniform which I'm not using, but which is by default set to 0. Thus nothing is rendered. –  Jul 26 '22 at 16:58

1 Answers1

2

When will OpenGL refuse to render?

Per the wiki, if both

  1. two different texture types (e.g. GL_TEXTURE_2D and GL_TEXTURE_CUBE) are bound to the same image unit (e.g. GL_TEXTURE0); and,
  2. two uniforms of different sampler types (e.g. sampler2D and samplerCube) are both set to use this image unit,

then no rendering will be performed (leading to a black screen).

Satisfying the first criterion

In creating a cubemap during instantiation of a point light, I was binding it to the active texture unit, which happened to be GL_TEXTURE0:

glGenTextures(1, &_shadowMap);
glBindTexture(GL_TEXTURE_CUBE_MAP, _shadowMap);

Later, when it came to rendering the gBuffer, I was binding the albedo/specular buffer also to GL_TEXTURE0, this time explicitly as:

// Load gBuffer textures
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _gAlbedoSpec);

Thus criterion 1 of the above has now been fulfilled.

Satisfying the second criterion

I also set a relevant uniform for the fragment shader so that I could sample the albedo/specular buffer:

shader.setInt("gAlbedoSpec", 0);

In the shader, this uniform is defined as

uniform sampler2D gAlbedoSpec;

In the same shader, at some point I have something along the lines of

uniform samplerCube pointLightShadowMap

This never has a value explicitly set, since I disabled shadow-casting for the point light, but its default value is 0, and this is enough to fulfil criterion 2 of the above.

Conclusion and solution

Thus, I had accidentally managed to set up a situation where I caused OpenGL to refuse to render. It didn't matter that I never actually accessed the second uniform, simply having it set was enough to cause problems.

The best solution is probably to make sure that the cubemap is unbound once we are finished with it during point light instantiation:

glGenTextures(1, &_shadowMap);
glBindTexture(GL_TEXTURE_CUBE_MAP, _shadowMap);

// ... do what we need to do

glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
  • Today (right now) I found out you can call [glValidateProgram](https://registry.khronos.org/OpenGL-Refpages/es2.0/xhtml/glValidateProgram.xml) to ask the driver for some debugging information and the manual specifically mentions this problem. (I only found it while searching about this problem - I didn't know it before) – user253751 Jul 26 '22 at 17:55
  • @user253751 I did not know about this. I shall be using it in future, many thanks. –  Jul 26 '22 at 17:56