Skip to content

Shader

The Lacking game engine uses a custom shading language called LSL. This language is then transpiled to the respective GPU API shader language (e.g. GLSL, WGSL). The syntax of the language is very similar to the Go syntax, though there are some notable differences. Using Go-like syntax allows one to write in a consistent way game code and shader code. Furthemore, the Go syntax is designed to be easily and unambiguously parsable.

Comments

Comments can be added with // anywhere in the file, where the remainder of the line is considered a comment and is ignored.

// An example comment.

The LSL does not have support for multi-line comments.

Literals

Literals are a mechanism to provide constant in-place values to a shader.

Type Example Description
bool true, false Specifies a boolean value.
int 13, -24 Specifies a 32bit signed integer value.
float -5.3, 2.4 Specifies a 32bit floating point value.

Operators

There are a number of operators supported by the LSL language.

Assignment Operators

Following is a list of assignment operators that can be used to assign a value to a variable.

Operator Description
= Assigns the value to the variable. Both sides need to have the same type.
:= Defines a new variable and initializes it with the contents and type of the value.
+= Adds the value to the variable.
-= Subtracts the value from the variable.
*= Multiplies the variable by the value.
/= Divides the variable by the value.
%= Assigns the modulo of the variable and the value.
<<= Shifts the variable to the left by the value number of bits.
>>= Shifts the variable to the right by the value number of bits.
&= Assigns the bitwise AND operation on the variable and the value to the variable.
│= Assigns the bitwise OR operation on the variable and the value to the variable.
^= Assigns the bitwise XOR operation on the variable and the value to the variable.

Unary Operators

The following unary operators can be used inside expressions to transform a single sub-expression.

Operator Description
! Inverts the value of the expression. It needs to be a boolean expression or a boolean vector expression.
- Negates the value of the expression. It needs to be a numeric expression or a numeric vector expression.
+ A no-op change to the value of the expression. it needs to be a numeric expression vector expression.
^ Performs a bitwise NOT operation on the value of the expression. It needs to be a numeric expression or a numeric vector expression.

Binary Operators

The following binary operators can be used inside expressions to combine two sub-expressions.

Operator Description
+ Returns the sum of the two expressions. Both sides need to be numeric expressions of the same type.
- Returns the difference between the two expressions. Both sides need to be numeric expressions of the same type.
* Returns the multiplication of the two expressions. Both sides need to be numeric expressions of the same type.
/ Returns the division of the two expressions. Both sides need to be numeric expressions of the same type.
% Returns the modulo of the two expressions. Both sides need to be integer expressions of the same type.
<< Returns the bitwise left-shift of the left expression by the right expression. Both sides need to be integer expressions.
>> Returns the bitwise right-shift of the left expression by the right expression. Both sides need to be integer expressions.
== Returns a boolean value indicating whether the two expressions are equal. Both sides need to be expressions of the same type and comparable.
!= Returns a boolean value indicating whether the two expressions are different. Both sides need to be expressions of the same type and comparable.
< Returns a boolean value indicating whether the first expression is smaller than the second expression. Both sides need to be expressions of the same type and be ordered.
> Returns a boolean value indicating whether the second expression is smaller than the first expression. Both sides need to be expressions of the same type and be ordered.
<= Returns a boolean value indicating whether the first expression is smaller than or equal to the second expression. Both sides need to be expressions of the same type and be ordered.
>= Returns a boolean value indicating whether the second expression is smaller than or equal to the first expression. Both sides need to be expressions of the same type and be ordered.
& Returns the result of a bitwise AND operation on the two expressions. Both sides need to be integer expressions of the same type.
Returns the result of a bitwise OR operation on the two expressions. Both sides need to be integer expressions of the same type.
^ Returns the result of a bitwise XOR operation on the two expressions. Both sides need to be integer expressions of the same type.
&& Returns the result of a logical AND operation on the two expressions. Both sides need to be boolean expressions of the same type.
││ Returns the result of a logical OR operation on the two expressions. Both sides need to be boolean expressions of the same type.

The operator precedence is similar to the official Go one and is described in the following table (higher is applied first).

Precedence Operator
5 *, /, %, <<, >>, &
4 +, -, , ^
3 ==, !=, <, <=, >, >=
2 &&
1 ││

Operators with the same precedence associate from left to right (i.e. the operators are applied from left to right).

Types

The following table lists the supported built-in types.

