Basic

  • There are a lot of texts here that need to be revised and properly rewritten.

  • GLSL  (OpenGL Shader Language).

  • Open standard (Khronos Group).

  • Works with :

    • OpenGL.

    • Vulkan.

    • WebGL.

  • How Vulkan uses it :

    • It's the most common shader language used for Vulkan.

    • It's human-readable, widely supported, and tools like glslangValidator  convert it to SPIR-V easily.

    • Compiling GLSL to SPIR-V:

      • The output is a binary SPIR-V files ( vert.spv , frag.spv ) fed to Vulkan at runtime.

      glslangValidator -V shader.vert -o vert.spv
      glslangValidator -V shader.frag -o frag.spv
      

Where I Stopped

Cheat Sheet

  • gl_Position

    • Is always in clip space  (homogeneous coordinates before perspective divide).

    • After the GPU does the perspective divide ( gl_Position.xyz / gl_Position.w ), you get Normalized Device Coordinates (NDC)  in the range [-1, 1] .

    • The viewport transform then maps NDC into window coordinates  (pixels on screen).

  • Model Space -> World Space -> View Space :

vec4 vertex_pos_world_space = model.model_matrix * vec4(vertex_pos, 1.0);
vec4 vertex_pos_view_space  = globals.view * vertex_pos_world_space;
gl_Position  = globals.proj * vertex_pos_view_space;
  • SCREEN UV :

vec2 screen_uv = gl_FragCoord.xy / vec2(viewport_size);

// Godot
vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size;
vec2 viewport_resolution = vec2(1920, 1080);
vec2 screen_space_uv = gl_FragCoord.xy / viewport_resolution.xy;
  • <excalidraw_not_loaded> .

  • Vertex Light :

#ifdef LIGHT_VERTEX_USED
vec3 light_vertex = vertex;
Enums
#define GLOW_MODE_ADD 0
#define GLOW_MODE_SCREEN 1
#define GLOW_MODE_SOFTLIGHT 2
#define GLOW_MODE_REPLACE 3
#define GLOW_MODE_MIX 4

