3
votes

After following a set of OpenGL tutorials which were great but didn't let me understand the basics, I'm trying some very basic OpenGL coding with C++. My program is supposed to read a vertex and fragment shader and draw a triangle.

I get an error when linking the shaders (I suspect the error can be tracked down to the compiling of the shader though). I know my shaders are read by my program, but any changes to them doesn't affect my error. By running:

glGetProgramiv(ShaderProgram, GL_LINK_STATUS, &Success);

I receive the error: "Link called without any attached shader object". The programs builds, and my triangle shows, but is not affected by the shaders.

UPDATE

I no longer get the above error after fixing a mistake. I now get a complain after glCompileShader():

"Error: 0:3 'location' : syntax error parse error"

So I imagine it has to do with my shader files (will add them below). The shader files are taken from a tutorial, so I assumed they would work.

Shader files:

Vertex shader:

#version 330                                                            

layout (location = 0) in vec3 Position;                                 

void main()                                                             
{                                                                       
    gl_Position = vec4(0.5*Position.x, 0.5*Position.y, Position.z, 1.0);    
}

Fragment shader:

#version 330                                                            

out vec4 FragColor;                                                     

void main()                                                             
{                                                                       
    FragColor = vec4(1.0, 0.0, 0.0, 1.0);                               
}

In my main function i run:

compileShader();

Attach shaders:

static void AddShader(GLuint ShaderProgram, GLenum ShaderType, std::string filePath){

//create shader object
GLuint ShaderObj = glCreateShader(ShaderType);
//error if no shader
if (ShaderObj == 0){
    fprintf(stderr, "Error creating shader type %d\n", ShaderType);
    exit(0);
}

//"specify source code"
//readShaderFile returns the shader file as a string
std::string shaderFile = readShaderFile(filePath);
const char* shaderFilePointer = shaderFile.c_str();
GLint ShaderFileLength[1];  
ShaderFileLength[0] = strlen(shaderFilePointer);

glShaderSource(ShaderObj, 1, &shaderFilePointer, ShaderFileLength);

//compile the shader
glCompileShader(ShaderObj);

//check if compile successful
GLint success;
glGetShaderiv(ShaderObj, GL_COMPILE_STATUS, &success);
if (!success){
    GLchar InfoLog[1024];
    glGetShaderInfoLog(ShaderObj, sizeof(InfoLog), NULL, InfoLog);
    fprintf(stderr, "Error compiling shader type %d: '%s'\n", ShaderType, InfoLog);
    exit(1);
}

glAttachShader(ShaderProgram, ShaderObj);

}

And here are the functions used:

static void compileShaders(){

//create program
GLuint ShaderProgram = glCreateProgram();
//check error
if (ShaderProgram == 0){
    fprintf(stderr, "Error creating shader program!\n");
    exit(1);
}

//attach compiled shaders
std::string vertexShaderFilePath    = "Shaders/colShading.vert";
std::string fragShaderFilePath      = "Shaders/colShading.frag";

AddShader(ShaderProgram, GL_VERTEX_SHADER, vertexShaderFilePath);
AddShader(ShaderProgram, GL_FRAGMENT_SHADER, fragShaderFilePath);

GLint Success = 0;
GLchar ErrorLog[1024] = { 0 };

//link shader to program
glLinkProgram(ShaderProgram);
//check link error
glGetProgramiv(ShaderProgram, GL_LINK_STATUS, &Success);

if (Success == 0) {
    glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
    fprintf(stderr, "Error linking shader program: '%s'\n", ErrorLog);
}

//use the linked shader program
glUseProgram(ShaderProgram);
}

I doubt more of the code is needed to find the problem here, but just let me know. Thanks a lot in advance!

SOLUTION

The first answer below did the trick. I removed:

layout (location = 0) 

from the vertex shader and then added:

glBindAttribLocation(ShaderProgram, 0, "Position");

before the linking.

1
Do you have any attributes that you'd need to call glBindAttribLocation for? - developerbmw
No, the shader files are very simple and if understood correctly it is not needed. And more importantly, that I forgot to mention; any changes to my shader files doesn't affect anything. I still get the same error. - remi
Could you post your shaders? - developerbmw
Have you tried changing the if (ShaderObj = 0){ line to use comparison (==) instead of assignment (=)? It is possible it is then reassigning ShaderObj to be always 0, which possibly does not exist, or has the wrong type. - Sebastian Mendez
Wow, that was bad mistake of me. I don't know how I missed it. But after fixing it, it still didn't run properly, so will check that and update the question. Thanks @SebastianMendez - remi

1 Answers

3
votes

It seems that either your hardware or your driver doesn't support the explicit shader location syntax, which require OpenGL/GLSL > 3.3.

To solve this, if samgak's answer doesn't help, you still have two options:

  1. Explicitly set the locations before linking the shader:

This is done with the glBindAttribLocation function, and means basically the same as what you have in your shader. Example:

AddShader(ShaderProgram, GL_VERTEX_SHADER, vertexShaderFilePath);
AddShader(ShaderProgram, GL_FRAGMENT_SHADER, fragShaderFilePath);
//...

//Define the location of the attributes
glBindAttribute(ShaderProgram, 0, "Position") //Bind "in Position" to location 0

//link shader to program
glLinkProgram(ShaderProgram);
  1. After linking, and while building your VAOs (or drawing the geometry), query the location of the attributes, and set the buffers accordingly:

You do this with the glGetAttribLocation function.

GLuint positionLoc = glGetAttribLocation(ShaderProgram, "Position");// Ask for the location of the attribute "Position

//Create buffers and transfer data...
glBindBuffer(gl.ARRAY_BUFFER, ...);


glBufferData(...);

//Turns on the vertex attribute for "Position" 
glEnableVertexAttribArray(positionLoc);

//Set the pointer between buffer and attribute 
glVertexAttribPointer(positionLoc,...);

For performance reasons, the first option is recommended, as it doesn't force a flush.