Name Description
bool Boolean type
int 32 bit signed integer type
uint 32 bit unsigned integer type
float 32 bit floating point type
vec2 2D vector type with two 32 bit floating point components
vec3 3D vector type with three 32 bit floating point components
vec4 4D vector type with four 32 bit floating point components
bvec2 2D vector type with two boolean components
bvec3 3D vector type with three boolean components
bvec4 4D vector type with four boolean components
ivec2 2D vector type with two 32 bit signed integer components
ivec3 3D vector type with three 32 bit signed integer components
ivec4 4D vector type with four 32 bit signed integer components
uvec2 2D vector type with two 32 bit unsigned integer components
uvec3 3D vector type with three 32 bit unsigned integer components
uvec4 4D vector type with four 32 bit unsigned integer components
mat2 2x2 matrix type with four 32 bit floating point components
mat3 3x3 matrix type with nine 32 bit floating point components
mat4 4x4 matrix type with sixteen 32 bit floating point components
sampler2D sampler to a 2D texture
samplerCube sampler to a Cube texture

Textures

Shaders often require textures to read texels off of. Such dependencies to external resources are declared through the texture keyword. It works in a similar way to the Go's var keyword, except that an initial value cannot be specified.

texture color sampler2D
texture env samplerCube

or

texture (
  color sampler2D
  env samplerCube
)

Texture fields are globally visible within the shader and are read-only.

Uniforms

Shaders often require external data so that they can be reused. This is done through the uniform keyword. It works in a similar way to the Go's var keyword, except that an initial value cannot be specified.

uniform color vec4
uniform intensity float

or

uniform (
  color vec4
  intensity float
)

Uniform fields are globally visible within the shader and are read-only.

Varying

Shader code is used in both vertex and fragment stages. Sometimes data needs to be passed from one stage onto the next. This is achieved via the varying keyword. It works in a similar way to the Go's var keyword, except that an initial value cannot be specified.

varying normal vec3

or

varying (
  normal vec3
)

Varying fields are globally visible within the shader and are read-only in the fragment shader stage.

Structs

It is possible to define custom struct types.

type Vertex struct {
  position vec3
  uv vec2
}

Functions

The following table lists constructor built-in functions.