if (params.glow_mode == GLOW_MODE_ADD) {
Bitmasks
#define FLAG_USE_BCS (1 << 0)
#define FLAG_USE_GLOW (1 << 1)
#define FLAG_USE_AUTO_EXPOSURE (1 << 2)
#define FLAG_USE_COLOR_CORRECTION (1 << 3)
#define FLAG_USE_FXAA (1 << 4)
#define FLAG_USE_DEBANDING (1 << 5)
#define FLAG_CONVERT_TO_SRGB (1 << 6)

if (bool(params.flags & FLAG_USE_BCS)) {
    color.rgb = apply_bcs(color.rgb, params.bcs);
}

Formats

  • every format that does not end in _u/s norm , _u/s scaled , or _*float/int  are storing non-linear colors

  • reads and writes through the texture interface will  have conversion to and from linear done for you

  • A raw memory access, e.g. through a buffer or a copy, does not.

  • So, if the uniform sampler2D  is SRGB, I will read it as linear inside the shader? Also, if the color attachment is SRGB, the output will be converted from linear to srgb?

    • only if you are accessing it through the texture/image interface, yes.

Constant Data

  • This data can be anything we want it to be, for instance it can be used for things such as calculating where an object should be placed inside our world, or computing the overall brightness of an object based on the lights in the scene.

  • The data remains constant  across every shader invocation of a draw call.

    • It differs from other data (e.g. input vertex data) in this sense.

  • The data can be shared  between shader stages, as we know it isn’t going to be changed throughout the runtime of a single draw call in a render pipeline.

  • Constant data is implemented in shader code by using global variables.

  • Global variables  have the following format: <layout> <storage> <type> <variable_name> .

layout(location = 0) in vec4 position;

layout(set = 0, binding = 0) uniform Data
{
    mat4 model;
} data;

layout(location = 0) out vec4 o_pos;

Varying Types

  • The global variables that use inputs ( in ) and outputs ( out ) are values that may vary  from one shader invocation to the next, therefore they shouldn’t  be used for constant data.

  • They require a layout location  which is used to identify a particular input/output.

  • They have slightly different rules for what they do depending on the shader stage, and have slightly different restrictions on the types of data it can represent. However, generally their use is to feed values from one stage to the next (e.g. from vertex shader to fragment shader).

Uniform Types

  • Uniform types are global variables that have either the uniform  or buffer  storage type, these are uniform buffer objects  and shader storage buffer objects  respectively. They describe data which remains constant across an entire draw call, meaning that the values stay the same across the different shader stages and shader invocations.

  • These values use a layout binding  and, when working with multiple VkDescriptorSet s, we will also give it a layout set

  • Uniform buffer objects (UBOs)

    • Are the more commonly used of the two. They are read-only  buffers, so trying to edit them in shader code will result in a compile-time error.

  • Shader storage buffer objects (SSBOs)

    • Are like special types of uniform buffer objects, denoted by the storage type buffer . Unlike UBOs they can be written to, meaning the values can  be changed in the shaders so therefore they don’t always represent data that is constant.

    • Having said this, depending on the implementation, they generally can hold a lot more data as opposed to UBOs.

      • To check how much data we can store in uniform buffers and storage buffers, you can query the physical device for its VkPhysicalDeviceLimits  and check the values maxUniformBufferRange  and maxStorageBufferRange  respectively.

Basic Syntax

Comments
//
/*
*/
Source Strings
float f\
oo;
// forms a single line equivalent to "float foo;"
// (assuming '\' is the last character before the new-line and "oo" are
// the first two characters of the next line)

Preprocessors

#  
#define  
#undef  

#if  
#ifdef  
#ifndef  
#else  
#elif  
#endif  

#error  
#pragma  

#extension  
#version  

#line

defined
##
  • #if , ** #ifdef,** #ifndef , #else , #elif , and #endif`.

    • Operate as is standard for C++ preprocessors, except for the following:

      • Expressions following #if  and #elif  are further restricted to expressions operating on literal integer constants, plus identifiers consumed by the defined  operator.

      • Character constants are not supported.

  • #pragma

    • Allows implementation-dependent compiler control. Tokens following #pragma  are not subject to preprocessor macro expansion. If an implementation does not recognize the tokens following #pragma , then it will ignore that pragma.

    • The following pragmas are defined as part of the language.

    • The STDGL  pragma is used to reserve pragmas for use by future revisions of this language. No implementation may use a pragma whose first token is STDGL .

      #pragma STDGL
      
    • Can be used to turn off optimizations as an aid in developing and debugging shaders. It can only be used outside function definitions. By default, optimization is turned on for all shaders.

      #pragma optimize(on)
      #pragma optimize(off)
      
    • Can be used to enable compiling and annotating a shader with debug information, so that it can be used with a debugger. It can only be used outside function definitions. By default, debug is turned off.

    #pragma debug(on)
    #pragma debug(off)
    
  • #version

    • Shaders should declare the version of the language they are written to.

    #version number profile_opt
    
    • Profile arguments:

      core
      compatibility
      es
      
      • The default is core , if none is provided.

  • #error

    • will cause the implementation to put a compile-time diagnostic message into the shader object’s information log (see section 7.12 “Shader, Program and Program Pipeline Queries” of the OpenGL Specification  for how to access a shader object’s information log). The message will be the tokens following the #error  directive, up to the first new-line. The implementation must treat the presence of a #error  directive as a compile-time error.

  • defined

    defined identifier
    defined ( identifier )
    
    • Two tokens in a macro can be concatenated into one token using the token pasting ( ## ) operator, as is standard for C++ preprocessors.

  • #extension

    • Directives to control the behavior of the compiler with respect to extensions are declared with:

    #extension extension_name : behavior
    #extension all : behavior
    
    • where extension_name  is the name of an extension. Extension names are not documented in this specification. The token all  means the behavior applies to all extensions supported by the compiler.

Predefined macros
  • All macro names containing two consecutive underscores ( __ ) are reserved for use by underlying software layers. Defining or undefining such a name in a shader does not itself result in an error, but may result in unintended behaviors that stem from having multiple definitions of the same name.

    • __LINE__

      • Will substitute a decimal integer constant that is one more than the number of preceding new-lines in the current source string.

    • __FILE__

      • Will substitute a decimal integer constant that says which source string number is currently being processed.

    • __VERSION__

      • Will substitute a decimal integer reflecting the version number of the OpenGL Shading Language. The version of the shading language described in this document will have __VERSION__  substitute the decimal integer 460.

  • All macro names prefixed with GL_  are also reserved, and defining or undefining such a name results in a compile-time error.

Basic Types

Transparent Types

| Type        | Meaning                                                            |
| ----------- | ------------------------------------------------------------------ |
| void     | for functions that do not return a value                           |
| bool     | a conditional type, taking on values of true or false              |
| int      | a signed integer                                                   |
| uint     | an unsigned integer                                                |
| float    | a single-precision floating-point scalar                           |
| double   | a double-precision floating-point scalar                           |
| vec2     | a two-component single-precision floating-point vector             |
| vec3     | a three-component single-precision floating-point vector           |
| vec4     | a four-component single-precision floating-point vector            |
| dvec2    | a two-component double-precision floating-point vector             |
| dvec3    | a three-component double-precision floating-point vector           |
| dvec4    | a four-component double-precision floating-point vector            |
| bvec2    | a two-component Boolean vector                                     |
| bvec3    | a three-component Boolean vector                                   |
| bvec4    | a four-component Boolean vector                                    |
| ivec2    | a two-component signed integer vector                              |
| ivec3    | a three-component signed integer vector                            |
| ivec4    | a four-component signed integer vector                             |
| uvec2    | a two-component unsigned integer vector                            |
| uvec3    | a three-component unsigned integer vector                          |
| uvec4    | a four-component unsigned integer vector                           |
| mat2     | a 2 Ă— 2 single-precision floating-point matrix                     |
| mat3     | a 3 Ă— 3 single-precision floating-point matrix                     |
| mat4     | a 4 Ă— 4 single-precision floating-point matrix                     |
| mat2x2   | same as a mat2                                                  |
| mat2x3   | a single-precision floating-point matrix with 2 columns and 3 rows |
| mat2x4   | a single-precision floating-point matrix with 2 columns and 4 rows |
| mat3x2   | a single-precision floating-point matrix with 3 columns and 2 rows |
| mat3x3   | same as a mat3                                                  |
| mat3x4   | a single-precision floating-point matrix with 3 columns and 4 rows |
| mat4x2   | a single-precision floating-point matrix with 4 columns and 2 rows |
| mat4x3   | a single-precision floating-point matrix with 4 columns and 3 rows |
| mat4x4   | same as a mat4                                                  |
| dmat2    | a 2 Ă— 2 double-precision floating-point matrix                     |
| dmat3    | a 3 Ă— 3 double-precision floating-point matrix                     |
| dmat4    | a 4 Ă— 4 double-precision floating-point matrix                     |
| dmat2x2  | same as a dmat2                                                 |
| dmat2x3  | a double-precision floating-point matrix with 2 columns and 3 rows |
| dmat2x4  | a double-precision floating-point matrix with 2 columns and 4 rows |
| dmat3x2  | a double-precision floating-point matrix with 3 columns and 2 rows |
| dmat3x3  | same as a dmat3                                                 |
| dmat3x4  | a double-precision floating-point matrix with 3 columns and 4 rows |
| dmat4x2  | a double-precision floating-point matrix with 4 columns and 2 rows |
| dmat4x3  | a double-precision floating-point matrix with 4 columns and 3 rows |
| dmat4x4  | same as a dmat4                                                 |

Floating-Point Opaque Types

| Type                                                           | Meaning                                                               |
|----------------------------------------------------------------|-----------------------------------------------------------------------|
| sampler1D   texture1D   image1D                       | a handle for accessing a 1D texture                                   |
| sampler1DShadow                                             | a handle for accessing a 1D depth texture with comparison             |
| sampler1DArray   texture1DArray   image1DArray        | a handle for accessing a 1D array texture                             |
| sampler1DArrayShadow                                        | a handle for accessing a 1D array depth texture with comparison       |
| sampler2D   texture2D   image2D                       | a handle for accessing a 2D texture                                   |
| sampler2DShadow                                             | a handle for accessing a 2D depth texture with comparison             |
| sampler2DArray   texture2DArray   image2DArray        | a handle for accessing a 2D array texture                             |
| sampler2DArrayShadow                                        | a handle for accessing a 2D array depth texture with comparison       |
| sampler2DMS   texture2DMS   image2DMS                 | a handle for accessing a 2D multisample texture                       |
| sampler2DMSArray   texture2DMSArray   image2DMSArray  | a handle for accessing a 2D multisample array texture                 |
| sampler2DRect   texture2DRect   image2DRect           | a handle for accessing a rectangle texture                            |
| sampler2DRectShadow                                         | a handle for accessing a rectangle texture with comparison            |
| sampler3D   texture3D   image3D                       | a handle for accessing a 3D texture                                   |
| samplerCube   textureCube   imageCube                 | a handle for accessing a cube mapped texture                          |
| samplerCubeShadow                                           | a handle for accessing a cube map depth texture with comparison       |
| samplerCubeArray   textureCubeArray   imageCubeArray  | a handle for accessing a cube map array texture                       |
| samplerCubeArrayShadow                                      | a handle for accessing a cube map array depth texture with comparison |
| samplerBuffer   textureBuffer   imageBuffer           | a handle for accessing a buffer texture                               |
| subpassInput                                                | a handle for accessing a floating-point subpass input                 |
| subpassInputMS                                              | a handle for accessing a multi-sampled floating-point subpass input   |

Signed Integer Opaque Types

| Type                                                              | Meaning                                                        |
|-------------------------------------------------------------------|----------------------------------------------------------------|
| isampler1D   itexture1D   iimage1D                       | a handle for accessing an integer 1D texture                   |
| isampler1DArray   itexture1DArray   iimage1DArray        | a handle for accessing an integer 1D array texture             |
| isampler2D   itexture2D   iimage2D                       | a handle for accessing an integer 2D texture                   |
| isampler2DArray   itexture2DArray   iimage2DArray        | a handle for accessing an integer 2D array texture             |
| isampler2DMS   itexture2DMS   iimage2DMS                 | a handle for accessing an integer 2D multisample texture       |
| isampler2DMSArray   itexture2DMSArray   iimage2DMSArray  | a handle for accessing an integer 2D multisample array texture |
| isampler2DRect   itexture2DRect   iimage2DRect           | a handle for accessing an integer 2D rectangle texture         |
| isampler3D   itexture3D   iimage3D                       | a handle for accessing an integer 3D texture                   |
| isamplerCube   itextureCube   iimageCube                 | a handle for accessing an integer cube mapped texture          |
| isamplerCubeArray   itextureCubeArray   iimageCubeArray  | a handle for accessing an integer cube map array texture       |
| isamplerBuffer   itextureBuffer   iimageBuffer           | a handle for accessing an integer buffer texture               |
| isubpassInput                                                  | a handle for accessing an integer subpass input                |
| isubpassInputMS                                                | a handle for accessing a multi-sampled integer subpass input   |

Unsigned Integer Opaque Types

| Type                                                              | Meaning                                                                 |
|-------------------------------------------------------------------|-------------------------------------------------------------------------|
| usampler1D   utexture1D   uimage1D                       | a handle for accessing an unsigned integer 1D texture                   |
| usampler1DArray   utexture1DArray   uimage1DArray        | a handle for accessing an unsigned integer 1D array texture             |
| usampler2D   utexture2D   uimage2D                       | a handle for accessing an unsigned integer 2D texture                   |
| usampler2DArray   utexture2DArray   uimage2DArray        | a handle for accessing an unsigned integer 2D array texture             |
| usampler2DMS   utexture2DMS   uimage2DMS                 | a handle for accessing an unsigned integer 2D multisample texture       |
| usampler2DMSArray   utexture2DMSArray   uimage2DMSArray  | a handle for accessing an unsigned integer 2D multisample array texture |
| usampler2DRect   utexture2DRect   uimage2DRect           | a handle for accessing an unsigned integer rectangle texture            |
| usampler3D   utexture3D   uimage3D                       | a handle for accessing an unsigned integer 3D texture                   |
| usamplerCube   utextureCube   uimageCube                 | a handle for accessing an unsigned integer cube mapped texture          |
| usamplerCubeArray   utextureCubeArray   uimageCubeArray  | a handle for accessing an unsigned integer cube map array texture       |
| usamplerBuffer   utextureBuffer   uimageBuffer           | a handle for accessing an unsigned integer buffer texture               |
| atomic_uint                                                    | a handle for accessing an unsigned integer atomic counter               |
| usubpassInput                                                  | a handle for accessing an unsigned-integer subpass input                |
| usubpassInputMS                                                | a handle for accessing a multi-sampled unsigned-integer subpass input   |

Sampler Opaque Types

| Type              | Meaning                                                                               |
|-------------------|---------------------------------------------------------------------------------------|
| sampler        | a handle for accessing state describing how to sample a texture                       |
| samplerShadow  | a handle for accessing state describing how to sample a depth texture with comparison |

Pointers
  • There are no pointer types.

Integers
  • For OpenGL:

    • unsigned integers have exactly 32 bits of precision.

    • signed integers use 32 bits, including a sign bit, in two’s complement form.

  • For Vulkan:

    • mediump  and lowp  integers are as defined by the SPIR-V RelaxedPrecision  decoration.

    • highp  unsigned integers have exactly 32 bits of precision.

    • highp  signed integers use 32 bits, including a sign bit, in two’s complement form.

Floats
float a, b = 1.5;    // single-precision floating-point
double c, d = 2.0LF; // double-precision floating-point
  • When the suffix lf  or LF  is present, the literal has type double . Otherwise, the literal has type float .

  • A decimal point ( . ) is not needed if the exponent part is present.

Vectors
vec2 texcoord1, texcoord2;
vec3 position;
vec4 myRGBA;
ivec2 textureLookup;
bvec3 less;
Matrices
  • Matrix types beginning with " mat " have single-precision components while matrix types beginning with " dmat " have double-precision components.

  • The first number in the type is the number of columns , the second is the number of rows .

  • If there is only one number, the matrix is square.

mat2 mat2D;
mat3 optMatrix;
mat4 view, projection;
mat4x4 view; // an alternate way of declaring a mat4
mat3x2 m;    // a matrix with 3 columns and 2 rows
dmat4 highPrecisionMVP;
dmat2x4 dm;
Atomic Counters
  • OpenGL:

    • Atomic counter types (e.g. atomic_uint ) are opaque handles to counters, declared and behaving as described above for opaque types. The variables they declare specify which counter to access when using the built-in atomic counter functions as described in “ Atomic Counter Functions ”. They are bound to buffers as described in “ Atomic Counter Layout Qualifiers ”.

    • Members of structures cannot be declared as atomic counter types.

  • Vulkan:

    • Atomic counter types are not available when targeting Vulkan.

Arrays

float frequencies[3];

uniform vec4 lightPosition[4u];

light lights[];            // Unsized. Valid in GLSL, illegal in ESSL.

const int numLights = 2;
light lights[numLights];

vec4 a[3][2];

// a shader storage block, introduced in section 4.3.7 "Buffer Variables"
buffer b {
    float u[]; // an error, unless u gets statically sized by link time
    vec4 v[];  // okay, v will be sized dynamically, if not statically
} name[3];     // when the block is arrayed, all u will be the same size, but not necessarily all v, if sized dynamically
  • Arrays only have a single dimension (a single number within “[ ]”), however, arrays of arrays can be declared.

  • Any type can be formed into an array.

  • All arrays are inherently homogeneous; made of elements all having the same type and size, with one exception. An array of shader storage blocks whose last member is a runtime-sized array allows the individual blocks to have different sizes and hence a different number of elements in the trailing array.

  • Explicitly Sized

    • The number of elements in the array is explicitly given.

    • It is a compile-time error if:

      • an explicitly sized array is indexed with a constant expression greater than or equal to the declared size.

  • Runtime Sized

    • The number of elements is not given and the array is the outermost dimension of the last declared member of a shader storage block (see section “ Interface Blocks ”). The array size is inferred at run-time from the size of the data store backing the shader storage block.

    • It is a compile-time error if:

      • a runtime array is passed as a function argument.

  • Unsized

    • The number of array elements is not given and the array is not runtime sized.

    • Unsized arrays may become explicitly sized following either an explicitly-sized initializer or a redeclaration with an explicit size (Explicitly-sized and runtime-sized arrays may not be redeclared). It is a compile-time error to redeclare an array with a different underlying member type.

    • It is a compile-time error if:

      • an unsized array is indexed with anything other than a constant integral expression.

      • an unsized array is declared as a formal parameter to a function.

      • an unsized array is declared as the return type of a function.

      • an unsized array is passed as a function argument.

      • an unsized array is redeclared with a size less than or equal to any constant index used earlier in the shader to index the array.

  • Undefined behavior results from indexing an array with a non-constant expression that’s greater than or equal to the array’s size or less than 0.

  • It is a compile-time error to assign either to or from a runtime-sized or unsized array (rather than specific elements). Note, this is a rare case that initializers and assignments appear to have different semantics. An initializer for an unsized array is valid and will size the array, but the equivalent assigment is not valid. For example,

float a[5];
float b[];
// An initializer sizes an array ...
float c[] = a;  // c is explicitly size 5
// ... but the equivalent assignment is not valid
float d[];
d = a;          // Error. Assignment to an unsized array
// It is never valid to assign from an unsized array
float e[] = b;  // Error. b is unsized so cannot be assigned
  • Alternatively, the initializer-list syntax can be used to initialize an array of arrays:

vec4 a[3][2] = { vec4[2](vec4(0.0), vec4(1.0)),
                 vec4[2](vec4(0.0), vec4(1.0)),
                 vec4[2](vec4(0.0), vec4(1.0)) };
  • For arrays of arrays, any unsized dimension is explicitly sized by the initializer:

vec4 a[][] = { vec4[2](vec4(0.0), vec4(1.0)), // okay, size to a[3][2]
               vec4[2](vec4(0.0), vec4(1.0)),
               vec4[2](vec4(0.0), vec4(1.0)) };
Array Type
  • An array type can be formed by specifying a non-array type

float[5]    // an array of size [5] of float
float[2][3] // an array of size [2] of array of size [3] of float,
            // not size [3] of float[2]
  • Such an array type can be used anywhere any other type can be used, including as the return value from a function, as a constructor of an array and in declarations.

// As a function return type
float[5] foo() { }
// As an array constructor
float[5](3.4, 4.2, 5.0, 5.2, 1.1)
// In declaring an unnamed parameter
void foo(float[5])
// In normal declarations
float[5] a;

// The following 3 declarations are equivalent:
vec4 a[3][2]; // size-3 array of size-2 array of vec4
vec4[2] a[3];
vec4[3][2] a;
  • If such an array type is unsized and used as a constructor then the size of the array is inferred from the constructor arguments. For example,

float a[5] = float[5](3.4, 4.2, 5.0, 5.2, 1.1);
float a[5] = float[](3.4, 4.2, 5.0, 5.2, 1.1);  // Constructor also of type float[5]
Length Method
  • The return value has type int .

float a[5];
a.length(); // returns 5

vec4 a[3][2];
a.length()    // returns 3
a[x].length() // returns 2
  • It is a compile-time error to use the length()  method on an unsized array. The return value is a constant expression if and only if the array is explicitly-sized.

Structs

struct light {
    float intensity;
    vec3 position;
} lightVar;
  • In this example, light  becomes the name of the new type, and lightVar  becomes a variable of type light .

  • To declare variables of the new type, use its name (without the keyword struct ).

light lightVar2;
  • Comptime errors:

struct S { float f; }; // Allowed: S is defined as a structure.

struct T {
    S;              // Error: anonymous structures disallowed
    struct { ... }; // Error: embedded structures disallowed
    S s;            // Allowed: nested structure with a name.
};

Functions

  • Functions that do not return a value must be declared as void .

    • There is no default function return type.

    • The keyword void  cannot be used in any other declarations (except for empty formal or actual parameter lists), or a compile-time error results.