Rendering Textures with Modern OpenGL - c++

So I'm currently making a game using SDL2 with OpenGL (glew), and using SOIL to load images, and I'm using modern opengl techniques with vertex array objects and shaders and whatnot. I'm currently trying to just render just a texture to the window, but I can't seem to do it. I've looked up many tutorials and solutions, but I can't seem to understand how I'm supposed to do it properly. I'm unsure of whether it's the shader that's the problem, or my code itself. I'll post all the necessary code below, and any more is needed I'd be happy to supply it. Any answers and solutions are welcome. For future context, I have a class I use to store data for VAOs for convenience purposes. Code:
Here's the code to load the texture:
void PXSprite::loadSprite() {
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glGenerateMipmap(GL_TEXTURE_2D);
int imagew, imageh;
//The path is a class variable, and I haven't received any errors from this function, so I can only assume it's getting the texture file correctly.
unsigned char* image = SOIL_load_image(path.c_str(), &imagew, &imageh, 0, SOIL_LOAD_RGB);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, imagew, imageh, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
SOIL_free_image_data(image);
glBindTexture(GL_TEXTURE_2D, 0);
//Don't worry about this code. It's just to keep the buffer object data.
//It works properly when rendering polygons.
spriteVAO.clear();
spriteVAO.addColor(PXColor::WHITE());
spriteVAO.addColor(PXColor::WHITE());
spriteVAO.addColor(PXColor::WHITE());
spriteVAO.addColor(PXColor::WHITE());
spriteVAO.addTextureCoordinate(0, 0);
spriteVAO.addTextureCoordinate(1, 0);
spriteVAO.addTextureCoordinate(1, 1);
spriteVAO.addTextureCoordinate(0, 1);
glGenVertexArrays(1, &spriteVAO.vaoID);
glGenBuffers(1, &spriteVAO.posVBOid);
glBindBuffer(GL_ARRAY_BUFFER, spriteVAO.posVBOid);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*12, nullptr, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glGenBuffers(1, &spriteVAO.colVBOid);
glBindBuffer(GL_ARRAY_BUFFER, spriteVAO.colVBOid);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 16, &spriteVAO.colors[0], GL_STATIC_DRAW);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, 0);
glGenBuffers(1, &spriteVAO.texVBOid);
glBindBuffer(GL_ARRAY_BUFFER, spriteVAO.texVBOid);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 8, &spriteVAO.texCoords[0], GL_STATIC_DRAW);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, 0);
glBindTexture(GL_TEXTURE_2D, 0);
}
And Here's my code for rendering the texture:
void PXSprite::render(int x, int y) {
spriteVAO.clear(PXVertexArrayObject::positionAttributeIndex);
spriteVAO.addPosition(x, y);
spriteVAO.addPosition(x+width, y);
spriteVAO.addPosition(x, y+height);
spriteVAO.addPosition(x+width, y+height);
glBindTexture(GL_TEXTURE_2D, textureID);
glBindVertexArray(spriteVAO.vaoID);
glBindBuffer(GL_ARRAY_BUFFER, spriteVAO.posVBOid);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat)*12, &spriteVAO.positions[0]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glBindTexture(GL_TEXTURE_2D, 0);
}
Here's my vertex shader:
#version 330 core
in vec3 in_Position;
in vec4 in_Color;
in vec2 in_TexCoord;
out vec4 outPosition;
out vec4 outColor;
out vec2 outTexCoord;
void main() {
gl_Position = vec4(in_Position.x, in_Position.y*-1.0, in_Position.z, 1.0);
outTexCoord = in_TexCoord;
outColor = in_Color;
}
Here's my fragment shader:
#version 330 core
in vec2 outTexCoord;
in vec4 outColor;
out vec4 glFragColor;
out vec4 glTexColor;
uniform sampler2D pxsampler;
void main() {
vec4 texColor = texture(pxsampler, outTexCoord);
//This outputs the color of polygons if I don't multiply outColor by texColor, but once I add texColor, no colors show up at all.
glFragColor = texColor*outColor;
}
And lastly here's a bit of code to give reference to the VAO Attribute Pointers that I use at the right time when loading the shaders:
glBindAttribLocation(ProgramID, 0, "in_Position");
glBindAttribLocation(ProgramID, 1, "in_Color");
glBindAttribLocation(ProgramID, 2, "in_TexCoord");
Any help or suggestions are welcome. If any extra code is needed I'll add it. I'm sorry if this question seems redundant, but I couldn't find anything to help me. If someone could explain to me how exactly the code for rendering the textures works, that would be great, because from tutorials I've read they usually don't explain what does what so that I know how to do it again, so I'm not clear on what I'm doing exactly. Again, any help is appreciated. Thanks!