Name Scope Description
bool(v int) bool unbounded Converts an integer into a boolean.
bool(v uint) bool unbounded Converts an unsigned integer into a boolean.
bool(v float) bool unbounded Converts a float into a boolean.
int(v bool) int unbounded Converts a boolean into an integer.
int(v uint) int unbounded Converts an unsigned integer into an integer.
int(v float) int unbounded Converts a float into an integer.
uint(v bool) uint unbounded Converts a boolean into an unsigned integer.
uint(v int) uint unbounded Converts an integer into an unsigned integer.
uint(v float) uint unbounded Converts a float into an unsigned integer.
float(v bool) float unbounded Converts a boolean into a float.
float(v int) float unbounded Converts an integer into a float.
float(v uint) float unbounded Converst an unsigned integer into a float.
vec2(v float) vec2 unbounded Returns a vec2 with all components equal to the value v.
vec2(x, y float) vec2 unbounded Returns a vec2 with the components set to x and y respectively.
vec3(v float) vec3 unbounded Returns a vec3 with all components equal to the value v.
vec3(x, y, z float) vec3 unbounded Returns a vec3 with the components set to x, y, and z respectively.
vec3(a vec2, z float) vec3 unbounded Returns a vec3 with the components set to a.x, a.y, and z respectively.
vec3(x float, a vec2) vec3 unbounded Returns a vec3 with the components set to x, a.x, and a.y respectively.
vec4(v float) vec4 unbounded Returns a vec4 with all components equal to the value v.
vec4(x, y, z, w float) vec4 unbounded Returns a vec4 with the components set to x, y, z, and w respectively.
vec4(a vec2, z, w float) vec4 unbounded Returns a vec4 with the components set to a.x, a.y, z, and w respectively.
vec4(x float, a vec2, w float) vec4 unbounded Returns a vec4 with the components set to x, a.x, a.y, and w respectively.
vec4(x, y float, a vec2) vec4 unbounded Returns a vec4 with the components set to x, y, a.x, and a.y respectively.
vec4(a vec3, w float) vec4 unbounded Returns a vec4 with the components set to a.x, a.y, a.z, and w respectively.
vec4(x float, a vec3) vec4 unbounded Returns a vec4 with the components set to x, a.x, a.y, and a.z respectively.
bvec2(v bool) bvec2 unbounded Returns a bvec2 with all components equal to the value v.
bvec2(x, y bool) bvec2 unbounded Returns a bvec2 with the components set to x and y respectively.
bvec3(v bool) bvec3 unbounded Returns a bvec3 with all components equal to the value v.
bvec3(x, y, z bool) bvec3 unbounded Returns a bvec3 with the components set to x, y, and z respectively.
bvec3(a bvec2, z bool) bvec3 unbounded Returns a bvec3 with the components set to a.x, a.y, and z respectively.
bvec3(x bool, a bvec2) bvec3 unbounded Returns a bvec3 with the components set to x, a.x, and a.y respectively.
bvec4(v bool) bvec4 unbounded Returns a bvec4 with all components equal to the value v.
bvec4(x, y, z, w bool) bvec4 unbounded Returns a bvec4 with the components set to x, y, z, and w respectively.
bvec4(a bvec2, z, w bool) bvec4 unbounded Returns a bvec4 with the components set to a.x, a.y, z, and w respectively.
bvec4(x bool, a bvec2, w bool) bvec4 unbounded Returns a bvec4 with the components set to x, a.x, a.y, and w respectively.
bvec4(x, y bool, a bvec2) bvec4 unbounded Returns a bvec4 with the components set to x, y, a.x, and a.y respectively.
bvec4(a bvec3, w bool) bvec4 unbounded Returns a bvec4 with the components set to a.x, a.y, a.z, and w respectively.
bvec4(x bool, a bvec3) bvec4 unbounded Returns a bvec4 with the components set to x, a.x, a.y, and a.z respectively.
ivec2(v int) ivec2 unbounded Returns a ivec2 with all components equal to the value v.
ivec2(x, y int) ivec2 unbounded Returns a ivec2 with the components set to x and y respectively.
ivec3(v int) ivec3 unbounded Returns a ivec3 with all components equal to the value v.
ivec3(x, y, z int) ivec3 unbounded Returns a ivec3 with the components set to x, y, and z respectively.
ivec3(a ivec2, z int) ivec3 unbounded Returns a ivec3 with the components set to a.x, a.y, and z respectively.
ivec3(x int, a ivec2) ivec3 unbounded Returns a ivec3 with the components set to x, a.x, and a.y respectively.
ivec4(v int) ivec4 unbounded Returns a ivec4 with all components equal to the value v.
ivec4(x, y, z, w int) ivec4 unbounded Returns a ivec4 with the components set to x, y, z, and w respectively.
ivec4(a ivec2, z, w int) ivec4 unbounded Returns a ivec4 with the components set to a.x, a.y, z, and w respectively.
ivec4(x int, a ivec2, w int) ivec4 unbounded Returns a ivec4 with the components set to x, a.x, a.y, and w respectively.
ivec4(x, y int, a ivec2) ivec4 unbounded Returns a ivec4 with the components set to x, y, a.x, and a.y respectively.
ivec4(a ivec3, w int) ivec4 unbounded Returns a ivec4 with the components set to a.x, a.y, a.z, and w respectively.
ivec4(x int, a ivec3) ivec4 unbounded Returns a ivec4 with the components set to x, a.x, a.y, and a.z respectively.
uvec2(v uint) uvec2 unbounded Returns a uvec2 with all components equal to the value v.
uvec2(x, y uint) uvec2 unbounded Returns a uvec2 with the components set to x and y respectively.
uvec3(v uint) uvec3 unbounded Returns a uvec3 with all components equal to the value v.
uvec3(x, y, z uint) uvec3 unbounded Returns a uvec3 with the components set to x, y, and z respectively.
uvec3(a uvec2, z uint) uvec3 unbounded Returns a uvec3 with the components set to a.x, a.y, and z respectively.
uvec3(x uint, a uvec2) uvec3 unbounded Returns a uvec3 with the components set to x, a.x, and a.y respectively.
uvec4(v uint) uvec4 unbounded Returns a uvec4 with all components equal to the value v.
uvec4(x, y, z, w uint) uvec4 unbounded Returns a uvec4 with the components set to x, y, z, and w respectively.
uvec4(a uvec2, z, w uint) uvec4 unbounded Returns a uvec4 with the components set to a.x, a.y, z, and w respectively.
uvec4(x uint, a uvec2, w uint) uvec4 unbounded Returns a uvec4 with the components set to x, a.x, a.y, and w respectively.
uvec4(x, y uint, a uvec2) uvec4 unbounded Returns a uvec4 with the components set to x, y, a.x, and a.y respectively.
uvec4(a uvec3, w uint) uvec4 unbounded Returns a uvec4 with the components set to a.x, a.y, a.z, and w respectively.
uvec4(x uint, a uvec3) uvec4 unbounded Returns a uvec4 with the components set to x, a.x, a.y, and a.z respectively.
mat2(x, y vec2) mat2 unbounded Returns a mat2 with columns x and y in order.
mat2(m mat3) mat2 unbounded Returns a mat2 using the upper left portion of the provided matrix.
mat3(x, y, z vec3) mat3 unbounded Returns a mat3 with columns x, y and z in order.
mat3(m mat4) mat3 unbounded Returns a mat3 using the upper left portion of the provided matrix.
mat4(x, y, z, w vec4) mat4 unbounded Returns a mat4 with columns x, y, z and w in order.

