GLSL Shaders and CMake

Hello World,

I’m currently trying to get OpenGL to work using c++, CMake and QT-creator, and I am now trying to create a shader program using external vertex and fragment shaders saved seperately from the main script. I have already managed to create a simple 2d triangle using glBegin() and glEnd(), but I didn’t use any custom shader program. I have configured to add the shaders to the executable in the following line:

add_executable(4d main.cpp Vertexshader.vs Fragmentshader.fs)

Where Vertexshader.vs and Fragmentshader.fs have been saved in the source directory. I am puzzeled how I should now acces the information I stored in these files to compile them in runtime. I have found several scripts that somehow use CMake and GLSL shaders, but they are either a massive avelanche of code, or they don’t seem to work in the first place. What I am looking for is the most simple piece of code implementing both CMake and GLSL shaders possible and/or available. Is there any basic tutorial/source code material available?

Just for clarity, here is the entire CMakeLists.txt code:

cmake_minimum_required(VERSION 2.8)
PROJECT(4D)

find_package(GLUT REQUIRED)
include_directories(${GLUT_INCLUDE_DIRS})
link_directories(${GLUT_LIBRARY_DIRS})
add_definitions(${GLUT_DEFINITIONS})

find_package(OpenGL REQUIRED)
include_directories(${OpenGL_INCLUDE_DIRS})
link_directories(${OpenGL_LIBRARY_DIRS})
add_definitions(${OpenGL_DEFINITIONS})

add_executable(4d main.cpp Vertexshader.vs Fragmentshader.fs)

target_link_libraries(4d ${OPENGL_LIBRARIES} ${GLUT_LIBRARY} )

And the main.cpp code:

#include <iostream>

// ensure apple compatibility
#ifdef __apple__
#   include <GLUT/glut.h>
#   include <OpenGL/OpenGL.h>
#else
#   include <GL/glew.h>
#   include <GL/freeglut.h>
#endif
#

using namespace std;

void render(void) {
    glBegin(GL_TRIANGLES);
    glVertex2f(-0.5, -0.5);
    glVertex2f(0.5, -0.5);
    glVertex2f(0.0, 0.5);
    glEnd();
    glutSwapBuffers();
}

int main (int argc, char* argv[]) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE|GLUT_DEPTH);
    glutInitWindowSize(1280, 720);
    glutCreateWindow("Test");

    glutDisplayFunc(render);

    glutMainLoop();
}

Best Regards,
Jan Heemstra

Ps: English is not my native language. I apologise for any language errors.

You can simply include in the source files where you need the GLSL source strings like this (assuming shader sources are in the same directory as the C++ source files that wants to access them):

#include "Vertexshader.vs"
#include "Fragmentshader.fs"

Also, what you with the add_executable function is add source files to the list compiled by g++. However, you’ll see no effect with file endings like *.vs or
*.fs because CMake simply ignores them. CMake will only add files with specific endings, like *.cpp or *.c

[QUOTE=thokra;1254519]You can simply include in the source files where you need the GLSL source strings like this (assuming shader sources are in the same directory as the C++ source files that wants to access them):

#include "Vertexshader.vs"
#include "Fragmentshader.fs"

Also, what you with the add_executable function is add source files to the list compiled by g++. However, you’ll see no effect with file endings like *.vs or
*.fs because CMake simply ignores them. CMake will only add files with specific endings, like *.cpp or *.c[/QUOTE]

If I understand what’s happening correctly, c++ now treats the Vertexshader.vs as a DLL, which means I still have to write the shader code inside a c++ string. This is also not going to work (at least not efficiently) when I’m trying to acces other non-script data structures, like textures. Is there any way I can tell CMake to include the shaders into the executable without having the compiler trying to compile it? And, is there any way to access it on run-time, and to convert it into a string?

The compiler will, as far as I know, “copy-and-paste” the code from the Shaderfiles into that specific file you used the include statement at that exaxt location. Then it will compile the whole File.
Using a shader in opengl requires to load the shader source code at runtime, pass it to opengl and compile it at runtime.
So you need the shader sourcecode as a string (a char array to be more precise) and pass it via glShaderSource to opengl.
Then you need to compile the code using glCompileShader. To use the vertex and fragment shader you’ll need to link them into a shader program using glAttachShader and glLinkProgram. Last but not least to make that program active you will need to use glUseProgram.