I spotted a few problems in this code. Partly already mentioned in other answers/comments, partly not.
VAO binding
The main problem is that your VAO is not bound while you set up the vertex attributes. See this code sequence:
glGenVertexArrays(1, &spriteVAO.vaoID);
glGenBuffers(1, &spriteVAO.posVBOid);
glBindBuffer(GL_ARRAY_BUFFER, spriteVAO.posVBOid);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*12, nullptr, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
Here, you create a VAO (or more precisely, the id for a VAO), but don't bind it. The state set up by the glVertexAttribPointer() call is stored in the currently bound VAO. This call should actually give you a GL_INVALID_OPERATION error in a core profile context, since you need to have a VAO bound when making it.
To fix this, bind the VAO after creating the id:
glGenVertexArrays(1, &spriteVAO.vaoID);
glBindVertexArray(spriteVAO.vaoID);
...
glGenerateMipmap() in wrong place
As pointed out in a comment, this call belongs after the glTexImage2D() call. It generate mipmaps based on the current texture content, meaning that you need to specify the texture data first.
This error does not cause immediate harm in your current code since you're not actually using mipmaps. But if you ever set the GL_TEXTURE_MIN_FILTER value to use mipmapping, this will matter.
glEnableVertexAttribArray() in wrong place
This needs to happen before the glDrawArray() call, because the attributes obviously have to be enabled for drawing.
Instead of just moving them before the draw call, it's even better to place them in the attribute setup code, along the glVertexAttribPointer() calls. The enabled/disabled state is tracked in the VAO, so there's no need to make these calls every time. Just set up all of this state once during setup, and then simply binding the VAO before the draw call will set all the necessary state again.
Unnecessary glVertexAttribPointer() call
Harmless, but this call in the render() method is redundant:
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
Again, this state is tracked in the VAO, so making this call once during setup is enough, once you fix the problems listed above.

It's best to simplify your problem to track down the issue. For example, instead of loading an actual image, you could just allocate a width*heigth*3 buffer and fill it with 127 to get a grey image (or pink to make it more obvious). Try coloring your fragments with the uv coordinates instead of using the sampler to see whether these values are set correctly.
I think you should enable the vertex attribute arrays before the draw call:
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
Also, before binding a texture to a texture unit, you should specify what unit to use instead of relying on the default:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureID);
And make sure you bind your sampler to that texture unit:
glUniform1i(glGetUniformLocation(ProgramID, "pxsampler"), 0);
For the sampler uniform, we're setting indices directly instead of the GL_TEXTURE* values. So GL_TEXTURE0 needs an index 0 when setting the uniform and not GL_TEXTURE0.

Related

opengl- does VAO store texrture unit calls as well

The very basic foundation for creating a VAO, VBO and applying a texture goes like this:
unsigned int VBO, VAO, EBO; //#1
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// color attribute
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
// texture coord attribute
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2); //#2
And for creating a texture:
unsigned int texture1, texture2;
// texture 1
// ---------
glGenTextures(1, &texture1); //#3
glBindTexture(GL_TEXTURE_2D, texture1); //#4
// set the texture wrapping parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// set texture filtering parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// load image, create texture and generate mipmaps
int width, height, nrChannels;
stbi_set_flip_vertically_on_load(true); // tell stb_image.h to flip loaded texture's on the y-axis.
// The FileSystem::getPath(...) is part of the GitHub repository so we can find files on any IDE/platform; replace it with your own image path.
unsigned char *data = stbi_load(FileSystem::getPath("resources/textures/container.jpg").c_str(), &width, &height, &nrChannels, 0);
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
//some more code , inside the render loop
glActiveTexture(GL_TEXTURE0); #5
glBindTexture(GL_TEXTURE0, texture1); #6
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE0, texture1);
glDrawElements(...);
glfwSwapBuffers(...);
//end of render loop
According to what I've understood from learnopengl, from line #1 to line #2, the calls are stored inside the VAO which is why we don't have to write the stuff over and over again and we only switch the VAO for drawing.
But, is the code from line #3 to line #6 also stored in the VAO? If it is, then why don't we write line #5 directly after line #3? And if it isn't how do we link a specific texture unit to a specific VAO if we are using multiple VAOs?
EDIT:
after the glBindTexture(GL_TEXTURE_2D, texture1) the the texture calls that proceed affect the currently bound texture, isn't it? Then does that mean glActiveTexture(...) also affect the currently bound texture? And why do we bind the texture again after Activating it using glBindTexture(...)?
The reason lines 1 and 2 are "stored inside the VAO" is because that's what those functions do. Those functions set state within the currently bound vertex array object.
A vertex array object, as the name suggests, is an object that deals with vertex arrays. Texture management, texture storage, and using textures for rendering have nothing to do with vertex arrays. As such, none of those functions in any way affects VAO state, in much the same way that modifying the contents of a vector<int> won't modify the contents of some list<float> object.
how do we link a specific texture unit to a specific VAO if we are using multiple VAOs?
Again, VAOs deal with vertex data. Textures aren't vertex data, so VAOs don't care about them and vice-versa. You don't link textures and VAOs.
You use VAOs and textures (among other things) to perform rendering. VAOs and textures each do different things within the process of rendering and thus have no direct relationship to one another.