The following table lists general math built-in functions.

Name Scope Description
abs(v T) T unbounded Returns the absolute value of the parameter.
sign(v T) T unbounded Returns 1 when positive, 0 when zero, and -1 when negative.
floor(v T) T unbounded Returns the nearest whole number less than or equal to the parameter.
trunc(v T) T unbounded Returns the nearest whole number for which the absolute value is less or equal to the absolute value of the parameter.
round(v T) T unbounded Rounds the value to the nearest whole number.
ceil(v T) T unbounded Returns the nearest whole number greater than or equal to the parameter.
fract(v T) T unbounded In essence, returns x - floor(x).
min(a, b T) T unbounded Returns the minimum of the two values.
max(a, b T) T unbounded Returns the maximum of the two values.
clamp(v, lower, upper T) T unbounded In essence, returns max(lower, min(v, upper)).
mix(a, b, z T) T unbounded Returns the linear interpolation between a and b. In essence, it calculates a * (1-z) + b * z.
smoothstep(a, b, z T) T unbounded Returns the Hermite interpolation between a and b.
length(v T) S unbounded Returns the length of the vector.
distance(a, b T) S unbounded Returns the distance between two vectors. In essence, it returns length(b - a).
dot(a, b T) S unbounded Returns the dot product of two vectors.
cross(a, b T) T unbounded Returns the cross product of two vectors.
normalize(v T) T unbounded Resizes a vector to the unit length.
faceforward(v, i, n T) unbounded Returns the vector v oriented to point away from the surface as dictated by in normal vector n and the incident vector i (which visually "points" towards the surface).
reflect(i, n T) T unbounded Reflects the incident vector i (which visually "points" towards the surface) according to the normal vector n.
refract(i, n T, e S) T unbounded Refracts the incident vector i (which visually "points" towards the surface) according to the normal vector n and the ratio of refraction e.
transpose(m T) T unbounded Returns the transpose of the matrix m. As only square matrices are supported right now, the returned type is always the same.
determinant(m T) K unbounded Returns the determinant of the matrix m.
any(v T) bool unbounded Returns true if any of the components of v are true.
all(v T) bool unbounded Returns true if all of the components of v are true.
atan2(y, x T) T unbounded Returns the atan2 of the parameters y over x.
cos(v T) T unbounded Returns the cosine of the parameter v.
sin(v T) T unbounded Returns the sine of the parameter v.

The following table lists general texture built-in functions.

Name Scope Description
sample(s sampler2D, uv vec2) vec4 unbounded Samples the specified 2D sampler and returns the value at position uv.
sample(s samplerCube, uv vec3) vec4 unbounded Samples the specified Cube sampler and returns the value at position uv.

The following table lists LSL helper functions.

Name Scope Description
extractRotation(matrix mat4) mat3 unbounded Extracts the rotation matrix from a general 3D transformation matrix.
normalFromTexel(texel vec3, scale float) vec3 unbounded Converts a texel value from a texture into a normal, scaled as specified.
vectorToSurface(vector, normal, tangent vec3) vec3 unbounded Transforms the specified vector according to the coordinate space defined by normal and tangent. This is usually used in normal mapping to transform a normal from local space into face orientation space.
billboard(model, camera mat4) mat4 unbounded Takes a model and camera matrices and calculates and returns a new model matrix that will transform the model so that it is always aligned towards the camera.
billboardX(model, camera mat4) mat4 unbounded Takes a model and camera matrices and calculates and returns a new model matrix that will transform the model so that its X axis matches the world X axis and the remaining axes are aligned with the camera's.
billboardY(model, camera mat4) mat4 unbounded Takes a model and camera matrices and calculates and returns a new model matrix that will transform the model so that its Y axis matches the world Y axis and the remaining axes are aligned with the camera's.
billboardZ(model, camera mat4) mat4 unbounded Takes a model and camera matrices and calculates and returns a new model matrix that will transform the model so that its Z axis matches the world Z axis and the remaining axes are aligned with the camera's.

