Following an OpenGL tutorial,
in a part, where it creates a vertex shader, it uses the following method,
const GLchar* VertexShader =
{
"#version 330\n"\
"layout(location=0) in vec4 in_Position;\n"\
"layout(location=1) in vec4 in_Color;\n"\
"out vec4 ex_Color;\n"\
"void main(void)\n"\
"{\n"\
" gl_Position = in_Position;\n"\
" ex_Color = in_Color;\n"\
"}\n"
};
I just replaces this code, with my code which brings the shader from a file,
string readShaderFile(string FileName)
{
string ShaderString = "";
ifstream shaderFile;
shaderFile.open(FileName);
while(!shaderFile.eof())
{
string tempholder;
getline(shaderFile, tempholder);
ShaderString.append(tempholder);
ShaderString.append("\n");
}
shaderFile.close();
return ShaderString;
}
const GLchar *VertexShader = readShaderFile("v.vert").c_str();
and BANG!
the code doesn't work anymore. What could be the issue?
the v.vert file contains the following code:
#version 330
layout(location=0) in vec4 in_Position;
layout(location=1) in vec4 in_Color;
out vec4 ex_Color;
void main(void)
{
gl_Position = in_Position;
ex_Color = in_Color;
}
You don't have to split the read file into lines, just pass the whole file as it is. Also those '\n' are in the C code, because you can't have ordinary newlines in a C string. You must escape them. But this is not neccesary reading from a file.
And then you have a problem here:
const GLchar *VertexShader = readShaderFile("v.vert").c_str();
readShaderFile returns a std::string that goes out of scope and the compiler may deconstrung the string instance right there. You must store the returned string in it's own variable and keep that around as long as you want to use its c_str();
Since this is supposedly on the global scope change it like this:
static std::string strVertexShader = readShaderFile("v.vert");
GLchar const *VertexShader = strVertexShader.c_str();
Your function to load the shader should be something like this :
string readShaderFile(const string fileName)
{
std::ifstream shaderFile( fileName.c_str() );
// find the file size
shaderFile.seekg(0,std::ios::end);
std::streampos length = shaderFile.tellg();
shaderFile.seekg(0,std::ios::beg);
// read whole file into a vector:
std::vector<char> buffer(length);
shaderFile.read(&buffer[0],length);
// return the shader string
return std::string( buffer.begin(), buffer.end() );
}
Also, what are you doing after this line :
const GLchar *VertexShader = readShaderFile("v.vert").c_str();
?
The temporary string gets destroyed, and the VertexShader contains a dangling pointer. What you need to do is this :
const std::string shaderProgram = readShaderFile("v.vert");
const GLchar *VertexShader = shaderProgram.c_str();
// create shader program
// etc
Related
From https://learnopengl.com/Getting-started/Shaders I read:
Shaders always begin with a version declaration, ...
But on OpenGL Window Example there is no version on shader code. What's the version then?
static const char *vertexShaderSource =
"attribute highp vec4 posAttr;\n"
"attribute lowp vec4 colAttr;\n"
"varying lowp vec4 col;\n"
"uniform highp mat4 matrix;\n"
"void main() {\n"
" col = colAttr;\n"
" gl_Position = matrix * posAttr;\n"
"}\n";
static const char *fragmentShaderSource =
"varying lowp vec4 col;\n"
"void main() {\n"
" gl_FragColor = col;\n"
"}\n";
Following seems to be related: How to Convert GLSL #version 330 core to GLSL ES #version 100?
In qt 5.12.2 source file : qopenglshaderprogram.cpp
LINE 604:
bool QOpenGLShader::compileSourceCode(const char *source){
Q_D(QOpenGLShader);
// This method breaks the shader code into two parts:
// 1. Up to and including an optional #version directive.
// 2. The rest.
// If a #version directive exists, qualifierDefines and redefineHighp
// are inserted after. Otherwise they are inserted right at the start.
// In both cases a #line directive is appended in order to compensate
// for line number changes in case of compiler errors.
if (d->shaderGuard && d->shaderGuard->id() && source) {
const QVersionDirectivePosition versionDirectivePosition = findVersionDirectivePosition(source);
QVarLengthArray<const char *, 5> sourceChunks;
QVarLengthArray<GLint, 5> sourceChunkLengths;
QOpenGLContext *ctx = QOpenGLContext::currentContext();
if (versionDirectivePosition.hasPosition()) {
// Append source up to and including the #version directive
sourceChunks.append(source);
sourceChunkLengths.append(GLint(versionDirectivePosition.position));
} else {
// QTBUG-55733: Intel on Windows with Compatibility profile requires a #version always
if (ctx->format().profile() == QSurfaceFormat::CompatibilityProfile) {
const char *vendor = reinterpret_cast<const char *>(ctx->functions()->glGetString(GL_VENDOR));
if (vendor && !strcmp(vendor, "Intel")) {
static const char version110[] = "#version 110\n";
sourceChunks.append(version110);
sourceChunkLengths.append(GLint(sizeof(version110)) - 1);
}
}
}
and
static QVersionDirectivePosition findVersionDirectivePosition(const char *source){
Q_ASSERT(source);
// According to the GLSL spec the #version directive must not be
// preceded by anything but whitespace and comments.
// In order to not get confused by #version directives within a
// multiline comment, we need to do some minimal comment parsing
// while searching for the directive.
If version is not available, "#version 110\n" will be added.
static const char version110[] = "#version 110\n";
sourceChunks.append(version110);
I've read all recommended posts, I've tried those solutions, but non of them helped.
In short problem lies in the third argument of
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
This code work:
const char *vertexShaderSource = "#version 120 \n"
"attribute vec3 pos;\n"
"attribute vec2 texCoord;\n"
"varying vec2 texCoord0;\n"
"uniform mat4 transform;\n"
"void main()\n"
"{\n"
" gl_Position = transform * vec4(pos, 1.0);\n"
" texCoord0 = texCoord;\n"
"}\0";
But I want to read it from a file, following code works
std::string s= "vertex";
std::ifstream file(s.c_str());
std::stringstream buffer;
buffer << file.rdbuf();
std::string str = buffer.str();
std::cout << str;
And is outputing:
#version 120
attribute vec3 pos;
attribute vec2 texCoord;
varying vec2 texCoord0;
uniform mat4 transform;
void main()
{
gl_Position = transform * vec4(pos, 1.0);
texCoord0 = texCoord;
}
I know that I cannot just simply convert string with code like this:
const char *vertexShaderSource = str.c_str();
And pass it into: glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
So I've used following code to prevent it from ceasing to exist:
char * writable = new char[str.size() + 1];
std::copy(str.begin(), str.end(), writable);
writable[str.size()] = '\0';
Passing glShaderSource(vertexShader, 1, &writable, NULL);does not work also.
I'm getting following error all the time, even with another copy&paste code from tutorials
0:1(4): preprocessor error: syntax error, unexpected HASH_TOKEN
What else I can do?
I've read these posts:
Reading a shader from a .txt file using a structure
read GLSL shaders from file
Here is the code I use to load shader sources:
ifstream iStream(filename);
stringstream buffer;
buffer << iStream.rdbuf();
string source = buffer.str();
const char* sources[] = { source.c_str() };
glShaderSource(handle, 1, sources, 0);
As you can see, the string can be given directly to glShaderSource; there is no need to create a copy of it. Have a look at the spec for c_str() if you're not convinced.
Not sure what the problem is in your case. I have used both \r\n and \n line endings successfully. Maybe you have a U+FEFF BOM character at the start of the file? It could with the the "hash" mentioned in the compilation error.
This question already has answers here:
What is the lifetime of the result of std::string::c_str()?
(7 answers)
Closed 7 years ago.
I'm new to OpenGL and I was trying to write a Shader class that loads the shaders from a file and compiles them. The problem is, neither of them are compiling. Both shows error messages like this:
0:1(1): error: syntax error, unexpected $end
I've searched many questions here with the same problem but none of them has worked. The code is below:
Shader.h
#ifndef SHADER_H
#define SHADER_H
#include <GL/glew.h>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
class Shader {
public:
Shader(const GLchar* path);
GLint Compile(GLenum type);
GLuint GetID() const;
const char* GetShaderCode() const;
void InfoLog() const;
private:
GLuint shaderId;
const char* shaderCode;
int shaderCodeLength;
char log[512];
};
#endif
Shader.cpp (the functions where the problem is)
#include "Shader.h"
Shader::Shader(const GLchar* path) {
std::string shaderString;
std::ifstream shaderFile;
std::stringstream shaderStream;
shaderFile.exceptions(std::ifstream::badbit);
try {
shaderFile.open(path);
shaderStream << shaderFile.rdbuf();
shaderFile.close();
shaderString = shaderStream.str();
} catch (std::ifstream::failure e) {
std::cout << "Error while reading the file. (Does the file exist?)" << std::endl;
}
shaderCode = const_cast<const GLchar*>(shaderString.c_str());
shaderCodeLength = shaderString.length();
}
GLint Shader::Compile(GLenum type) {
//Compilates the shader and returns GL_TRUE if succesful or GL_FALSE otherwise.
GLint status;
shaderId = glCreateShader(type);
glShaderSource(shaderId, 1, &shaderCode, &shaderCodeLength);
glCompileShader(shaderId);
glGetShaderiv(shaderId, GL_COMPILE_STATUS, &status);
glGetShaderInfoLog(shaderId, 512, NULL, log);
return status;
}
shader.vert
#version 130
in vec2 position;
in vec3 color;
in vec2 texcoord;
out vec3 Color;
out vec2 Texcoord;
uniform mat4 trans;
void main() {
Color = color;
Texcoord = texcoord;
gl_Position = trans * vec4(position, 0.0, 1.0);
}
shader.frag
#version 130
in vec3 Color;
in vec2 Texcoord;
out vec4 outColor;
uniform sampler2D texKitten;
uniform sampler2D texPuppy;
void main() {
outColor = mix(texture(texKitten, Texcoord), texture(texPuppy, Texcoord), 0.5);
};
Many people said that the problem was that the shader source ends up a little bit like this: #version 130in vec3 Color;... (but when I print it on the main code, it prints just like it is on the file) and many people are saying that this is the correct way of loading shaders from a file. So, what's wrong in doing this way? Is there a better way?
shaderCode becomes invalid as soon as the constructor returns because you're saving the result of shaderString.c_str().
Using it is undefined.
Use a std::string member and only do the conversion when you compile the shader, or use dynamic allocation.
The first option is better.
I'm having some issues with compiling my vertex shaders under OpenGL. I have a pretty standard vertex shader:
#version 330
layout(location=0) in vec4 in_Position;
layout(location=1) in vec4 in_Color;
out vec4 ex_Color;
void main(void)
{
gl_Position = in_Position;
ex_Color = in_Color;
}
and my shader loading function looks like:
string temp = LoadFile(vShaderPath);
const char* vShaderString = temp.c_str();
const char* vShaderPathC = vShaderPath.c_str();
fprintf(stderr, "File: %s \nContents: %s\n", vShaderPathC, vShaderString);
temp = LoadFile(fShaderPath);
const char* fShaderString = temp.c_str();
vShaderHandle = glCreateShader(GL_VERTEX_SHADER);
fShaderHandle = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(vShaderHandle, 1, &vShaderString, NULL);
glShaderSource(fShaderHandle, 1, &fShaderString, NULL);
GLint compiled;
glCompileShader(vShaderHandle);
glCompileShader(fShaderHandle);
glGetShaderiv(vShaderHandle, GL_COMPILE_STATUS, &compiled);
if(compiled == false)
{
fprintf(stderr, "ERROR: Vertex shader not compiled properly.\n");
GLint blen = 0;
GLsizei slen = 0;
glGetShaderiv(vShaderHandle, GL_INFO_LOG_LENGTH , &blen);
if (blen > 1)
{
GLchar* compiler_log = new GLchar[blen];
glGetInfoLogARB(vShaderHandle, blen, &slen, compiler_log);
fprintf(stderr, "compiler log:\n %s", compiler_log);
delete [] compiler_log;
}
}
but when I run my program, I get an output of:
INFO: OpenGL Version: 3.3.0 NVIDIA 310.19
File: vShader.v.glsl
Contents: #version 330
layout(location=0) in vec4 in_Position;
layout(location=1) in vec4 in_Color;
void main(void)
{
gl_Position = in_Position;
ex_Color = in_Color;
}
ERROR: Vertex shader not compiled properly.
compiler log:
(0) : error C0000: syntax error, unexpected $end at token "<EOF>"
Loadfile is defined:
string ShaderEffect::LoadFile(string path)
{
ifstream in(path.c_str(), ios::in);
if(in.is_open())
{
string contents;
in.seekg(0, ios::end);
contents.resize(in.tellg());
in.seekg(0, ios::beg);
in.read(&contents[0], contents.size());
in.close();
return contents;
}
else
throw "Problem reading file!";
}
I know the glsl code itself is not the problem because I have hard coded the string in:
const char * testvShader = {
"#version 330\n"\
"layout(location=0) in vec4 in_Position;\n"\
"layout(location=1) in vec4 in_Color;\n"\
"out vec4 ex_Color;"
"void main()\n"\
"{\n"\
" gl_Position = in_Position;\n"\
" ex_Color = in_Color;\n"\
"}"};
and when I switch &vShaderString to &testvShader the program runs fine. But I don't see how the loading can be the problem, because the fragment shader loads the same way and compiles and runs perfectly fine, and I print the file to the console before compiling it and it looks fine. I'm at my wits end, I can't figure out what the issue is.
P.S. I'm running on Fedora, if that matters.
You call c_str() on temp and store the result in a variable, and then you modify temp and use the saved result of c_str(). This doesn't work because the result of c_str() becomes invalid if you modify the string.
This compiler error is usually caused by trailing garbage. The most simple remedy is telling OpenGL which length the source string has exactly.
Replace your glShaderSource calls with
const char* vShaderString = temp.c_str();
int vertex_shader_string_length = temp.length();
/* ... */
const char* fShaderString = temp.c_str();
int fragment_shader_string_length = temp.length();
glShaderSource(vShaderHandle, 1, &vShaderString, &vertex_shader_string_length);
glShaderSource(fShaderHandle, 1, &fShaderString, &fragment_shader_string_length);
I have two simple shaders (vertex and fragment)
Vertex Shader
#version 330 core
//=============================================
//---[Structs/Vertex Data]---------------------
//=============================================
layout(location = 0) in vec3 vertexPosition_modelspace;
//=============================================
//---[Variables]-------------------------------
//=============================================
uniform mat4 projection;
uniform mat4 view;
//=============================================
//---[Vertex Shader]---------------------------
//=============================================
void main()
{
gl_Position = projection * view * vertexPosition_modelspace;
}
Fragment Shader
#version 330 core
//=============================================
//---[Output Struct]---------------------------
//=============================================
out vec3 colour;
//=============================================
//---[Fragment Shader]-------------------------
//=============================================
void main()
{
colour = vec3(1, 0, 0);
}
I am trying to compile them and I get the following error
error C0000: syntax error, unexpected $undefined, expecting "::" at token "undefined"
I believe this may have something to do with the way I am reading the files. Here is an example for the vertex shader file
std::string VertexShaderCode = FindFileOrThrow(vertex_file_path);
std::ifstream vShaderFile(VertexShaderCode.c_str());
std::stringstream vShaderData;
vShaderData << vShaderFile.rdbuf();
vShaderFile.close();
The file is then compiled using
char const *VertexSourcePointer = VertexShaderCode.c_str();
glShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL);
glCompileShader(VertexShaderID);
How can I efficiently read in the files and avoid these errors?
EDIT:
Correct compile:
const std::string &shaderText = vShaderData.str();
GLint textLength = (GLint)shaderText.size();
const GLchar *pText = static_cast<const GLchar *>(shaderText.c_str());
glShaderSource(VertexShaderID, 1, &pText, &textLength);
glCompileShader(VertexShaderID);
VertexShaderCode is your filename, not the shader string. That's still in vShaderData. You need to copy the string out of vShaderData, and feed it into glShaderSource.