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); }