I Hope that helped a little

Best regards
Lighttec

EDIT: To access the information in the external files, you’ll need to write a simple ascii/utf-8 reader.

add_executable(4d main.cpp Vertexshader.vs Fragmentshader.fs)

You shouldn’t do this. GLSL is not something that is (generally speaking) compiled by an off-line compiler.

The way GLSL works is that your C++ code, at runtime, will load those files and compile them into programs. This is not a process that CMake will be useful at handling. Think of shader loading the way you would texture loading or handling of any other file-based asset.

CMake might be of use to you by generating C++ files that contain these shader files. But that would require some kind of build script to handle this. And that would mean a lot of CMake configuration stuff.

The compiler will, as far as I know, “copy-and-paste” the code from the Shaderfiles into that specific file you used the include statement at that exaxt location. Then it will compile the whole File.

Yes, that’s what happens. I just don’t see how that helps.

GLSL is not C++. GLSL is not something that a C++ compiler can process. Now, a string literal that just so happens to contain GLSL is something that a C++ compiler can process. But that’s just because it’s a string literal.

#including a text file does not turn it into a string literal. If you #include a text file, the compiler will do exactly what you said: copy the data from the shader into your C++ source code.

So unless your GLSL shader files are already written C++ string literals (and if they are, you’re probably the only person who does that), #including them is not going to work.

GUI toolkits often have “resource compilers” that allow embedding of various data (often icons or string tables for localization) into executables. It may be possible to (ab-)use on of those to get the shader source into string literals.

On Windows, you can store strings as resources in the executable then access them with LoadString().

On Unix, you can use “ld -b binary …” to convert an arbitrary file to an object file containing the data as an array.

Or you can look for the “bin2c” utility which will convert arbitrary files to C source code containing the data as an array initialisation.

Another way in C++11 is to wrap the shader code as raw string literal. As Alfonse already mentioned, you can have CMake do that for you.

Just for clarity, the end result I’d like to have is preferably to have the GLSL code somehow embedded into the executable to make for an executable that is not dependant on external recourses. Also, I’d like to be able to write the code into the text file without having to worry about the character array declaration (thus, directly writing in GLSL and afterwards having the c++ code convert it into a const char on runtime). Then my worries are on how to acces the code on running the program.

[QUOTE=GClements;1254531]On Windows, you can store strings as resources in the executable then access them with LoadString().

On Unix, you can use “ld -b binary …” to convert an arbitrary file to an object file containing the data as an array.

Or you can look for the “bin2c” utility which will convert arbitrary files to C source code containing the data as an array initialisation.[/QUOTE]
Is “ld -b binary …” supposed to be make code? Or should this be passed onto the compiler? Where should it be written? I’m really confused.

Does that mean the text file is already converted into a string when the build environment is made? Because I really want to do that upon running the executable.

Is “ld -b binary …” supposed to be make code?

ld is a program. The above is a command line. Where you put it is up to you. It could go into a makefile, or it could go elsewhere.

Does that mean the text file is already converted into a string when the build environment is made? Because I really want to do that upon running the executable.

Upon running what executable? The one you’re trying to build?

CMake can be used to generate source code at one of two times. It could do it when CMake builds the build system. Or it could be done as part of the build system; that is, when you’re building the executable. It can’t be done after the executable you’re trying to compile has been compiled.

Does that mean the text file is already converted into a string when the build environment is made? Because I really want to do that upon running the executable.

Raw string literals are C++ constructs - they’re a language feature. The allow you to write text continuous text without special interpretation of special characters which don’t have the same effect for “normal” string literals. CMake’s job would only be, at config time, to generate valid C++ code, i.e. take some file skeleton, replace some replaceable placeholder with your shader code and spit out a header and possibly a source file containing your raw string literals. Then you can compile your source strings directly into your executable.

If by “upon running the executable” you mean you simply want to load text files containing GLSL source code at runtime, why the hell didn’t you just say so? This is standard procedure if you’re neither loading code compiled into your exe, nor loading shader binaries generated beforehand.

“ld” is the linker. It’s used to make object files, shared libraries and executables.

The command:


ld -b binary -r -o vshader.o vshader.vs

will generate an object file, vshader.o, from the text file vshader.vs. The object file will be similar to the result of converting the vshader.vs file to C source code using bin2c then compiling it.