C++ - OpenGL crash on glDrawElements()

So in my program I have a number of textures that I am trying to display. Earlier in my code I generate the VAO for the textures and the ibo (or index buffer) for each texture. But when I run my code it crashes at the glDrawElements() call and in nvoglv32.dll. I've read around that a bug in the nvidia driver might be causing it but I doubt it. Something is probably wrong when I generate or bind the VAO or ibo but I have no idea where. Here's the section of code where the error happens:
for (int i = 0; i < NUM_TEXTURES; i++){
glBindVertexArray(VAO_T[i]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo[i]);
glBindTexture(GL_TEXTURE_2D, texture[i]);
//error here
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, BUFFER_OFFSET(0));//error right here
}
This is the error I get when running in debug:
Unhandled exception at 0x0263FE4A in Comp465.exe: 0xC0000005: Access violation reading location 0x00000000.
Heres my code where I generate the VAO, ibo, and textures:
glGenVertexArrays(NUM_TEXTURES, VAO_T);
glGenBuffers(NUM_TEXTURES, VBO_T);
glGenBuffers(NUM_TEXTURES, ibo);
glGenTextures(NUM_TEXTURES, texture);
...
for (int i = 0; i < NUM_TEXTURES; i++){
//Tel GL which VAO we are using
glBindVertexArray(VAO_T[i]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo[i]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices[i]), indices[i], GL_STATIC_DRAW);
//initialize a buffer object
glEnableVertexAttribArray(VBO_T[i]);
glBindBuffer(GL_ARRAY_BUFFER, VBO_T[i]);
glBufferData(GL_ARRAY_BUFFER, sizeof(point[i]) + sizeof(texCoords), NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(point[i]), point[i]);
glBufferSubData(GL_ARRAY_BUFFER, sizeof(point[i]), sizeof(texCoords), texCoords);
GLuint vPosition = glGetAttribLocation(textureShader, "vPosition");
glVertexAttribPointer(vPosition, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
glEnableVertexAttribArray(vPosition);
GLuint vTexCoord = glGetAttribLocation(textureShader, "vTexCoord");
glVertexAttribPointer(vTexCoord, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(sizeof(point[i])));
glEnableVertexAttribArray(vTexCoord);
//Get handles for the uniform structures in the texture shader program
VP = glGetUniformLocation(textureShader, "ViewProjection");
//Bind the texture that we want to use
glBindTexture(GL_TEXTURE_2D, texture[i]);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
// set texture parameters
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
//Load texture
texture[i] = loadRawTexture(texture[i], TEX_FILE_NAME[i], PixelSizes[i][0], PixelSizes[i][1]);
if (texture[i] != 0) {
printf("texture loaded \n");
}
else
printf("Error loading texture \n");
}
This statement certainly looks wrong:
glEnableVertexAttribArray(VBO_T[i]);
glEnableVertexAttribArray() takes an attribute location as its argument, not a buffer id. You actually use it correctly later:
GLuint vPosition = glGetAttribLocation(textureShader, "vPosition");
...
glEnableVertexAttribArray(vPosition);
GLuint vTexCoord = glGetAttribLocation(textureShader, "vTexCoord");
...
glEnableVertexAttribArray(vTexCoord);
So you should be able to simply delete that extra call with the invalid argument.
Apart from that, I noticed a couple of things that look slightly off, or at least suspicious:
The following call is meaningless if you use the programmable pipeline, which you are based on what's shown in the rest of the code. It can be deleted.
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
This is probably just a naming issue, but textureShader needs to be a program object, i.e. the return value of a glCreateProgram(), not a shader object.
While inconclusive without seeing the declaration, I have a bad feeling about this, and a couple other similar calls:
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices[i]), indices[i], GL_STATIC_DRAW);
If indices[i] is an array, i.e. the declaration looks something like this:
indices[NUM_TEXTURES][INDEX_COUNT];
then this is ok. But if indices[i] is a pointer, or degenerated to a pointer when it was passed as a function argument, sizeof(indices[i]) will be the size of a pointer. You may want to double check that it gives the actual size of the index array. Same thing for other similar cases.

