Shaders style guide

This style guide lists conventions to write elegant shaders. The goal is to encourage writing clean, readable code and promote consistency across projects, discussions, and tutorials. Hopefully, this will also support the development of auto-formatting tools.

Since the Godot shader language is close to C-style languages and GLSL, this guide is inspired by Godot's own GLSL formatting. You can view an example of a GLSL file in Godot's source code here.

Style guides aren't meant as hard rulebooks. At times, you may not be able to apply some of the guidelines below. When that happens, use your best judgment, and ask fellow developers for insights.

In general, keeping your code consistent in your projects and within your team is more important than following this guide to a tee.

Note

Godot's built-in shader editor uses a lot of these conventions by default. Let it help you.

Here is a complete shader example based on these guidelines:

shader_type canvas_item;
// Screen-space shader to adjust a 2D scene's brightness, contrast
// and saturation. Taken from
// https://github.com/godotengine/godot-demo-projects/blob/master/2d/screen_space_shaders/shaders/BCS.shader

uniform float brightness = 0.8;
uniform float contrast = 1.5;
uniform float saturation = 1.8;

uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;

void fragment() {
    vec3 c = textureLod(screen_texture, SCREEN_UV, 0.0).rgb;

    c.rgb = mix(vec3(0.0), c.rgb, brightness);
    c.rgb = mix(vec3(0.5), c.rgb, contrast);
    c.rgb = mix(vec3(dot(vec3(1.0), c.rgb) * 0.33333), c.rgb, saturation);

    COLOR.rgb = c;
}

Formatting

Encoding and special characters

  • Use line feed (LF) characters to break lines, not CRLF or CR. (editor default)

  • Use one line feed character at the end of each file. (editor default)

  • Use UTF-8 encoding without a byte order mark. (editor default)

  • Use Tabs instead of spaces for indentation. (editor default)

Indentation

Each indent level should be one tab greater than the block containing it.

Good:

void fragment() {
    COLOR = vec3(1.0, 1.0, 1.0);
}

Bad:

void fragment() {
        COLOR = vec3(1.0, 1.0, 1.0);
}

Use 2 indent levels to distinguish continuation lines from regular code blocks.

Good:

vec2 st = vec2(
        atan(NORMAL.x, NORMAL.z),
        acos(NORMAL.y));

Bad:

vec2 st = vec2(
    atan(NORMAL.x, NORMAL.z),
    acos(NORMAL.y));

Line breaks and blank lines

For a general indentation rule, follow the "1TBS Style" which recommends placing the brace associated with a control statement on the same line. Always use braces for statements, even if they only span one line. This makes them easier to refactor and avoids mistakes when adding more lines to an if statement or similar.

Good:

void fragment() {
    if (true) {
        // ...
    }
}

Bad:

void fragment()
{
    if (true)
        // ...
}

Blank lines

Surround function definitions with one (and only one) blank line:

void do_something() {
    // ...
}

void fragment() {
    // ...
}

Use one (and only one) blank line inside functions to separate logical sections.

Line length

Keep individual lines of code under 100 characters.

If you can, try to keep lines under 80 characters. This helps to read the code on small displays and with two shaders opened side-by-side in an external text editor. For example, when looking at a differential revision.

One statement per line

Never combine multiple statements on a single line.

Good:

void fragment() {
    ALBEDO = vec3(1.0);
    EMISSION = vec3(1.0);
}

Bad:

void fragment() {
    ALBEDO = vec3(1.0); EMISSION = vec3(1.0);
}

The only exception to that rule is the ternary operator:

void fragment() {
     bool should_be_white = true;
     ALBEDO = should_be_white ? vec3(1.0) : vec3(0.0);
 }

Comment spacing

Regular comments should start with a space, but