Shading language

Introduction

Godot uses a shading language similar to GLSL ES 3.0. Most datatypes and functions are supported, and the few remaining ones will likely be added over time.

If you are already familiar with GLSL, the Godot Shader Migration Guide is a resource that will help you transition from regular GLSL to Godot's shading language.

Data types

Most GLSL ES 3.0 datatypes are supported:

Type

Description

void

Void datatype, useful only for functions that return nothing.

bool

Boolean datatype, can only contain true or false.

bvec2

Two-component vector of booleans.

bvec3

Three-component vector of booleans.

bvec4

Four-component vector of booleans.

int

Signed scalar integer.

ivec2

Two-component vector of signed integers.

ivec3

Three-component vector of signed integers.

ivec4

Four-component vector of signed integers.

uint

Unsigned scalar integer; can't contain negative numbers.

uvec2

Two-component vector of unsigned integers.

uvec3

Three-component vector of unsigned integers.

uvec4

Four-component vector of unsigned integers.

float

Floating-point scalar.

vec2

Two-component vector of floating-point values.

vec3

Three-component vector of floating-point values.

vec4

Four-component vector of floating-point values.

mat2

2x2 matrix, in column major order.

mat3

3x3 matrix, in column major order.

mat4

4x4 matrix, in column major order.

sampler2D

Sampler type for binding 2D textures, which are read as float.

isampler2D

Sampler type for binding 2D textures, which are read as signed integer.

usampler2D

Sampler type for binding 2D textures, which are read as unsigned integer.

sampler2DArray

Sampler type for binding 2D texture arrays, which are read as float.

isampler2DArray

Sampler type for binding 2D texture arrays, which are read as signed integer.

usampler2DArray

Sampler type for binding 2D texture arrays, which are read as unsigned integer.

sampler3D

Sampler type for binding 3D textures, which are read as float.

isampler3D

Sampler type for binding 3D textures, which are read as signed integer.

usampler3D

Sampler type for binding 3D textures, which are read as unsigned integer.

samplerCube

Sampler type for binding Cubemaps, which are read as float.

samplerCubeArray

Sampler type for binding Cubemap arrays, which are read as float.

Casting

Just like GLSL ES 3.0, implicit casting between scalars and vectors of the same size but different type is not allowed. Casting of types of different size is also not allowed. Conversion must be done explicitly via constructors.

Example:

float a = 2; // invalid
float a = 2.0; // valid
float a = float(2); // valid

Default integer constants are signed, so casting is always needed to convert to unsigned:

int a = 2; // valid
uint a = 2; // invalid
uint a = uint(2); // valid

Members

Individual scalar members of vector types are accessed via the "x", "y", "z" and "w" members. Alternatively, using "r", "g", "b" and "a" also works and is equivalent. Use whatever fits best for your needs.

For matrices, use the m[column][row] indexing syntax to access each scalar, or m[idx] to access a vector by row index. For example, for accessing the y position of an object in a mat4 you use m[3][1].

Constructing

Construction of vector types must always pass:

// The required amount of scalars
vec4 a = vec4(0.0, 1.0, 2.0, 3.0);
// Complementary vectors and/or scalars
vec4 a = vec4(vec2(0.0, 1.0), vec2(2.0, 3.0));
vec4 a = vec4(vec3(0.0, 1.0, 2.0), 3.0);
// A single scalar for the whole vector
vec4 a = vec4(0.0);

Construction of matrix types requires vectors of the same dimension as the matrix. You can also build a diagonal matrix using matx(float) syntax. Accordingly, mat4(1.0) is an identity matrix.

mat2 m2 = mat2(vec2(1.0, 0.0), vec2(0.0, 1.0));
mat3 m3 = mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 1.0));
mat4 identity = mat4(1.0);

Matrices can also be built from a matrix of another dimension. There are two rules:

1. If a larger matrix is constructed from a smaller matrix, the additional rows and columns are set to the values they would have in an identity matrix. 2. If a smaller matrix is constructed from a larger matrix, the top, left submatrix of the larger matrix is used.

mat3 basis = mat3(MODEL_MATRIX);
mat4 m4 = mat4(basis);
mat2 m2 = mat2(m4);

Swizzling

It is possible to obtain any combination of components in any order, as long as the result is another vector type (or scalar). This is easier shown than explained:

