21

I'd like to declare GLSL shader strings inline using macro stringification:

#define STRINGIFY(A)  #A
const GLchar* vert = STRINGIFY(
#version 120\n
attribute vec2 position;
void main()
{
    gl_Position = vec4( position, 0.0, 1.0 );
}
);

This builds and runs fine using VS2010 but fails to compile on gcc with:

error: invalid preprocessing directive #version

Is there a way to use stringification like this in a portable manner?

I'm trying to avoid per-line quotes:

const GLchar* vert = 
"#version 120\n"
"attribute vec2 position;"
"void main()"
"{"
"    gl_Position = vec4( position, 0.0, 1.0 );"
"}"
;

...and/or line continuation:

const GLchar* vert = "\
#version 120\n                                 \
attribute vec2 position;                       \
void main()                                    \
{                                              \
    gl_Position = vec4( position, 0.0, 1.0 );  \
}                                              \
";
a3f
  • 8,517
  • 1
  • 41
  • 46
genpfault
  • 51,148
  • 11
  • 85
  • 139

5 Answers5

27

Can you use C++11? If so you could use raw string literals:

const GLchar* vert = R"END(
#version 120
attribute vec2 position;
void main()
{
    gl_Position = vec4( position, 0.0, 1.0 );
}
)END";

No need for escapes or explicit newlines. These strings start with an R (or r). You need a delimiter (I chose END) between the quote and the first parenthesis to escape parenthesis which you have in the code snippet.

Tommy
  • 99,986
  • 12
  • 185
  • 204
emsr
  • 15,539
  • 6
  • 49
  • 62
  • This is mostly for streamlining [SSCCE](http://sscce.org/) programs/examples here on SO so in the interests of portability I'd like to avoid C++11. – genpfault Dec 14 '12 at 05:03
  • 8
    Excellent suggestion. GLSL shaders and OpenCL kernels are one of the best use-cases for raw string literals. But note, that the delimiter is not strictly neccessary, it can just be empty, only the `R"()"` is mandatory. – Christian Rau Dec 14 '12 at 08:50
  • @ChristianRau You're right. I only need the END to escape a literal )" not just ). – emsr Dec 14 '12 at 12:21
  • Note, some GLSL compilers hew *very* closely to the spec ([ANGLE](https://github.com/google/angle)'s, for instance) & may reject shaders with an initial newline before the `#version` directive. A quick solution is to add one to the pointer to skip over it: `const char* vert = 1 + R"END(`... – genpfault Feb 18 '19 at 17:10
  • 1
    @genpfault Or just start out with `const GLchar* vert = R"END(#version 120` without the newline. I just wrote my answer that way to make it look nicer. (To me). – emsr Feb 19 '19 at 23:01
  • 1
    @genpfault: Sounds like they're not following the spec closely, as it says: "The #version directive must occur in a shader before anything else, except for comments and white space.", and newlines are whitespace. – Chris Dodd Sep 23 '19 at 17:50
  • @ChrisDodd: Hrm, good point, thanks! I might have to check ANGLE's compilation pipeline & file a bug. – genpfault Sep 24 '19 at 16:16
20

Unfortunately, having preprocessor directives in the argument of a macro is undefined, so you can't do this directly. But as long as none of your shaders need preprocessor directives other than #version, you could do something like:

#define GLSL(version, shader)  "#version " #version "\n" #shader

const GLchar* vert = GLSL(120,
    attribute vec2 position;
    void main()
    {
        gl_Position = vec4( position, 0.0, 1.0 );
    }
);
Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
5

To achieve this purpose I used sed. I have seperate files with GLSL which I edit (with proper syntax highlighting), and in the same time GLSL in inlined in C++. Not very cross platform, but with msys it works under windows.

In C++ code:

const GLchar* vert = 
#include "shader_processed.vert"
;

In Makefile:

shader_processed.vert: shader.vert
    sed -f shader.sed shader.vert > shader_processed.vert

programm: shader_processed.vert main.cpp
    g++ ...

shader.sed

s|\\|\\\\|g
s|"|\\"|g
s|$|\\n"|g
s|^|"|g
Oleg Titov
  • 1,100
  • 1
  • 8
  • 13
5

The problem is due to gcc preprocessing macros meant for GLSL. Using standard stringify and escaping preprocessor directives with new lines in GLSL code worked for me.

#define STRINGIFY(A)  #A

const GLchar* vert = STRINGIFY(

\n#version 120\n
\n#define MY_MACRO 999\n

attribute vec2 position;
void main()
{
    gl_Position = vec4( position, 0.0, 1.0 );
}
);
Alexey Podlasov
  • 993
  • 10
  • 18
0

An alternative approach: include a header file, but with a .glsl extension.

For example, I have a file called bar.h.glsl. It exposes my vertex and fragment shader code as raw literals:

#pragma once

namespace bar {

static const char vertex[] = R"(#version 410 core


// ----- vertex shader start -----
layout( location = 0 ) in vec4 vPosition;

uniform float Time;
uniform float Width;

void main()
{
    float x = vPosition.x;
    float y = vPosition.y * Width;
    float yOffset = mix(sin(Time), sin(Time * 0.75), x * 0.5 + 0.5);
    gl_Position = vec4(x, y + yOffset, vPosition.z, vPosition.w);
}
// ------ vertex shader end ------


)";
static const char fragment[] = R"(#version 410 core


// ----- fragment shader start ----
out vec4 fragColor;

void main()
{
    fragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
// ------ fragment shader end -----


)";

}

Then in my source, I simply include the file:

#include "bar.h.glsl"

and access the glsl strings like so:

bool success = barShader.Compile( bar::vertex, bar::fragment );

This way, although I need to overlook a bit of C code in my glsl files, I get the best of glsl syntax highlighting without having to dynamically load the file.

Mattijs
  • 1,909
  • 3
  • 19
  • 28