I'm working on a project that uses OpenGL 4.0 shaders.
I have to supply the call to glShaderSource() with an array of char arrays, which represents the source of the shader.
The shader compilation is failing, with the following errors:
(0) : error C0206: invalid token "<null atom>" in version line
(0) : error C0000: syntax error, unexpected $end at token "<EOF>"
Here's my (hello world) shader - straight from OpenGL 4.0 shading language cookbook
#version 400
in vec3 VertexPosition;
in vec3 VertexColor;
out vec3 Color;
void main()
{
Color = VertexColor;
gl_Position = vec4( VertexColor, 1.0 );
}
And here's my code to read the shader file into my C++ code, and compile the shader at runtime:
const int nMaxLineSize = 1024;
char sLineBuffer[nMaxLineSize];
ifstream stream;
vector<string> vsLines;
GLchar** ppSrc;
GLint* pnSrcLineLen;
int nNumLines;
stream.open( m_sShaderFile.c_str(), std::ios::in );
while( (stream.good()) && (stream.getline(sLineBuffer, nMaxLineSize)) )
{
if( strlen(sLineBuffer) > 0 )
vsLines.push_back( string(sLineBuffer) );
}
stream.close();
nNumLines = vsLines.size();
pnSrcLineLen = new GLint[nNumLines];
ppSrc = new GLchar*[nNumLines];
for( int n = 0; n < nNumLines; n ++ )
{
string & sLine = vsLines.at(n);
int nLineLen = sLine.length();
char * pNext = new char[nLineLen+1];
memcpy( (void*)pNext, sLine.c_str(), nLineLen );
pNext[nLineLen] = '\0';
ppSrc[n] = pNext;
pnSrcLineLen[n] = nLineLen+1;
}
vsLines.clear();
// just for debugging purposes (lines print out just fine..)
for( int n = 0; n < nNumLines; n ++ )
ATLTRACE( "line %d: %s\r\n", n, ppSrc[n] );
// Create the shader
m_nShaderId = glCreateShader( m_nShaderType );
// Compile the shader
glShaderSource( m_nShaderId, nNumLines, (const GLchar**)ppSrc, (GLint*) pnSrcLineLen );
glCompileShader( m_nShaderId );
// Determine compile status
GLint nResult = GL_FALSE;
glGetShaderiv( m_nShaderId, GL_COMPILE_STATUS, &nResult );
The C++ code executes as expected, but the shader compilation fails. Can anyone spot what I might be doing wrong?
I have a feeling that this may be to do with end of line characters somehow, but as this is my first attempt at shader compilation, I'm stuck!
I've read other SO answers on shader compilation, but they seem specific to Java / other languages, not C++. If it helps, I'm on the win32 platform.
You have made a mistake that others have made. This is the definition of glShaderSource:
void glShaderSource(GLuint shader, GLsizei count, const GLchar **string, const GLint *length);
The string is an array of strings. It is not intended to be an array of lines in your shader. The way the compiler will interpret this array of strings is by concatenating them together, one after another. Without newlines.
Since stream.getline will not put the \n character in the string, each of the shader strings you generate will not have a newline at the end. Therefore, when glShaderSource goes to compile them, your shader will look like this:
#version 400in vec3 VertexPosition;in vec3 VertexColor;out vec3 Color;...
That's not legal GLSL.
The proper way to do this is to load the file as a string.
std::ifstream shaderFile(m_sShaderFile.c_str());
if(!shaderFile)
//Error out here.
std::stringstream shaderData;
shaderData << shaderFile.rdbuf(); //Loads the entire string into a string stream.
shaderFile.close();
const std::string &shaderString = shaderData.str(); //Get the string stream as a std::string.
Then you can just pass that along to glShaderSource easily enough:
m_nShaderId = glCreateShader( m_nShaderType );
const char *strShaderVar = shaderString.c_str();
GLint iShaderLen = shaderString.size();
glShaderSource( m_nShaderId, 1, (const GLchar**)&strShaderVar, (GLint*)&iShaderLen );
glCompileShader( m_nShaderId );
If you copied this loading code from somewhere, then I strongly suggest you find a different place to learn about OpenGL. Because that's terrible coding.
glShaderSource( m_nShaderId, nNumLines, (const GLchar**)ppSrc, (GLint*) pnSrcLineLen );
I know the signature of glShaderSource looks tempting to send each line of the shader separately. But that's now what it's meant for. The point of being able to send is multiple arrays is so that one can mix multiple primitive shader sources into a single shader, kind of like include files. Understanding this, makes it much simpler to read in a shader file – and avoids such nasty bugs.
Using C++ you can do it much nicer and cleaner. I already wrote the follwing in Getting garbage chars when reading GLSL files
You're using C++, so I suggest you leverage that. Instead of reading into a self allocated char array I suggest you read into a std::string:
#include <string>
#include <fstream>
std::string loadFileToString(char const * const fname)
{
std::ifstream ifile(fname);
std::string filetext;
while( ifile.good() ) {
std::string line;
std::getline(ifile, line);
filetext.append(line + "\n");
}
return filetext;
}
That automatically takes care of all memory allocation and proper delimiting -- the keyword is RAII: Resource Allocation Is Initialization. Later on you can upload the shader source with something like
void glcppShaderSource(GLuint shader, std::string const &shader_string)
{
GLchar const *shader_source = shader_string.c_str();
GLint const shader_length = shader_string.size();
glShaderSource(shader, 1, &shader_source, &shader_length);
}
You can use those two functions together like this:
void load_shader(GLuint shaderobject, char * const shadersourcefilename)
{
glcppShaderSource(shaderobject, loadFileToString(shadersourcefilename));
}
Just a quick hunch:
Have you tried calling glShaderSource with NULL as length parameter? In that case OpenGL will assume your code to be null-terminated.
(Edited because of stupidity)
Related
I know that this "sometimes" is a little bit against the rules of stack overflow, but I don't know how to describe my problem in a better way:
I have this code:
static const std::string parseShader(const std::string &fileName){
std::ifstream ifs(fileName);
std::stringstream buffer;
buffer << ifs.rdbuf();
std::string s = buffer.str();
return s;
}
And in my main function I have this code:
const GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
const char* vertex_shader_text = parseShader("res/shaders/basic.vertex.glsl").c_str();
std::cout << "Vertex shader length is " << strlen(vertex_shader_text) << std::endl;
glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL);
glCompileShader(vertex_shader);
const GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
const char* fragment_shader_text = parseShader("res/shaders/basic.fragment.glsl").c_str();
std::cout << "Fragment shader length is " << strlen(fragment_shader_text) << std::endl;
glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL);
glCompileShader(fragment_shader);
So, if I execute my program, without moving files or change any code, sometimes I get:
Vertex shader length is 160
Fragment shader length is 90
And sometimes:
Vertex shader length is 0
Fragment shader length is 0
And even, most of times
Vertex shader length is 160
Fragment shader length is 0
So, seems like some part of file reading would be asynchronous and slower than the rest of the program. I'm working with C++17, CLion and MacOS Mojave, as additional information....
Also, when both files are read correctly, then the image in opengl (a triangle) is correctly painted, but when some of the files are incorrectly read, nothing is shown.
The problem is that the string object that the function returns is temporary and will end its life-time almost immediately. That will leave you with a pointer to a string that no longer exists.
Use a std::string as the destination instead, and only use the c_str member function to get a pointer when absolutely needed.
The following GLSL fragment shader compiles and works as expected:
#version 330 core
out vec3 color;
in float U;
in vec4 vertexNormal_worldSpace;
uniform sampler1D TextureSampler;
uniform vec4 LightPos;
void main()
{
float cosT = dot(normalize(vertexNormal_worldSpace.xyz),normalize(LightPos.xyz));
color = cosT * texture(TextureSampler,U).rgb;
}
However, when I change line 9 to clamp the value of "cosT" between 0 and 1:
float cosT = clamp(dot(normalize(vertexNormal_worldSpace.xyz),normalize(LightPos.xyz)),0.0,1.0);
I get the errors:
0(1) : error C0000: syntax error, unexpected integer constant, expecting "::" at token "<int-const>"
0(10) : error C7532: global function texture requires "#version 130" or later
This appears to say the error appears on the first line, but nothing has changed there at all. Furthermore the 2nd error suggests that there is an issue with the version of GLSL I am using, however #version 330 core should be a later version than #version 130, as the error message states.
EDIT:
This is my code for loading in shaders:
static GLuint LoadShaders(const char* vertex_file_path, const char* frag_file_path){
GLuint VertID = glCreateShader(GL_VERTEX_SHADER);
GLuint FragID = glCreateShader(GL_FRAGMENT_SHADER);
char const* VertPointer = ReadShaderFile(vertex_file_path);
char const* FragPointer = ReadShaderFile(frag_file_path);
glShaderSource(VertID,1,&VertPointer,NULL);
glCompileShader(VertID);
GLint Result = GL_FALSE;
int InfoLogLength;
glGetShaderiv(VertID, GL_COMPILE_STATUS, &Result);
glGetShaderiv(VertID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if ( InfoLogLength > 0 ){
std::vector<char> VertexShaderErrorMessage(InfoLogLength+1);
glGetShaderInfoLog(VertID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
printf("%s\n", &VertexShaderErrorMessage[0]);
}
glShaderSource(FragID,1,&FragPointer,NULL);
glCompileShader(FragID);
glGetShaderiv(FragID, GL_COMPILE_STATUS, &Result);
glGetShaderiv(FragID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if ( InfoLogLength > 0 ){
std::vector<char> FragmentShaderErrorMessage(InfoLogLength+1);
glGetShaderInfoLog(FragID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
printf("%s\n", &FragmentShaderErrorMessage[0]);
}
GLuint ProgramID = glCreateProgram();
glAttachShader(ProgramID,VertID);
glAttachShader(ProgramID,FragID);
glLinkProgram(ProgramID);
glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if ( InfoLogLength > 0 ){
std::vector<char> ProgramErrorMessage(InfoLogLength+1);
glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
printf("%s\n", &ProgramErrorMessage[0]);
}
return ProgramID;
}
static char const* ReadShaderFile(const char* path){
std::string ShaderCode;
std::ifstream ShaderStream(path,std::ios::in);
if(ShaderStream.is_open()){
std::string line = "";
while (std::getline(ShaderStream,line)){
ShaderCode +="\n"+line;
}
ShaderStream.close();
return ShaderCode.c_str();
}else{
return 0;}
}
This is basically straight from the tutorial I am following link, only change is putting the file reading in the ReadShaderFile function.
EDIT 2:
My OpenGL context is created with the following version:
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR,3);
The problem is indeed the shader loader. You are adding with this statement
ShaderCode +="\n"+line;
a newline character (\n) before each line which moves everything one line down compared to your input.
Since the version statement has to be in the first line of a shader and you move it to the second, the statement seems to be ignored by your driver. My NVIDIA driver, for example, states:
error C0204: version directive must be first statement and may not be repeated
A simple fix would be to add to newline character after each line instead of before, but I would strongly encourage you not to read whole files line by line since this will give you a terrible performance. For example, the ShaderCode variable will be resized for each line which means you get a memory allocation and a copy operation for the whole string. Have a look at this question on how to read complete files.
Edit:
Another problem is that you're returning the c_str() pointer of local variable. When the ReadShaderFile method ends, the std::string ShaderCode variable goes out of scope (thus frees it's content) and the returned pointer points to an invalid memory address.
Solution: Return the std::string object instead of the const char pointer to its content.
This question has been asked multiple times in different ways. My question is specific to OpenGL 2.0 / GLSL 1.10 and higher and potential compatibility with OpenGL ES 2.0 and its supported version of GLSL:
What are recommended C/C++ APIs used to combine multiple files into one shader source to pass into glShaderSource.
For example, if I have 3 files A.frag, B.frag, C.frag that have the following contents:
/* A.frag */
struct A
{
vec3 val;
};
/* B.frag */
struct B
{
vec3 val;
};
/* C.frag */
void main() {
struct A a;
a.val = vec3(1.0, 1.0, 1.0);
struct B b;
b.val = vec3(1.0, 1.0, 1.0);
float f = dot(a.val, b.val);
}
What already existing tools would allow me to combine all three file's contents into one source so it could be compiled? Take into consideration that each shader source could be far more complex.
If you look at the function specification glShaderSource:
void glShaderSource(GLuint shader,
GLsizei count,
const GLchar **string,
const GLint *length);
You see that the third parameter is an array of array of string (chars).
I wrote a c++ class to achieve what you are trying to do. The best approach I found is to load the files separately and then put in a double pointer the different shader source codes and then finally pass it to the function glShaderSource.
For example:
GLchar** fragment_code; // This must be initialized
GLint* fragment_char_count;
void LoadFragment()
{
//assuming fragment_filepath a vector<string> containing different shader filepaths
for (size_t i = 0; i < fragment_filepath.size(); i++)
{
Load(fragment_filepath[i], fragment_code[i], fragment_char_count[i]);
}
}
// source_path is the shader file path in the filesystem
// output will contain the source code of the shader being loaded
// fragment_char_count will contain the number of char for the shader being loaded
static void Load(string source_path, GLchar*& output,GLint& fragment_char_count)
{
string return_code;
try
{
// Open files
ifstream vShaderFile(source_path);
stringstream vShaderStream;
// Read file's buffer contents into streams
vShaderStream << vShaderFile.rdbuf();
// close file handlers
vShaderFile.close();
// Convert stream into GLchar array
return_code = vShaderStream.str();
}
catch(exception e)
{
cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ: " << source_path << endl;
}
fragment_char_count = return_code.length() ;
output = new GLchar[count + 1];
std::size_t length = return_code.copy(output,fragment_char_count,0);
output[length]= '\0';
}
// eventually when you compile you pass to glShaderSource the fragment_id, the number of shader program you are loading (fragment_filepath.size()), the double pointer of char containing the different source codes (fragment_code) and an array representing the char count for each source (fragment_char_count)
void CompileFragment()
{
glShaderSource(fragment_shader_id, fragment_filepath.size(), fragment_code, fragment_char_count);
glCompileShader(d_vertex_shader);
}
It's not very straightforward, unfortunately OpenGL is still written in plain C. However Here you can find a gist with the whole class implemented. By doing so you can compile and link several shaders all together (as long as they are all of the same type of course).
hope it helps.
The problem is that, shaders (pretty simple ones, as I'm learning OpenGL) fail to compile in a seemingly random manner (and gives random error messages * ).
The same shaders, however, compile after about 3 or 4 tries.
Here is the code:
Shader::Shader(GLenum Type,std::string filename)
{
shader_type = Type;
std::ifstream ifs(filename);
if(!ifs)
throw(std::runtime_error("File:"+filename+" not opened."));
std::ostringstream stream;
stream<<ifs.rdbuf();
const GLchar* data = stream.str().c_str();
handle = glCreateShader(shader_type);
glShaderSource(handle,1,static_cast<const GLchar**>(&data),0);
glCompileShader(handle);
int status;
glGetShaderiv(handle,GL_COMPILE_STATUS,&status);
if(status == GL_FALSE)
{
int loglength;
glGetShaderiv(handle,GL_INFO_LOG_LENGTH,&loglength);
auto data = new char[loglength];
glGetShaderInfoLog(handle,loglength,&loglength,data);
std::string strdata(data);
delete [] data;
throw(std::runtime_error(strdata));
}
}
Note that the shaders aren't missing newlines at the end, has an extra space after the last semicolon and uses tabs instead of spaces. (as suggested in various old posts over the internet!).
Here are two error messages produced from the same vertex shader here, not at the same time:
#version 330
in vec2 Position;
uniform mat4 transform;
void main()
{
gl_Position = transform*vec4(Position,0.0f,1.0f);
}
Errors:
0(1) : error C0000: syntax error, unexpected $undefined at token "<undefined>"
0(6) : error C0000: syntax error, unexpected '!', expecting ',' or ')' at token "!"
And sometimes it just works !
Is it a problem with my drivers ?
(I'm using the recent 302.x stable nvidia binary drivers on Arch Linux 64 bit, with an aged 9600 GSO card )
P.S: The code works as expected ,whenever the shader compiles correctly, so I think it shoud be correct.
I'll be happy to post a working(sometimes !) example as a zip file if the problem can't be found from this, and someone wants to take a look.
const GLchar* data = stream.str().c_str();
This is bad. If you want the string's data, you need to store it. str will return a copy of the buffer, which you then get a pointer to with c_str. Once that temporary is destroyed (at the end of this line), that pointer will point to memory you no longer have access to.
The correct code is this:
std::string dataString = stream.str();
const GLchar *data = reinterpret_cast<GLchar*>(dataString.c_str());
I am writing my first program using OpenGL, and I have gotten to the point where I am trying to get it to compile my extremely simple shader program. I always get the error that it failed to compile the shader. I use the following code to compile the shader:
struct Shader
{
const char* filename;
GLenum type;
GLchar* source;
};
...
static char* readShaderSource(const char* shaderFile)
{
FILE* fp = fopen(shaderFile, "r");
if ( fp == NULL ) { return NULL; }
fseek(fp, 0L, SEEK_END);
long size = ftell(fp);
fseek(fp, 0L, SEEK_SET);
char* buf = new char[size + 1];
fread(buf, 1, size, fp);
buf[size] = '\0';
fclose(fp);
return buf;
}
...
Shader s;
s.filename = "<name of shader file>";
s.type = GL_VERTEX_SHADER;
s.source = readShaderSource( s.filename );
GLuint shader = glCreateShader( s.type );
glShaderSource( shader, 1, (const GLchar**) &s.source, NULL );
glCompileShader( shader );
And my shader file source is as follows:
#version 150
in vec4 vPosition;
void main()
{
gl_Position = vPosition;
}
I have also tried replacing "in" with "attribute" as well as deleting the version line. Nothing compiles.
Note:My actual C program compiles and runs. The shader program that runs on the GPU is what is failing to compile.
I have also made sure to download my graphics card's latest driver. I have an NVIDIA 8800 GTS 512;
Any ideas on how to get my shader program (written in GLSL) to compile?
As wrote in comments, does compile shader output anything to console? To my surprise, while I was using ATI, I got message that shader program compiled successfuly, however when I started using Nvidia, I was staring at screen for first time, because nothing got output... however shaders were working. So maybe you are successfuly compiling and just don't take it? And if they are not working in context you try to use shader program and nothing happens, I think you're missing the linking of shader (it may be further in code however). Google has some good answers on how to correctly perform every step, you can compare your code to this example. I also made an interface for working whit shaders, you can take a look my UniShader. Project lacks english documentation and is mainly used for GPGPU, but you can easily load any shader, and the code itself is written whit english naming, so it should be quite comfortable. Look in folder UniShader in that zip for source codes. There are also few examples, the one named Ukazkovy program na GPGPU got also source included so you can look how to use those classes.. good luck!