vec4 a = vec4(0.0, 1.0, 2.0, 3.0);
vec3 b = a.rgb; // Creates a vec3 with vec4 components.
vec3 b = a.ggg; // Also valid; creates a vec3 and fills it with a single vec4 component.
vec3 b = a.bgr; // "b" will be vec3(2.0, 1.0, 0.0).
vec3 b = a.xyz; // Also rgba, xyzw are equivalent.
vec3 b = a.stp; // And stpq (for texture coordinates).
float c = b.w; // Invalid, because "w" is not present in vec3 b.
vec3 c = b.xrt; // Invalid, mixing different styles is forbidden.
b.rrr = a.rgb; // Invalid, assignment with duplication.
b.bgr = a.rgb; // Valid assignment. "b"'s "blue" component will be "a"'s "red" and vice versa.

Precision

It is possible to add precision modifiers to datatypes; use them for uniforms, variables, arguments and varyings:

lowp vec4 a = vec4(0.0, 1.0, 2.0, 3.0); // low precision, usually 8 bits per component mapped to 0-1
mediump vec4 a = vec4(0.0, 1.0, 2.0, 3.0); // medium precision, usually 16 bits or half float
highp vec4 a = vec4(0.0, 1.0, 2.0, 3.0); // high precision, uses full float or integer range (default)

Using lower precision for some operations can speed up the math involved (at the cost of less precision). This is rarely needed in the vertex processor function (where full precision is needed most of the time), but is often useful in the fragment processor.

Some architectures (mainly mobile) can benefit significantly from this, but there are downsides such as the additional overhead of conversion between precisions. Refer to the documentation of the target architecture for further information. In many cases, mobile drivers cause inconsistent or unexpected behavior and it is best to avoid specifying precision unless necessary.

Arrays

Arrays are containers for multiple variables of a similar type.

Local arrays

Local arrays are declared in functions. They can use all of the allowed datatypes, except samplers. The array declaration follows a C-style syntax: [const] + [precision] + typename + identifier + [array size].

void fragment() {
    float arr[3];
}

They can be initialized at the beginning like:

float float_arr[3] = float[3] (1.0, 0.5, 0.0); // first constructor

int int_arr[3] = int[] (2, 1, 0); // second constructor

vec2 vec2_arr[3] = { vec2(1.0, 1.0), vec2(0.5, 0.5), vec2(0.0, 0.0) }; // third constructor

bool bool_arr[] = { true, true, false }; // fourth constructor - size is defined automatically from the element count

You can declare multiple arrays (even with different sizes) in one expression:

float a[3] = float[3] (1.0, 0.5, 0.0),
b[2] = { 1.0, 0.5 },
c[] = { 0.7 },
d = 0.0,
e[5];

To access an array element, use the indexing syntax:

float arr[3];

arr[0] = 1.0; // setter

COLOR.r = arr[0]; // getter

Arrays also have a built-in function .length() (not to be confused with the built-in length() function). It doesn't accept any parameters and will return the array's size.

float arr[] = { 0.0, 1.0, 0.5, -1.0 };
for (int i = 0; i < arr.length(); i++) {
    // ...
}

Note

If you use an index either below 0 or greater than array size - the shader will crash and break rendering. To prevent this, use length(), if, or clamp() functions to ensure the index is between 0 and the array's length. Always carefully test and check your code. If you pass a constant expression or a number, the editor will check its bounds to prevent this crash.

Global arrays

You can declare arrays at global space like:

shader_type spatial;

const lowp vec3 v[1] = lowp vec3[1] ( vec3(0, 0, 1) );

void fragment() {
  ALBEDO = v[0];
}

Note

Global arrays have to be declared as global constants, otherwise they can be declared the same as local arrays.

Constants

Use the const keyword before the variable declaration to make that variable immutable, which means that it cannot be modified. All basic types, except samplers can be declared as constants. Accessing and using a constant value is slightly faster than using a uniform. Constants must be initialized at their declaration.

const vec2 a = vec2(0.0, 1.0);
vec2 b;

a = b; // invalid
b = a; // valid

Constants cannot be modified and additionally cannot have hints, but multiple of them (if they have the same type) can be declared in a single expression e.g

const vec2 V1 = vec2(1, 1), V2 = vec2(2, 2);

Similar to variables, arrays can also be declared with const.

const float arr[] = { 1.0, 0.5, 0.0 };

arr[0] = 1.0; // invalid

COLOR.r = arr[0]; // valid

Constants can be declared both globally (outside of any function) or locally (inside a function). Global constants are useful when you want to have access to a value throughout your shader that does not need to be modified. Like uniforms, global constants are shared between all sh