One annoyance that I have with GLSL shaders is that the vertex and fragment shader files must be placed into the directory with the binaries (or some other path accessible by the program). There are alternatives that involve using a resource manager but for smaller programs this seems like overkill.

One solution is to make a string out of the shader and include it with the code. The major problem with this is that editing the shader becomes a bit more tedious. One solution which I will discuss here involves converting the shader at compile time into a header and including it into the binary/library. I am using CMake for my build system which makes it trivial to do this.

References

GLSL Shader to Header

Cmake Embed Resources

Using these two posts you should be able to figure everything out but I wanted to describe in detail the full process.

Cmake

Cmake takes care of the heavy lifting and determines whether not not the header file needs to be generated. In my build system I have the shaders stored in a “resource” folder in my ${CMAKE_SOURCE_DIR} and the header files will be generated in the ${CMAKE_BINARY_DIR}.

embedfile.cpp

This awesome little piece of code was written by the user ScottManDeath from this post. I made a small modification and got rid of the debug wait for user input and made the char array a const.

The program takes three arguments:

embedfile [NAME OF VARIALBE] [INPUT FILE] [OUTPUT FILE]
#include <cstdio>
#include <cstdlib>
#include <string>
#include <cstdarg>

using namespace std;

FILE* fp_in = 0;
FILE* fp_out = 0;

string in_file = "";
string out_file = "";
string array_name = "";

void out( const char* format, ...) {
   char printBuffer[1024];
   va_list list;
   va_start(list, format);
   vsprintf(printBuffer, format, list);
   va_end(list);
#ifndef NDEBUG
   printf(printBuffer);
#endif
   fprintf(fp_out, printBuffer);
}
void write_file_header() {
   out("// header file generated by txt2h, 2003-2006 by ScottManDeath\n");
   out("#ifndef  TXT_HEADER_%s\n", array_name.c_str());
   out("#define  TXT_HEADER_%s\n", array_name.c_str());
}
void write_file_footer() {
   out("#endif  // #ifdef TXT_HEADER_%s\n", array_name.c_str());
}
void make_c_string(
      string & in) {
   string out;
   for (size_t i = 0; i < in.size(); ++i) {
      char c = in[i];
      if ('"' == c)
         out += "\\\"";
      else if ('\\' == c)
         out += "\\\\";
      else
         out += c;
   }
   in = out;
}
void write_array_header() {
   out("const char %s [] =\n", array_name.c_str());
}
void write_array_footer() {
   out(";\n");
}
void write_line(const string & line) {
   out("\"%s\\n\"\n", line.c_str());
}
int main(int argc, char** args) {
   if (argc != 4) {
      printf("syntax error, usage :  txt2h array_name infile outfile");
      exit(0xff);
   }
   array_name = args[1];
   in_file = args[2];
   out_file = args[3];

   if (fp_in = fopen(in_file.c_str(), "rt")) {
      if (fp_out = fopen(out_file.c_str(), "wt")) {
         write_file_header();
         out("\n\n");
         write_array_header();
         char buff[1024];
         while (fgets(buff, sizeof(buff), fp_in)) {
            string s(buff);
            s = s.substr(0, s.find('\n'));
            make_c_string(s);
            write_line(s);
         }
         write_array_footer();
         out("\n\n");
         write_file_footer();
         fclose(fp_out);
      } else {
         printf("error opening %s\n", out_file.c_str());
         exit(0xff);
      }
      fclose(fp_in);
   } else {
      printf("error opening %s\n", in_file.c_str());
      exit(0xff);
   }
}

CmakeLists.txt

Here is the cmake code that uses embedfile.cpp to generate an executable

add_executable(embedfile embedfile.cpp)

This code uses the executable to compile the shader.vert file into vertex_shader.h where the code is stored in a string called vertex_shader .

add_custom_command(
  OUTPUT  ${CMAKE_BINARY_DIR}/vertex_shader.h
  COMMAND embedfile vertex_shader ${CMAKE_SOURCE_DIR}/resources/shader.vert ${CMAKE_BINARY_DIR}/vertex_shader.h
  DEPENDS ${CMAKE_SOURCE_DIR}/resources/shader.vert
  WORKING_DIRECTORY ${CMAKE_BINARY_DIR})

Add the vertex_shader.h to your list of sources/headers, that way Cmake knows to execute the custom command before trying to compile the code.

The same can be done for the fragment shader. Then simply include the generated headers into your source code and treat the variable like a const char *.

Notes:

Make sure that the shader files are using unix line endings otherwise the embedfile code will not work. Also you will need to add ${CMAKE_BINARY_DIR} to your INCLUDE_DIRECTORIES so that it finds the generated include files.

Example Input/Output:

Input:

#version 330

in vec4 position;
out vec2 texCoords;

void main(void) {
    gl_Position = vec4(position.xy, 0, 1);
    texCoords = position.zw;
}

Output:

// header file generated by txt2h, 2003-2006 by ScottManDeath
#ifndef  TXT_HEADER_text_vert
#define  TXT_HEADER_text_vert


const char text_vert [] =
"#version 330\n"
"\n"
"in vec4 position;\n"
"out vec2 texCoords;\n"
"\n"
"void main(void) {\n"
"    gl_Position = vec4(position.xy, 0, 1);\n"
"    texCoords = position.zw;\n"
"}\n"
;


#endif  // #ifdef TXT_HEADER_text_vert