Related
I'm working on shadow casting using this lovely tutorial. The process is, we render the scene to a frame buffer, attached to which is a cubemap to hold the depth values. Then, we pass this cubemap to a fragment shader which samples it and gets the depth values from there.
I took a slight deviation from the tutorial in that instead of using a geometry shader to render the entire cubemap at once, I instead render the scene six times to get the same effect - largely because my current shader system doesn't support geometry shaders and for now I'm not too concerned about the performance hit.
The depth cubemap is being drawn to fine, here's a screenshot from gDEBugger:
Everything seems to be in order here.
However, I'm having issues in my fragment shader when I attempt to sample this cubemap. After the call to glDrawArrays, a call to glGetError returns GL_INVALID_OPERATION, and as best as I can tell, it's coming from here: (The offending line has been commented)
struct PointLight
{
vec3 Position;
float ConstantRolloff;
float LinearRolloff;
float QuadraticRolloff;
vec4 Color;
samplerCube DepthMap;
float FarPlane;
};
uniform PointLight PointLights[NUM_POINT_LIGHTS];
[...]
float CalculateShadow(int lindex)
{
// Calculate vector between fragment and light
vec3 fragToLight = FragPos - PointLights[lindex].Position;
// Sample from the depth map (Comment this out and everything works fine!)
float closestDepth = texture(PointLights[lindex].DepthMap, vec3(1.0, 1.0, 1.0)).r;
// Transform to original value
closestDepth *= PointLights[lindex].FarPlane;
// Get current depth
float currDepth = length(fragToLight);
// Test for shadow
float bias = 0.05;
float shadow = currDepth - bias > closestDepth ? 1.0 : 0.0;
return shadow;
}
Commenting out the aforementioned line seems to make everything work fine - so I'm assuming it's the call to the texture sampler that's causing issues. I saw that this can be attributed to using two textures of different types in the same texture unit - but according to gDEBugger this isn't the case:
Texture 16 is the depth cube map.
In case it's relevant, here's how I'm setting up the FBO: (called only once)
// Generate frame buffer
glGenFramebuffers(1, &depthMapFrameBuffer);
// Generate depth maps
glGenTextures(1, &depthMap);
// Set up textures
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, depthMap);
for (int i = 0; i < 6; ++i)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT,
ShadowmapSize, ShadowmapSize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
// Set texture parameters
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
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);
// Attach cubemap to FBO
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFrameBuffer);
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthMap, 0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
ERROR_LOG("PointLight created an incomplete frame buffer!\n");
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
Here's how I'm drawing with it: (called every frame)
// Set up viewport
glViewport(0, 0, ShadowmapSize, ShadowmapSize);
// Bind frame buffer
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFrameBuffer);
// Clear depth buffer
glClear(GL_DEPTH_BUFFER_BIT);
// Render scene
for(int i = 0; i < 6; ++i)
{
sh->SetUniform("ShadowMatrix", lightSpaceTransforms[i]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, depthMap, 0);
Space()->Get<Renderer>()->RenderScene(sh);
}
// Unbind frame buffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
And here's how I'm binding it before drawing:
std::stringstream ssD;
ssD << "PointLights[" << i << "].DepthMap";
glActiveTexture(GL_TEXTURE4 + i);
glBindTexture(GL_TEXTURE_CUBE_MAP, pointlights[i]->DepthMap()); // just returns the ID of the light's depth map
shader->SetUniform(ssD.str().c_str(), i + 4); // just a wrapper around glSetUniform1i
Thank you for reading, and please let me know if I can supply more information!
It is old post, but i think it may be useful for other people from the search.
Your problem here:
glActiveTexture(GL_TEXTURE4 + i);
glBindTexture(GL_TEXTURE_CUBE_MAP, pointlights[i]->DepthMap());
This replacement should fix problem:
glActiveTexture(GL_TEXTURE4 + i);
glUniform1i(glGetUniformLocation("programId", "cubMapUniformName"), GL_TEXTURE4 + i);
glBindTexture(GL_TEXTURE_CUBE_MAP, pointlights[i]->DepthMap());
It set texture unit number for shader sampler
I have problem getting cube maps to work in OpenGL. I followed some tutorials and created my own class wrapping the OpenGL cube map texture.
For start I'm trying to load six images for the cube map sides and render them onto a pot model. I want to create a mirror-like effect ultimately but for now I'm using just the normal as the cube map texture coordinate. The problem is the sampler in fragment shader seems to be returning only zeroes so the whole model is just black.
my findings so far:
Texture data should be uploaded to GPU - I checked this by uploading (glTexImage2D) and retrieving the data back (glGetTexImage2D).
I checked glGetError() and it returns no error, even though when I set glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR); or
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); for the cubemap textures, I get invalid operation error inside the rendering loop when I try to render the mirror (the pot) - strange.
Normals of the pot should be OK, I have checked them by rendering them (see images below).
The ppm image I'm loading is 512 x 512, works for 2D textures, so there shouldn't be a problem.
Here is how I initialize the cube map:
texture_cube = new TextureCubeMap(512,TEXEL_TYPE_COLOR);
cout << "cubemap loading: " << texture_cube->load_ppms("rock.ppm","rock.ppm","rock.ppm","rock.ppm","rock.ppm","rock.ppm") << endl;
texture_cube->update_gpu();
...
glUniform1i(sampler_location,0); // 'tex' unit
glUniform1i(sampler_cube_location,0); // 'tex_cube' unit
Here's my rendering loop:
void render()
{
glClear(GL_COLOR_BUFFER_BIT);
glClear(GL_DEPTH_BUFFER_BIT);
glUniformMatrix4fv(view_matrix_location,1,GL_TRUE,glm::value_ptr(CameraHandler::camera_transformation.get_matrix()));
texture_cup->bind(0);
glUniformMatrix4fv(model_matrix_location,1,GL_TRUE,glm::value_ptr(transformation_cup.get_matrix()));
geometry_cup->draw_as_triangles();
texture_rock->bind(0);
glUniformMatrix4fv(model_matrix_location,1,GL_TRUE,glm::value_ptr(transformation_rock.get_matrix()));
geometry_rock->draw_as_triangles();
texture_cow->bind(0);
glUniformMatrix4fv(model_matrix_location,1,GL_TRUE,glm::value_ptr(transformation_cow.get_matrix()));
geometry_cow->draw_as_triangles();
texture_room->bind(0);
glUniformMatrix4fv(model_matrix_location,1,GL_TRUE,glm::value_ptr(glm::mat4(1.0)));
geometry_room->draw_as_triangles();
glUniformMatrix4fv(model_matrix_location,1,GL_TRUE, glm::value_ptr(transformation_mirror.get_matrix()));
// draw the mirror:
texture_cube->bind(0);
glUniform1ui(mirror_location,1);
geometry_mirror->draw_as_triangles();
glUniform1ui(mirror_location,0);
ErrorWriter::checkGlErrors("rendering loop");
glutSwapBuffers();
}
Here's my fragment shader:
#version 330
in vec3 transformed_normal;
in vec2 uv_coords;
uniform vec3 light_direction;
uniform sampler2D tex;
uniform samplerCube tex_cube;
uniform bool mirror;
out vec4 FragColor;
float diffuse_intensity;
float lighting_intensity;
void main()
{
diffuse_intensity = clamp(dot(normalize(transformed_normal),-1 * light_direction),0.0,1.0);
lighting_intensity = clamp(0.4 + diffuse_intensity,0.0,1.0);
if (mirror)
{
FragColor = texture(tex_cube, transformed_normal);
}
else
{
FragColor = 0.3 * vec4(lighting_intensity, lighting_intensity, lighting_intensity, 1.0);
FragColor += texture(tex, uv_coords);
}
}
And here is my whole cube map class (Image2D is my own class that should be working OK, I have it tested with 2D textures):
class TextureCubeMap: public Texture
{
protected:
unsigned int size;
public:
Image2D *image_front;
Image2D *image_back;
Image2D *image_left;
Image2D *image_right;
Image2D *image_top;
Image2D *image_bottom;
/**
* Initialises a new cube map object.
*
* #size width and height resolution in pixels (cube map must
* have square size)
*/
TextureCubeMap(unsigned int size, unsigned int texel_type = TEXEL_TYPE_COLOR)
{
this->size = size;
glGenTextures(1,&(this->to));
glBindTexture(GL_TEXTURE_CUBE_MAP,this->to);
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);
glBindTexture(GL_TEXTURE_CUBE_MAP,0);
this->image_front = new Image2D(size,size,texel_type);
this->image_back = new Image2D(size,size,texel_type);
this->image_left = new Image2D(size,size,texel_type);
this->image_right = new Image2D(size,size,texel_type);
this->image_top = new Image2D(size,size,texel_type);
this->image_bottom = new Image2D(size,size,texel_type);
}
virtual ~TextureCubeMap()
{
delete this->image_front;
delete this->image_back;
delete this->image_left;
delete this->image_right;
delete this->image_top;
delete this->image_bottom;
}
virtual void update_gpu()
{
int i;
Image2D *images[] =
{this->image_front,
this->image_back,
this->image_left,
this->image_right,
this->image_bottom,
this->image_top};
GLuint targets[] =
{GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
GL_TEXTURE_CUBE_MAP_POSITIVE_X,
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
GL_TEXTURE_CUBE_MAP_POSITIVE_Y};
glBindTexture(GL_TEXTURE_CUBE_MAP,this->to);
for (i = 0; i < 6; i++)
{
glTexImage2D(targets[i],0,GL_RGBA,this->size,this->size,0,GL_RGBA,GL_FLOAT,images[i]->get_data_pointer());
// glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_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);
}
glBindTexture(GL_TEXTURE_CUBE_MAP,0);
}
virtual void load_from_gpu()
{
int i;
glBindTexture(GL_TEXTURE_CUBE_MAP,this->to);
Image2D *images[] =
{this->image_front,
this->image_back,
this->image_left,
this->image_right,
this->image_bottom,
this->image_top};
GLuint targets[] =
{GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
GL_TEXTURE_CUBE_MAP_POSITIVE_X,
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
GL_TEXTURE_CUBE_MAP_POSITIVE_Y};
for (i = 0; i < 6; i++)
{
images[i]->set_size(this->size,this->size);
glGetTexImage(targets[i],0,GL_RGBA,GL_FLOAT,images[i]->get_data_pointer());
}
}
bool load_ppms(string front, string back, string left, string right, string bottom, string top)
{
bool result = true;
result = result && this->image_front->load_ppm(front);
result = result && this->image_back->load_ppm(back);
result = result && this->image_left->load_ppm(left);
result = result && this->image_right->load_ppm(right);
result = result && this->image_bottom->load_ppm(bottom);
result = result && this->image_top->load_ppm(top);
auto lambda_size_ok = [](Image2D *image, int size)
{
return (image->get_width() == size) && (image->get_height() == size);
};
if (!lambda_size_ok(this->image_front,this->size) ||
!lambda_size_ok(this->image_back,this->size) ||
!lambda_size_ok(this->image_left,this->size) ||
!lambda_size_ok(this->image_right,this->size) ||
!lambda_size_ok(this->image_top,this->size) ||
!lambda_size_ok(this->image_bottom,this->size))
ErrorWriter::write_error("Loaded cubemap images don't have the same size.");
return result;
}
virtual void print()
{
cout << "front:" << endl;
this->image_front->print();
cout << "back:" << endl;
this->image_back->print();
cout << "left:" << endl;
this->image_left->print();
cout << "right:" << endl;
this->image_right->print();
cout << "bottom:" << endl;
this->image_bottom->print();
cout << "top:" << endl;
this->image_top->print();
}
virtual void bind(unsigned int unit)
{
switch (unit)
{
case 0: glActiveTexture(GL_TEXTURE0); break;
case 1: glActiveTexture(GL_TEXTURE1); break;
case 2: glActiveTexture(GL_TEXTURE2); break;
case 3: glActiveTexture(GL_TEXTURE3); break;
case 4: glActiveTexture(GL_TEXTURE4); break;
case 5: glActiveTexture(GL_TEXTURE5); break;
case 6: glActiveTexture(GL_TEXTURE6); break;
case 7: glActiveTexture(GL_TEXTURE7); break;
case 8: glActiveTexture(GL_TEXTURE8); break;
case 9: glActiveTexture(GL_TEXTURE9); break;
default:
break;
}
glBindTexture(GL_TEXTURE_CUBE_MAP,this->to);
}
};
And the images of the error (first one what I get, the second one is with normals rendered):
You figured out the solution yourself in the comments, but requested some more background on why it behaves the way it does.
Yes, if you want to use two different textures in the same fragment shader, you will have to bind the two textures to different texture units, and set the values of the sampler uniform variables to the matching texture unit indices.
For the case where the two textures use the same target, it's fairly obvious that this must be true. Otherwise your texture binding code would look something like this:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex1);
glBindTexture(GL_TEXTURE_2D, tex2);
and the second glBindTexture() call would of course override the first one. You will therefore use different texture units:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, tex2);
Things are much less obvious in the case you are looking at, where you use two different texture targets. After this call sequence:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex1);
glBindTexture(GL_TEXTURE_CUBE_MAP, tex2);
You indeed have both textures bound to texture unit 0. In the OpenGL state, each texture unit contains a binding for each target.
Now, if you want to use a 2D and a cube map texture in the same shader, it seems like a perfectly reasonable expectation that you could bind the two textures as shown above, and set both sampler uniforms to value 0. But in fact, this is not supported, because... it's defined that way.
This is on page 74 of the OpenGL 3.3 spec, in section "2.11.5 Samplers":
It is not allowed to have variables of different sampler types pointing to the same texture image unit within a program object. This situation can only be detected at the next rendering command issued, and an INVALID_OPERATION error will then be generated.
While this makes sense based on how GPUs normally access samplers from shader code, it's unfortunate that you can set up state for texture bindings in ways that are not usable from shader code. Part of this is probably based on the long history of OpenGL.
To make this work, you will have to use two different texture units, just like you did for the case with two textures using the same target:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_CUBE_MAP, tex2);
I'm trying to do a compute pass where I render to a texture that will be used in a draw pass later on. My initial implementation was based on shader storage buffer objects and was working nicely. But I want to apply a computation method that is going to take advantage of the blend hardware of the GPU so I started porting the SSBO implementation to RTT one. Unfortunately the code has stopped working. Now when I read back the texture it is getting wrong values.
Here is my texture and frame buffer setup code:
glGenFramebuffers(1, &m_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
// Create render textures
glGenTextures(NUM_TEX_OUTPUTS, m_renderTexs);
m_texSize = square_approximation(m_numVertices);
cout << "Textures size: " << glm::to_string(m_texSize) << endl;
GLenum drawBuffers[NUM_TEX_OUTPUTS];
for (int i = 0 ; i < NUM_TEX_OUTPUTS; ++i)
{
glBindTexture(GL_TEXTURE_2D, m_renderTexs[i]);
// 1st 0: level, 2nd 0: no border, 3rd 0: no initial data
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_texSize.x, m_texSize.y, 0, GL_RGBA, GL_FLOAT, 0);
// XXX: do we need this?
// Poor filtering. Needed !
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
// 0: level
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, m_renderTexs[i], 0);
drawBuffers[i] = GL_COLOR_ATTACHMENT0 + i;
}
glDrawBuffers(NUM_TEX_OUTPUTS, drawBuffers);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
cout << "Error when setting frame buffer" << endl;
// throw exception?
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
And this is the code to start the compute pass:
m_shaderProgram.use();
// setup openGL
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glViewport(0, 0, m_texSize.x, m_texSize.y); // setup viewport (equal to textures size)
// make a single patch have the vertex, the bases and the neighbours
glPatchParameteri(GL_PATCH_VERTICES, m_maxNeighbours + 5);
// Wait all writes to shader storage to finish
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
glUniform1i(m_shaderProgram.getUniformLocation("curvTex"), m_renderTexs[2]);
glUniform2i(m_shaderProgram.getUniformLocation("size"), m_texSize.x, m_texSize.y);
glUniform2f(m_shaderProgram.getUniformLocation("vertexStep"), (umax - umin)/divisoes,
(vmax-vmin)/divisoes);
// Bind buffers
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo);
glBindBufferBase(GL_UNIFORM_BUFFER, m_mvp_location, m_mvp_ubo);
// Make textures active
for (int i = 0; i < NUM_TEX_OUTPUTS; ++i)
{
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, m_renderTexs[i]);
}
// no need to pass index array 'cause ibo is bound already
glDrawElements(GL_PATCHES, m_numElements, GL_UNSIGNED_INT, 0);
I then read back the textures using the following:
bool readTex(GLuint tex, void *dest)
{
glBindTexture(GL_TEXTURE_2D, tex);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, dest);
glBindTexture(GL_TEXTURE_2D, 0);
// TODO: check glGetTexImage return values for error
return true;
}
for (int i = 0; i < NUM_TEX_OUTPUTS; ++i)
{
if (m_tensors[i] == NULL) {
m_tensors[i] = new glm::vec4[m_texSize.x*m_texSize.y];
}
memset(m_tensors[i], 0, m_texSize.x*m_texSize.y*sizeof(glm::vec4));
readTex(m_renderTexs[i], m_tensors[i]);
}
Finally, the fragment shader code is:
#version 430
#extension GL_ARB_shader_storage_buffer_object: require
layout(pixel_center_integer) in vec4 gl_FragCoord;
layout(std140, binding=6) buffer EvalBuffer {
vec4 evalDebug[];
};
uniform ivec2 size;
in TEData {
vec4 _a;
vec4 _b;
vec4 _c;
vec4 _d;
vec4 _e;
};
layout(location = 0) out vec4 a;
layout(location = 1) out vec4 b;
layout(location = 2) out vec4 c;
layout(location = 3) out vec4 d;
layout(location = 4) out vec4 e;
void main()
{
a= _a;
b= _b;
c= _c;
d= _d;
e= _e;
evalDebug[gl_PrimitiveID] = gl_FragCoord;
}
The fragment coordinates are correct (each fragment is pointing to a x,y coordinate in the texture), so are all the input values (_a to _e), but I do not see them outputted correctly to the textures when reading back. I also tried accessing the texture in the shader to see if it was only a read-back error, but my debug SSBO returned all zeroes.
Am I missing some setup step?
I've tested both on linux and windows (titan and 540M geforces) and I'm using openGL 4.3.
As derhass pointed out in the comments above, the problem was with the texture format. I assumed that by passing GL_FLOAT as the data type it would use 32bit floats for each of the RGBA channels. It was not so.
As derhass said, the data type parameter here does not change the texture format. I had to change the internalFormat parameter to what I wanted (GL_RGBA32F) so that it would work as expected.
So, after changing glTexImage2D call to:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, m_texSize.x, m_texSize.y, 0, GL_RGBA, GL_FLOAT, 0);
I was able to correctly render the results to the texture and read it back. :)
Image with two cubes is when its using BLIT Call - Test Code
Image with one Cube is when its not Using BLIT CALL - Test Code
Folks,
Please follow the attached pictures & Code as below..
Struggling with this from last 2 days..
Facing an issue while using FBO in below code, I am able to attach & render to FBo successfully, BUT some how not able to use that as a texture (color & depth buffers) in the same program, MY glReadpixel call after rendering to FBO Shows me all Zero's data...
When generated FBO(color,depth) is used in GLSL- shader code , which is called from Draw2() function displays a blend white cube without texture..
please suggest..., below is the complete code... , I am creating an FBO & using 2 shaders one for Normal rendering & other one uses the color & depth from FBO processed earlier..
Here is MY SHADER CODE
init_gl()
{
This Fragment Shader Code is for normal rendering used in Program under draw()
static const char *fragment_shader_source =
"precision mediump float; \n"
" \n"
"varying vec4 vVaryingColor; \n"
" \n"
"void main() \n"
"{ \n"
" gl_FragColor = vVaryingColor; \n"
"} \n";
This Fragment Shader Code is used in Program2 under draw2()
static const char *fragment_shader_source2 =
"precision mediump float; \n"
" \n"
"varying vec4 vVaryingColor; \n"
"uniform sampler2D color; \n"
"uniform sampler2D depth; \n"
" \n"
"void main() \n"
"{ \n"
"float r = pow(texture2D(color, gl_FragCoord.xy / vec2(256, 256)).a, 128.0); \n"
"float g = texture2D(depth, gl_FragCoord.xy / vec2(256, 256)).a;\n"
"float b = pow(texture2D(depth, gl_FragCoord.xy / vec2(256, 256)).a, 128.0); \n"
"gl_FragColor = vec4(r, g, b, 1.); \n"
"} \n";
MY FIRST DRAWING ON FBO
static void draw(uint32_t i)
{
EGL_CHECK(glEnable(GL_DEPTH_TEST));
glEnable(GL_TEXTURE_2D);
EGL_CHECK(glDepthFunc(GL_ALWAYS));
EGL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, fboInfo.id));
EGL_CHECK(glUseProgram(program));
//CUBE DRAWING
Doing Read Pixel here, Its returning all zero's
GLubyte *pixels = malloc(4 * 256 * 256);
EGL_CHECK(glReadBuffer(GL_COLOR_ATTACHMENT0));
EGL_CHECK(glReadPixels(0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
//**TO TEST my FBO , IF ITS HAVING SOME RENDER DATA**
EGL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboInfo.id);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, 256, 256, 0, 0, 256, 256, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
//TEST CODE ONLY
glBindTexture(GL_TEXTURE_2D, fboInfo.id);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
draw2(i);
}
2nd DRAWING WITHOUT FBO (BUT this uses FRAG SHADER WHICH CAN ACCESS FBO data color,depth)
static void draw2(uint32_t i)
{
glUseProgram(program2);
//Draw Same Kind of Cube , Which we draw in draw() function
}
These are the functions to create FBO (color,depth)
GLuint createTexture2D(const int w, const int h, GLint internalFormat, GLenum format, GLenum type)
{
GLuint textureIdX;
EGL_CHECK(glGenTextures(1, &textureIdX));
EGL_CHECK(glBindTexture(GL_TEXTURE_2D, textureIdX));
EGL_CHECK(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
EGL_CHECK(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
EGL_CHECK(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
EGL_CHECK(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
if (GL_DEPTH_COMPONENT == format) {
EGL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE));
EGL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY));
}
EGL_CHECK(glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, w, h, 0, format, type, 0));
EGL_CHECK(glBindTexture(GL_TEXTURE_2D, 0));
return textureIdX;
}
int createFBO(void)
{
int result = 0;
unsigned int fb = 0;
fboInfo.color = createTexture2D(WINDOW_WIDTH, WINDOW_HEIGHT, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE);
fboInfo.depth = createTexture2D(WINDOW_WIDTH, WINDOW_HEIGHT, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_FLOAT);
EGL_CHECK(glGenFramebuffers(1, &fboInfo.id));
EGL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, fboInfo.id));
EGL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboInfo.color, 0));
EGL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fboInfo.depth, 0));
int data = EGL_CHECK(glCheckFramebufferStatus(GL_FRAMEBUFFER));
if (GL_FRAMEBUFFER_COMPLETE == glCheckFramebufferStatus(GL_FRAMEBUFFER)) {
printf("FBO %d set up successfully\n", fboInfo.id);
result = 1;
}
else {
printf("FBO %d NOT set up properly!\n", fboInfo.id);
}
EGL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
return result;
}
This is how my FBO Struct Looks Like
typedef struct {
GLuint id;
GLuint color;
GLuint depth;
}FBOInfo;
My MAIN function looks a below where I create FBO & then call my draw function & swap the buffers..
unsigned long i = 0;
int ret = init_gl();
ret = createFBO();
draw(i);
EGL_CHECK(eglSwapBuffers(eglDisplay, eglSurface));
Since this is quite a lot of code, and you suggested that the problem is with the FBO setup, I focused on that section. I did spot a critical problem in that part of the code where you set up the attachments:
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fboInfo.depth, 0);
If you call glGetError() after this, you should see an error returned, because GL_RENDERBUFFER is not a valid value for the 3rd argument. The only valid arguments are GL_TEXTURE_2D and the GL_TEXTURE_CUBE_MAP_* values. You have to use GL_TEXTURE_2D in this case.
It might actually be better to use a renderbuffer for the depth buffer if you never use it as a texture. But to do that, you'll have to use all the corresponding calls:
Create the id with glGenRenderbuffers.
Bind it with glBindRenderbuffer.
Allocated it with glRenderbufferStorage.
Attach it to the FBO with glFramebufferRenderbuffer.
EDIT: Think I've narrowed down the problem. Skip to the running section.
I'm trying to sample a 3d texture in my vertex shader, I'm going to use the texel values as corner value in Marching Cubes. The issue I'm having is that no matter what method I use to sample it, I always get (0,0,0,0). I've tried using texelFetch and texture3D and neither seem to work.
I'm also using transform feedback, but as far as I'm aware that shouldn't cause this issue.
Shader setup:
glEnable(GL_TEXTURE_3D);
Shader vertListTriangles(GL_VERTEX_SHADER_ARB);
vertListTriangles.setSource(lst_tri_vert); //Util to load from file.
vertListTriangles.compile();
vertListTriangles.errorCheck(); //Prints errors to console if they exist - shader compiles fine.
Shader geomListTriangles(GL_GEOMETRY_SHADER_ARB);
geomListTriangles.setSource(lst_tri_geom); //Util to load from file
geomListTriangles.compile();
geomListTriangles.errorCheck(); //Prints errors to console if they exist - shader compiles fine.
program.attach(vertListTriangles);
program.attach(geomListTriangles);
//Setup transform feedback varyings, also works as expected.
const GLchar* varyings1[1];
varyings1[0] = "gTriangle";
glTransformFeedbackVaryings(program.getID(), 1, varyings1, GL_INTERLEAVED_ATTRIBS);
program.link();
program.checkLink(); //Prints link errors to console - program links fine aparently.
Texture setup:
glBindTexture(GL_TEXTURE_3D, textureID);
errorCheck("texture bind"); //<- Detects GL errors, I actually get a GL_INVALID_OPERATION here, not sure if its the cause of the problem though as all subsuquent binds go smoothly.
if(!(glIsTexture(textureID)==GL_TRUE)) consolePrint("Texture Binding Failed."); //Oddly the texture never registers as failed despite the previous error message.
//Generate Texture
GLfloat volumeData[32768*3];
for(int z = 0; z < 32; z++)
{
for(int y = 0; y < 32; y++)
{
for(int x = 0; x < 32; x++)
{
//Set all 1s for testing purposes
volumeData[(x*3)+(y*96)+(z*3072)] = 1.0f;
volumeData[(x*3)+(y*96)+(z*3072)+1] = 1.0f;
volumeData[(x*3)+(y*96)+(z*3072)+2] = 1.0f;
}
}
}
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_BASE_LEVEL, 0);
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB8, 32, 32, 32, 0, GL_RGB,
GL_FLOAT, volumeData);
glBindTexture(GL_TEXTURE_3D, 0);
Running Shader:
EDIT: Here it gets interesting. If I specify an incorrect uniform name or comment out the below lines it appears to work.
program.use();
//Disable Rastering
glEnable(GL_RASTERIZER_DISCARD);
//Input buffer: Initial vertices
glBindBuffer(GL_ARRAY_BUFFER, mInitialDataBuffer);
glEnableVertexAttribArray(0);
glVertexAttribIPointer(0, 1, GL_UNSIGNED_INT, 0, 0); //Initial input is array of uints
//Output buffer: Triangles
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mTriangleBuffer); //Triangle Markers, in the form of uints. NOT actual triangles.
//Texture setup
//If I comment out from here....
GLint sampler = glGetUniformLocation(program.getID(), "densityVol");
glUniform1i(sampler, GL_TEXTURE0);
glActiveTexture(GL_TEXTURE0);
//To here. It appears to work.
glBindTexture(GL_TEXTURE_3D, textureID);
//Just using this to debug texture.
//test is all 1s, so the texture is uploading correctly.
GLfloat test[32768*3];
memset(test, 0, sizeof(test));
glGetTexImage(GL_TEXTURE_3D, 0, GL_RGB, GL_FLOAT, test);
//Transform Feedback and Draw
glBeginTransformFeedback(GL_POINTS);
glDrawArrays(GL_POINTS, 0, 29790);
glEndTransformFeedback();
//Re-enable Rastering and cleanup
glDisable(GL_RASTERIZER_DISCARD);
glDisableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
My code is a little more spread out in reality, but I hope I managed to edit it into something cohesive. Anyway if I map to the output buffer it does indeed output some information, however it processes as if all the texture data is 0s. I hacked the shader to just output some test results instead but I can't find any evidence the shader is using the texture correctly:
#version 410
#extension GL_EXT_gpu_shader4 : require
layout (location = 0) in int x_y_z;
uniform sampler3D densityVol;
out Voxel
{
/*
Each triangle is the edges it joins. There are 12 edges and so we need 12 bits. 4 For each edge.
There are up to 32 voxels, which means we need 6 bits for each coord, which is 18.
30 bits total.
int format 00xxxxxxyyyyyyzzzzzz111122223333
*/
uint triangles[5];
uint triangleCount;
} vVoxel;
//... Omitted some huge ref tables.
void main()
{
vec4 sample0 = texture3D(densityVol, vec3(0.1,0.1,0.1) );
vec4 sample1 = texture3D(densityVol, vec3(0.9,0.9,0.9) );
vec4 sample2 = texture3D(densityVol, vec3(0.1,0.1,0.9) );
vec4 sample3 = texture3D(densityVol, vec3(0.9,0.9,0.1) );
if(sample0.r > 0.0f)
{
vVoxel.triangles[1] = 1;
}
if(sample1.r > 0.0f)
{
vVoxel.triangles[2] = 2;
}
if(sample2.r > 0.0f)
{
vVoxel.triangles[3] = 3;
}
if(sample3.r > 0.0f)
{
vVoxel.triangles[4] = 4;
}
vVoxel.triangleCount = 5;
}
Not the best designed test, but I didn't want to write something from scratch. If I change the if clauses to if(true) the test outputs correctly. When the shader is compiled as above, the buffer is blank. I'm using a GS for pass through.
Can anyone see an obvious mistake in there? I've been stumped for about 2 hours now and I can't see what I'm doing different from many of the GLSL texturing tutorials.
Okay figured it out.
glUniform1i(sampler, GL_TEXTURE0);
The GL_TEXTURE0 is incorrect here.
glUniform1i(sampler, 0);
Is how it should be.