andersch.dev

<2024-07-03 Wed>

OpenGL Shading Language (GLSL)

GLSL is the programming language used to write shaders in OpenGL.

Column major order matrices

GLSL uses column major order matrices. The mapping from a subscript operator to an element in the matrix can be illustrated like this:

[0][0]  [1][0]  [2][0]  [3][0]
[0][1]  [1][1]  [2][1]  [3][1]
[0][2]  [1][2]  [2][2]  [3][2]
[0][3]  [1][3]  [2][3]  [3][3]

Preprocessor

The GLSL compilation pipeline includes preprocessing step that uses a C-like preprocessor. However, following features are missing:

  • No file inclusion using #include "file.glsl"
  • No variadic macros
  • No stringification using #
  • No sizeof() operator

Predefined macros may also differ:

  • __FILE__: source string number being processed (not a filename like in C)
  • __LINE__: line number
  • __VERSION__: GLSL version being compiled (glsl version 3.30 gives 330)
  • GL_: Reserved prefix. Defining them results in compile-time error.

Pragmas and other directives:

  • #pragma optimize(off/on): enable optimization (default: on)
  • #pragma debug(off/on): enable debug info for debuggers (default: off)
  • #extension extension_name : behavior: e.g. extension_name : require

See the spec for more info.

Using the C Preprocessor for GLSL

Features:

  • Does not rely on any additional preprocessing step
  • Option of having shader code in a separate file or defined inline
  • Preserves (most) syntax highlighting in both cases
  • Portable across both C and C++
  • Shader code embedded in your program as a C string (no file I/O necessary)
  • Share #define's between C and GLSL code
  • Centralize common helper functions between shaders (e.g. in common.glsl)
  • Centralize common struct definitions between GLSL and C (e.g. in common.h)
  • Conditional compilation (e.g. for having fragment & vertex shader in one file)

Downsides:

  • Newlines are lost, so debug info containing line numbers can be imprecise
  • The glsl files become more noisy
  • Admittedly ugly use of #include in the C code
SHADER_VERSION_STRING
//#include "common.h"    // shared code between C and GLSL
//#include "common.glsl" // shared code between shaders
S(
//\n#version 430 core\n // also works, but we cannot include .glsl files then...
int main() {
    int a,b;

#if (SHADER_DEFINES & SHADER_MVP)
    gl_Position = mvp * apos;
#else
    gl_Position = apos;
#endif

#if (SHADER_DEFINES & SHADER_LIGHT)
    light = 1;
#endif
}
)
#undef SHADER_DEFINES
#include <stdio.h>

/* shader features (can't be an enum) */
#define    SHADER_NONE  (     0)
#define    SHADER_MVP   (1 << 0)
#define    SHADER_LIGHT (1 << 1)

#define SHADER_VERSION_STRING "#version 430 core\n"
#define _STRINGIFY(...) #__VA_ARGS__
#define S(...) _STRINGIFY(__VA_ARGS__)

#define SHADER_DEFINES (SHADER_MVP)
const char* shader1_src =
                          SHADER_VERSION_STRING
                          //#include "common.h"    // shared code between C and GLSL
                          //#include "common.glsl" // shared code between shaders
                          S(
                          //\n#version 430 core\n // also works, but we cannot include .glsl files then...
                          int main() {
                              int a,b;

                          #if (SHADER_DEFINES & SHADER_MVP)
                              gl_Position = mvp * apos;
                          #else
                              gl_Position = apos;
                          #endif

                          #if (SHADER_DEFINES & SHADER_LIGHT)
                              light = 1;
                          #endif
                          }
                          )
                          #undef SHADER_DEFINES //#include "shader.glsl"
                       ;

#define SHADER_DEFINES (SHADER_LIGHT)
const char* shader2_src =
                          SHADER_VERSION_STRING
                          //#include "common.h"    // shared code between C and GLSL
                          //#include "common.glsl" // shared code between shaders
                          S(
                          //\n#version 430 core\n // also works, but we cannot include .glsl files then...
                          int main() {
                              int a,b;

                          #if (SHADER_DEFINES & SHADER_MVP)
                              gl_Position = mvp * apos;
                          #else
                              gl_Position = apos;
                          #endif

                          #if (SHADER_DEFINES & SHADER_LIGHT)
                              light = 1;
                          #endif
                          }
                          )
                          #undef SHADER_DEFINES //#include "shader.glsl"
                       ;

int main () {
    printf("%s\n", shader1_src);
    printf("%s\n", shader2_src);
}