OpenGL can only bind to GL_TEXTURE0

I'm trying to bind multiple textures to my shader but when I use some other enum than GL_TEXTURE0 my shader shows unpredictable behaviour (e.g. all inout variables get zero).
This is how I bind the textures (in other projects this works prefectly)
glActiveTexture(GL_TEXTURE0);
sceneTex->bind();
glActiveTexture(GL_TEXTURE1);
depthTex->bind();
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
VBO->bind(GL_ARRAY_BUFFER);
unsigned int stride = 2 * sizeof(float) + 2 * sizeof(float);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, stride, (void*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, (void*)(2 * sizeof(float)));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
VBO->unbind(GL_ARRAY_BUFFER);
glActiveTexture(GL_TEXTURE0);
sceneTex->unbind();
glActiveTexture(GL_TEXTURE1);
depthTex->unbind();
When I remove glActiveTexture(GL_TEXTURE1) everything works fine. When I remove glActiveTexture(GL_TEXTURE0) I still get the problem. The same problem occurs if I try to use GL_TEXTURE2 or some other enum.
This is my fragment shader:
#version 440
out vec4 fragColor;
layout(binding = 0) uniform sampler2D colorMap;
layout(binding = 1) uniform sampler2D depthMap;
in vec2 texUV;
void main()
{
fragColor = texture(colorMap, texUV) / 2.0 + texture(depthMap, texUV) / 2.0;
}
When I check the states with glGetIntegerv everything seems fine. But it isn't.
I finally found the error and I have to commit that I made a real beginner mistake. Hard to believe that no one found this error.
I thought that one call of glEnable(GL_TEXTURE_2D) would be enough for all texture slots but how i found it you have to call glEnable(GL_TEXTURE_2D) for every active texture you have. So the right code looks like this:
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
sceneTex->bind();
glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
depthTex->bind();
// draw some stuff
glActiveTexture(GL_TEXTURE1);
glDisable(GL_TEXTURE_2D)
sceneTex->unbind();
glActiveTexture(GL_TEXTURE0);
glDisable(GL_TEXTURE_2D)
depthTex->unbind();

Opengl Textures without Texture image Units

recently i found an Opengl example that applies a single texture without specifying texture image unit and without sending the corresponding unit integer uniform into the shader, is it possible to apply textures without using texture units?? or it simply uses a default value for both active texture unit and its shader sampler value.
my code block (texture related):
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
GLuint TextureID = 0;
SDL_Surface* Surface = IMG_Load("Abrams_Tank.jpg");
glGenTextures(1, &TextureID);
glBindTexture(GL_TEXTURE_2D, TextureID);
int Mode = GL_RGB;
if(Surface->format->BytesPerPixel == 4) {
Mode = GL_RGBA;
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Surface->w, Surface->h, 0, GL_BGRA, GL_UNSIGNED_BYTE, Surface->pixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
GLuint vbo;
GLuint vbo_Text;
GLuint vao;
glGenBuffers(1, &vbo);
glGenBuffers(1, &vbo_Text);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData) * 30,vertexData, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)(sizeof(float) * 18));
glDrawArrays(GL_TRIANGLES, 0,6);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glBindBuffer(0, vbo);
my fragment shader:
#version 410
in vec2 texCoordout;
uniform sampler2D mysampler;
out vec4 Fcolor;
void main()
{
Fcolor = texture (mysampler, texCoordout);
}
This will work, and is within specs:
The default texture unit is GL_TEXTURE0, so there is no need to ever call glActiveTexture() if only texture unit 0 is used.
Uniforms have a default value of 0, which is the correct value for a sampler that references texture unit 0.
I would still always set the uniform values just to make it explicit. If you don't set the uniform in this case, it is also much more likely that you will miss to set it if you ever use a texture unit other than 0.

Can't render to texture (anymore)

