I took a look at your code. The main problem seems to be that in the ShaderProgram::InstantiateShader member function defined, your intention seems to be that for the “GLuint shaderID” parameter, you want to create a shader object, and assign it to this parameter in a way that it updates some private member variable called “GLuint _Shaders[2]”.
However, your code doesn’t do that. It does assign the parameter variable to the integer name returned by glCreateShader, but it has no interaction with your _Shaders array. It looks like you thought a line like “InstantiateShader(_Shaders[0], GL_VERTEX_SHADER, “Mesh/Mesh.vert”)” would do an assignment like this, but it doesn’t. In this case, it only passes the first element of your _Shaders array into the function, and that’s it. What’s passed is copied into the function and has no relevance to the array anymore. So, I modified your code with the intention of assigning the shader object names to the _Shaders array.
Overall, it’s a C++ problem, not an OpenGL one. If you just want my solution, here’s a dropbox link to it. I’m seeing a window with a pink background, along with a console window. Hopefully that’s right.
You seemed to have used Visual Studio 2017. I only have 2015. If you use my VS solution, I think you should get a prompt message to upgrade the solution. Other than that, it should work the same. Also, again, I’m only familiar with OpenGL 3.3 core, and I edited the code to test it based on that. My card can only support up to 4.0. You say you can use OpenGL 4.5, so 3.3 core should run too.
If you want to see 4.5 run, modify the DisplayManger::Init changes I made to request an OpenGL 4.5 core profile (I assume you want core, you didn’t mention a profile in your shaders, and according to the GLSL 4.5 spec, that would mean those shaders would be for 4.5 core). I made a note about changes to the Init function below. And change your shaders back to your own source. Again, I think this’ll work, but I don’t have 4.5, so you have to see for yourself.
That’s the summary of it. Below are be my complete notes on debugging your code, so you know what I changed.
===
First note, I’ve never used SDL seriously. The only time I’ve ever actually used it was to try to find out why some person was getting a link error in Visual Studio, and the person said he/she used SDL. I thought I could help with the link error, so I looked into SDL for that. Here’s the link to that post, if you’re interested. Anyways, I made my own notes the best I could about SDL in this code.
===
File-wise, looking at your header files, it looks like you planned to use the dll versions of GLEW and SDL. In that case, for GLEW, in your Dependencies/libs folder, I’m guessing glew32s.lib is your GLEW static library. Since you didn’t use it, I deleted it. From my knowledge of SDL, I’ve never seen an “SDL2test.lib” required. Not sure what that’s for or where that came from, but the name suggests it was just for some certain test, so I deleted that too.
Also, neither of these libraries were mentioned in your VS Project Properties. So I thought that was a reason too to safely delete them. I did this when I was debugging so I could minimize the files to think about.
The main problem though is that you forgot to include the dll files in your Github repo. I had a spare glew32.dll and an SDL dll from that forum post I mentioned. Don’t know if they’re the right versions. They seemed to work, so it was okay in the end. Nonetheless, make sure to include your dlls in your own repo. I put the DLLs in the 2D Platformer folder, along with your source files.
===
Looking at your project properties, you only used the Debug x86 mode in Visual Studio. I also kept it to Debug x86.
===
So the main problem I mentioned in the beginning of this reply was that it looks like you wanted to do array asisgnment in your ShaderProgram::InstantiateShader member function, but you did it incorrectly.
In your code, you had this snippet in your ShaderProgram::Init function:
//Create and compile shaders.
InstantiateShader(_Shaders[0], GL_VERTEX_SHADER, "Mesh/Mesh.vert");
InstantiateShader(_Shaders[1], GL_FRAGMENT_SHADER, "Mesh/Mesh.frag");
And the first lines of code definition for your ShaderProgram::InstantiateShader function were:
void ShaderProgram::InstantiateShader(GLuint shaderID, const GLenum shaderType, const std::string dataPath)
{
//Create the shader.
shaderID = glCreateShader(shaderType);
...
You also had a ShaderProgram::LinkShaders functions that did this:
void ShaderProgram::LinkShaders()
{
//Attach the shaders. the shaders.
int shaderCount = sizeof(_Shaders) / sizeof(GLuint);
for (int i = 0; i < shaderCount; i++)
{
glAttachShader(_ShaderProgramID, _Shaders[i]);
}
//Link the program.
glLinkProgram(_ShaderProgramID);
}
So my guess at your intentions was that you wanted InstantiateShader to create shader objects whose integer names would be put into an array called _Shaders. And you later wanted to use this array in LinkShaders to attach the shader objects to some program object, where you would then link the program object. However, a line like “InstantiateShader(_Shaders[0], GL_VERTEX_SHADER, “Mesh/Mesh.vert”)” only passes an integer to the “shaderID” parameter in your InstantiateShader function. There is no sense of assigning to the _Shaders array in doing this, and there is no mention of assigning to that array either in your actual InstantiateShader function. So I modified the and definiton and function call like so in your ShaderProgram.cpp:
(changed header definition in ShaderProgram.h as well)
void ShaderProgram::InstantiateShader(int index_to_store_shader_in, const GLenum shaderType, const std::string dataPath)
{
//Create the shader.
//shaderID = glCreateShader(shaderType);
GLuint shaderID = glCreateShader(shaderType);
_Shaders[index_to_store_shader_in] = shaderID;
//Creates, compiles and links the shaders.
void ShaderProgram::Init()
{
//Create the shader program.
_ShaderProgramID = glCreateProgram();
//Create and compile shaders.
InstantiateShader(0, GL_VERTEX_SHADER, "Mesh/Mesh.vert");
InstantiateShader(1, GL_FRAGMENT_SHADER, "Mesh/Mesh.frag");
...
}
The InstantiateShader code now expects the first parameter to be the index you want to store a shader object name in. I guess that’s what you want.
Anyways, that’s the fix. An std::vector can work well here, since you can change the size of it to use more shader object names. An array is just fine. You just need to be careful with indexing. Maybe a member variable mentioning the size of this array would be a good idea here, so you can explicitly reference it. You can make the array super large as well, so you don’t have to worry about running out of indices.
===
In your post, you said you got the logging error “Program failed to linkL:”. I took a look at your code. Your ShaderProgram::LinkShaders call in ShaderProgram::Init is where you link all your shaders. I found some ShaderProgram::CheckShaderError that seems to be used also for checking the program linking status. I thought there’d be that call after “LinkShaders()”, but I didn’t see anything there.
I don’t know how you got that error message about program linking failure if you didn’t actually call CheckShaderError to check it. Maybe I missed something. Nonetheless, I just added the call with the arguments for a program check instead of a shader check.
void ShaderProgram::Init()
{
...
LinkShaders();
CheckShaderError(_ShaderProgramID, true);
}
===
With the main problem out of the way, a first side detail I wanted to mention was that since I can’t use 4.5 (I never tried it, and my card can’t support it), I looked up the SDL docs to explicitly request a 3.3 core context. I was able to do this in your DisplayManager::Init function, with the SDL_GL_SetAttribute function provided by SDL.
I also noticed you had a “SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1)” call at the end of your Init funciton, after SDL_CreateWindow. I moved it to before the SDL_CreateWindow call, next to my GL version request call. The docs for SDL_GL_SetAttribute say that “The requested attributes should be set before creating an OpenGL window”. It also makes sense to actually request certain OpenGL window and rendering context attributes before actual creating the window and context.
So anyways, I changed DisplayManger::Init to this:
void DisplayManager::Init()
{
//Create the window.
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
//Make sure to enable the doublebuffer.
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
...
}
===
With using OpenGL 3.3 core, I changed your vertex and fragment shaders to use 3.3 core:
Mesh.vert
#version 330 core
void main()
{
}
Mesh.frag
#version 330 core
void main()
{
}
===
During debugging, I got some shader compilation errors, probably from attempting to use 4.5. I changed the last else statement in ShaderProgram::CheckShaderError to use a GLchar array instead of a vector because my Debugger was only showing the first character in your GLchar vector. I wanted to see a more complete message:
void ShaderProgram::CheckShaderError(const GLuint shaderID, const bool isProgram)
{
...
else {
glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &maxLength);
//std::vector<GLchar> errorlog(maxLength);
//glGetShaderInfoLog(shaderID, maxLength, &maxLength, &errorlog[0]);
GLchar e[1024];
glGetShaderInfoLog(shaderID, maxLength, &maxLength, e);
FatalError();
...
}
===
In your ShaderProgram::ReadShaderFromFile function, the build output from Visual Studio had a warning about not all control paths returning a value. I modified your last else statement to return an empty std::string, to get rid of the warning. Change it as you want:
std::string ShaderProgram::ReadShaderFromFile(std::string dataPath)
{
...
else
{
printf("Failed to read the shader");
FatalError();
std::string dummy_return = "";
return dummy_return;
}
...
}
===
Lastly, in your ShaderProgram::InstantiateShader function, your line about assigning to your shaderSource string variable was showing garbage in my debugger. The line looks okay though, may have worked for you. Anyways, I made an explicit std::string variable to load the string from ReadShaderFromFile before assigning its C string to shaderSource:
void ShaderProgram::InstantiateShader(int index_to_store_shader_in, const GLenum shaderType, const std::string dataPath)
{
...
//Set the shadersource.
//const GLchar* shaderSource = (const GLchar*)ReadShaderFromFile("../2D Platformer/Shaders/" + dataPath).c_str();
std::string a = ReadShaderFromFile("../2D Platformer/Shaders/" + dataPath).c_str();
const GLchar* shaderSource = a.c_str();
...
}