I've written a shader that writes depth to each face of a cube map, as part of rendering cube map shadows for a realtime 3D engine I'm writing with OpenGL and C++.
The shader transforms each vertex into world space in the vertex shader, then transforms polygons to each of the cube map's 6 faces in the geometry shader, and then writes depth to the cube map in the fragment shader.
The shader works correctly, but I receive the following error message when rendering with it for the first time:
Error: OpenGL Error Callback:
Source:
Type: PERFORMANCE
id: 131218
Severity: MEDIUM
Message: Program/shader state performance warning: Vertex shader in program 89 is being recompiled based on GL state.
After that, the shader functions perfectly and I'm able to render depth to a cube map, and use it to render shadows for my point lights.
The shader has a vertex, geometry, and fragment shader component:
Vertex:
#version 430 core
layout (location = 0) in vec3 in_position;
uniform mat4 in_model;
void main()
{
gl_Position = in_model * vec4(in_position.xyz, 1.0);
}
Geometry
#version 430 core
layout (triangles) in;
layout (triangle_strip, max_vertices = 18) out;
uniform mat4 shadowCamCubeMap_vp[6];
out vec4 FragPos;
void main()
{
for(int currentCubeFace = 0; currentCubeFace < 6; currentCubeFace++)
{
gl_Layer = currentCubeFace; // Set the cube map face we're rendering to
for(int currentVert = 0; currentVert < 3; currentVert++)
{
FragPos = gl_in[currentVert].gl_Position; // World pos from vert shader
gl_Position = shadowCamCubeMap_vp[currentCubeFace] * gl_in[currentVert].gl_Position;
EmitVertex();
}
EndPrimitive();
}
}
Fragment:
#version 430 core
uniform vec3 lightWorldPos;
uniform float shadowCam_near;
uniform float shadowCam_far;
in vec4 FragPos;
void main()
{
float lightDistance = length(FragPos.xyz - lightWorldPos);
lightDistance = lightDistance / shadowCam_far;
gl_FragDepth = lightDistance;
}
If I delete the geometry shader and just compile the vertex and fragment shaders together, the issue still occurs.
If I try and debug the program in RenderDoc, the error does not occur (!!).
I'm scratching my head here. The vertex shader looks near identical to other vertex shaders I'm using.
I've poured over every google result I could find, but still don't have any idea what's going on here. One thread suggested that, possibly, some part of the OpenGL state was compiled into the program when the shader was first loaded. Then, at some point the state was changed, triggering a recompile when the shader is first used to render something... But it didn't offer any solutions and I'm not really sure if/how this could be the case.
I'd appreciate any help I can get here!
EDIT: I have another 2D depth shader that I use to write depth for a key light that doesn't suffer this issue. If I completely replace the cube map depth shader code with the functional key light depth shader (and delete the geometry shader), the issue still persists.
If I don't render the keylight at all (making the cube map depth shader the only depth shader that gets executed), the issue persists.
The only difference between the cube and 2D depth map shaders is that the cube map depth shader is being rendered with cube map textures bound... Is it possible I'm screwing up their initialization somehow?
Here is my cube map setup:
bool RenderTexture::BufferCubeMap(RenderTexture** cubeFaceRTs)
{
// Bind Texture:
glBindTexture(GL_TEXTURE_CUBE_MAP, this->textureID);
if (!glIsTexture(this->textureID))
{
glGenTextures(1, &this->textureID);
glBindTexture(GL_TEXTURE_CUBE_MAP, this->textureID);
if (! glIsTexture(this->textureID))
{
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
return false;
}
}
// Generate faces:
for (int i = 0; i < CUBE_MAP_COUNT; i++)
{
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT32F, this->width, this->height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
}
// Set texture params:
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);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// Bind sampler:
glBindSampler(12, this->samplerID);
if (!glIsSampler(this->samplerID))
{
glGenSamplers(1, &this->samplerID);
glBindSampler(12, this->samplerID);
}
// Set sampler params:
glSamplerParameteri(this->samplerID, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glSamplerParameteri(this->samplerID, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glSamplerParameteri(this->samplerID, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glSamplerParameteri(this->samplerID, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(this->samplerID, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Bind framebuffer:
glBindFramebuffer(GL_FRAMEBUFFER, this->frameBufferObject);
if (!glIsFramebuffer(this->frameBufferObject))
{
glGenFramebuffers(1, &this->frameBufferObject);
glBindFramebuffer(GL_FRAMEBUFFER, this->frameBufferObject);
if (!glIsFramebuffer(this->frameBufferObject))
{
return false;
}
}
// Attach framebuffer as a cube map render buffer:
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, this->textureID, 0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
// Ensure all of the textures have the correct information stored in them:
for (int i = 0; i < 6; i++)
{
if (cubeFaceRTs[i] != this)
{
cubeFaceRTs[i]->textureID = this->textureID;
cubeFaceRTs[i]->samplerID = this->samplerID;
cubeFaceRTs[i]->frameBufferObject = this->frameBufferObject;
}
}
bool result = glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
if (!result)
{
LOG_ERROR("Cube map framebuffer is not complete!");
}
// Cleanup:
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
glBindSampler(12, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return result;
}
Finally, the cube map texture is bound with:
void RenderTexture::Bind(GLuint const& shaderReference)
{
glActiveTexture(GL_TEXTURE0 + 12);
glBindTexture(GL_TEXTURE_CUBE_MAP, this->textureID);
glBindSampler(12, this->samplerID);
}
I've edited my code to make it as brief and readable as possible, hopefully I didn't make any typos...