Godot shader language 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.

风格指南并不是硬性的规则手册。有时,您可能无法应用下面的一些准则。当这种情况发生时,使用你最好的判断,并询问其他开发人员的见解。

一般来说,在项目和团队中保持代码的一致性比遵循本指南进行tee更为重要。

注解

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;

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

格式

编码和特殊字符

  • 使用换行符(LF)来换行,而不是 CRLFCR。(编辑默认)
  • 在每个文件的末尾使用一个换行符。(编辑器默认设置)
  • 使用不带 字节顺序标记(BOM)UTF-8 编码。(编辑默认)
  • 使用 Tabs 代替制表符进行缩进(称为“软制表符”)。(编辑默认)

缩进

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

良好的

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

糟糕的

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

使用2个缩进级别来区分续行与常规代码块。

良好的

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

糟糕的

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

换行符和空白行

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.

良好的

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

糟糕的

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

空白行

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(可能是字符长度)

把每行代码控制在100个字符以内。

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.

一条语句一行

Never combine multiple statements on a single line.

良好的

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

糟糕的

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

该规则的唯一例外是三元运算符:

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

注释间距

普通注释开头应该留一个空格,但如果是为了停用代码而将其注释掉则不需要留。这样可以用来区分文本注释和停用的代码。

良好的

// This is a comment.
//return;

糟糕的

//This is a comment.
// return;

Don't use multiline comment syntax if your comment can fit on a single line:

/* This is another comment. */

注解

In the shader editor, to make the selected code a comment (or uncomment it), press Ctrl + K. This feature adds or removes // at the start of the selected lines.

空格

Always use one space around operators and after commas. Also, avoid extraneous spaces in function calls.

良好的

COLOR.r = 5.0;
COLOR.r = COLOR.g + 0.1;
COLOR.b = some_function(1.0, 2.0);

糟糕的

COLOR.r=5.0;
COLOR.r = COLOR.g+0.1;
COLOR.b = some_function (1.0,2.0);

不要使用空格垂直对齐表达式:

ALBEDO.r   = 1.0;
EMISSION.r = 1.0;

Floating-point numbers

Always specify at least one digit for both the integer and fractional part. This makes it easier to distinguish floating-point numbers from integers, as well as distinguishing numbers greater than 1 from those lower than 1.

良好的

void fragment() {
    ALBEDO.rgb = vec3(5.0, 0.1, 0.2);
}

糟糕的

void fragment() {
    ALBEDO.rgb = vec3(5., .1, .2);
}

Accessing vector members

Use r, g, b, and a when accessing a vector's members if it contains a color. If the vector contains anything else than a color, use x, y, z, and w. This allows those reading your code to better understand what the underlying data represents.

良好的

COLOR.rgb = vec3(5.0, 0.1, 0.2);

糟糕的

COLOR.xyz = vec3(5.0, 0.1, 0.2);

命名约定

这些命名约定遵循 Godot 引擎风格。打破这些都会使你的代码与内置的命名约定冲突,导致风格不一致的代码。

函数与变量

使用蛇形命名法(snake_case)来命名函数与变量:

void some_function() {
     float some_variable = 0.5;
}

常量

使用 CONSTANT_CASE,全部大写,用下划线(_)分隔单词 :

const float GOLDEN_RATIO = 1.618;

代码顺序

We suggest to organize shader code this way:

01. shader type declaration
02. render mode declaration
03. // docstring

04. uniforms
05. constants
06. varyings

07. other functions
08. vertex() function
09. fragment() function
10. light() function

我们优化了顺序,使从上到下阅读代码变得容易,帮助第一次阅读代码的开发人员了解代码的工作原理,并避免与变量声明顺序相关的错误。

This code order follows two rules of thumb:

  1. Metadata and properties first, followed by methods.
  2. "Public" comes before "private". In a shader language's context, "public" refers to what's easily adjustable by the user (uniforms).

Local variables

声明局部变量的位置离首次使用它的位置越近越好。这让人更容易跟上代码的思路,而不需要上下翻找该变量的声明位置。