Predefined Variables

Following are variables that are pre-defined for some of the render stages.

Name Type Mode Scope Description
#time float32 ReadOnly #vertex, #fragment (shadow, geometry, forward, sky) The time in seconds since the scene was created.
#spawnTime float32 ReadOnly #vertex, #fragment (shadow, geometry, forward) The time in seconds since the mesh was created.
#vertexCoord vec4 ReadOnly #vertex (shadow, geometry, forward) Contains the position of the vertex in local space. Value is vec4(0.0, 0.0, 0.0, 1.0) if the mesh does not contain vertex coords.
#vertexNormal vec3 ReadOnly #vertex (shadow, geometry, forward) Contains the normal of the vertex in local space. Value is vec3(0.0, 0.0, 1.0) if the mesh does not contain vertex normals.
#vertexTangent vec3 ReadOnly #vertex (shadow, geometry, forward) Contains the tangent of the vertex in local space. Value is vec3(1.0, 0.0, 0.0) if the mesh does not contain vertex tangents.
#vertexUV vec2 ReadOnly #vertex (shadow, geometry, forward) Contains the texture coordinates of the vertex. Value is vec2(0.0, 0.0) if the mesh does not contain vertex texture coords.
#vertexColor vec4 ReadOnly #vertex (shadow, geometry, forward) Contains the color of the vertex. Value is vec4(1.0, 1.0, 1.0, 1.0) if the mesh does not contain vertex coloring.
#modelMatrix mat4 ReadOnly #vertex (shadow, geometry, forward) Contains the model transformation matrix for the rendered object.
#cameraMatrix mat4 ReadOnly #vertex, #fragment (shadow, geometry, forward, sky) Contains the camera transformation matrix. NOTE: Not to be confused with #viewMatrix, which is often the desired one.
#viewMatrix mat4 ReadOnly #vertex, #fragment (shadow, geometry, forward, sky) Contains the view transformation matrix. It is equal to the inverse of the #cameraMatrix but is already pre-calculated.
#projectionMatrix mat4 ReadOnly #vertex, #fragment (shadow, geometry, forward, sky) Contains the projection matrix.
#viewport vec4 ReadOnly #vertex, #fragment (shadow, geometry, forward, sky) The x and y fields contain the positioning of the viewport and z and w contain the size of the viewport.
#position vec4 ReadWrite #vertex (shadow, geometry, forward) Contains the output position of the vertex from a vertex shader.
#color vec4 ReadWrite #fragment (geometry, forward, sky) Specifies the color to be placed on the screen. Default value is vec4(0.0, 0.0, 0.0, 1.0).
#normal vec3 ReadWrite #fragment (geometry) Contains the output normal of a texel from a geometry fragment shader.
#metallic float ReadWrite #fragment (geometry) Contains the output metallic value of a texel from a geometry fragment shader.
#roughness float ReadWrite #fragment (geometry) Contains the output roughness value of a texel from a geometry fragment shader.
#varyingNormal vec3 ReadWrite #vertex, #fragment (geometry) A varying variable used to transfer a normal value between shader stages in a geometry shader. If a #vertex function is not specified, this value is automatically filled. NOTE: Make sure to normalize before usage due to interpolation.
#varyingTangent vec3 ReadWrite #vertex, #fragment (geometry) A varying variable used to transfer a tangent value between shader stages in a geometry shader. If a #vertex function is not specified, this value is automatically filled. NOTE: Make sure to normalize before usage due to interpolation.
#varyingUV vec2 ReadWrite #vertex, #fragment (geometry) A varying variable used to transfer a texture coordinate value between shader stages in a geometry shader. If a #vertex function is not specified, this value is automatically filled.
#varyingColor vec4 ReadWrite #vertex, #fragment (geometry) A varying variable used to transfer a color value between shader stages in a geometry shader. If a #vertex function is not specified, this value is automatically filled.
#direction vec3 ReadOnly #fragment (sky) Contains the world space direction of the ray that is being rendered.