I implemented a volume rendering demo application a few months ago. Everything worked fine in Windows XP-32bits. I used OpenGL -glew and SFML2.0-rc as a windowing&input library.
Now. I moved to windows 7-64bits just recently.
The program did not work out of the box, SFML seemed to crash. I changed the windowing library to GLFW, still using Glew. By going through the code i realized the very basic render to texture technique did not work anymore.
So i broke everything down to a minimal case so i could present it to you. (I also made a port to Qt5.0.2 to cross-check my assumptions : same diagnosis).
So here is the problem :
The program is supposed to render a simple unit cube with front-face culling to a texture in pass 1. Then in pass 2 i switch to back-face culling and render the same cube again. In the fragment shader (pass 2) i have the option to read the texture (from pass 1) and write it to the output : but i get a big black screen when i should see the front-face culled cube ...
Initialization code :
glGenFramebuffers(1, &raycastingFrameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, raycastingFrameBuffer);
glGenTextures(1, &cubeRenderTexture);
glBindTexture(GL_TEXTURE_2D, cubeRenderTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, viewWidth, viewHeight, 0, GL_RGBA, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
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, cubeRenderTexture, 0);
GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0};
glDrawBuffers(1, DrawBuffers);
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
return false;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
Render passes :
// PASS ONE :
// render the unit cube (with front face culling) to texture
// we end up with a texture whose colors represent outgoing rays locations on the box
//
glBindFramebuffer(GL_FRAMEBUFFER, raycastingFrameBuffer);
glEnable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glViewport(0, 0, viewWidth, viewHeight);
glCullFace(GL_FRONT);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderRaycasting1.getProgramID());
glBindBuffer(GL_ARRAY_BUFFER, cube_VBO_ID);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (char*)NULL + 0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (char*)NULL + 108*sizeof(float));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUniformMatrix4fv(glGetUniformLocation(shaderRaycasting1.getProgramID(), "modelview"), 1, GL_TRUE, modelview.getData());
glUniformMatrix4fv(glGetUniformLocation(shaderRaycasting1.getProgramID(), "projection"), 1, GL_TRUE, projection.getData());
glDrawArrays(GL_TRIANGLES, 0, 36);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(0);
glUseProgram(0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glEnable(GL_DEPTH_TEST);
// PASS TWO :
// render the unit cube (with back face culling this time)
// we get colors representing ray entrance locations on the box
//
glViewport(0, 0, viewWidth, viewHeight);
glCullFace(GL_BACK);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClearDepth(1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(shaderRaycasting2.getProgramID());
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, cubeRenderTexture);
glUniform1i(cubeRenderTextureID, 0);
glUniform1i(glGetUniformLocation(shaderRaycasting2.getProgramID(), "displayWidth"), (GLint) viewWidth);
glUniform1i(glGetUniformLocation(shaderRaycasting2.getProgramID(), "displayHeight"), (GLint) viewHeight);
glBindBuffer(GL_ARRAY_BUFFER, cube_VBO_ID);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (char*)NULL + 0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (char*)NULL + 108*sizeof(float));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUniformMatrix4fv(glGetUniformLocation(shaderRaycasting1.getProgramID(), "modelview"), 1, GL_TRUE, modelview.getData());
glUniformMatrix4fv(glGetUniformLocation(shaderRaycasting1.getProgramID(), "projection"), 1, GL_TRUE, projection.getData());
glDrawArrays(GL_TRIANGLES, 0, 36);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(0);
glActiveTexture(0);
glUseProgram(0);
... and finally the minimal fragment shader :
#version 330
in vec3 color;
uniform int displayWidth;
uniform int displayHeight;
uniform sampler2D cubeTex;
layout (location = 0) out vec4 outColor;
void main()
{
float viewWidth = displayWidth;
float viewHeight = displayHeight;
vec3 boxIn = color;
vec2 cubeCoord = vec2( (gl_FragCoord.x - 0.5) / viewWidth, (gl_FragCoord.y - 0.5) / viewHeight);
vec3 boxOut = texture(cubeTex, cubeCoord).rgb;
vec3 rayColor = boxOut;
outColor = vec4(rayColor, 1); // i get a black screen here ...
}
Some last words :
- Everything compiles with no warnings, no errors (same for Qt 5.0.2 port of the demo)
- I tried every possible little "tweaking" like glEnable(...), changing opengl version, using texelFetch, and what not ... obviously i can't find what's wrong with this code.
- The original code was much more complex and did run, but on XP and not on Win7.
- etc.
Did you install the original vendor drivers for your GPU as downloaded from the vendor's driver support website, or do you still have installed the crippled versions that are shipped with Windows 7?
The drivers shipping with Windows 7 do not offer modern OpenGL support. Microsoft strips them of anything OpenGL and the default OpenGL implementation of Windows-7 is just a OpenGL-1.4 emulation on top of Direct3D.
If you didn't already, then download the original drivers from your GPU's vendor and install those, then report back if this changed the outcome.