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.

風格指南並不是一種強制規範。有時候可能會無法符合下列的某些準則,這是則需要自行判斷最佳方法,或是詢問其他開發者的意見。

一般來說,在專案中或是在團隊成員間保持程式碼風格的一致性比嚴格遵守本指南還要重要。

備註

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 換行字元來換行,而不是 CRLF 或 CR。 (編輯器預設值)

  • 每個檔案都以 LF 換行字元來結束。 (編輯器預設值)

  • 使用不帶 BOM <https://zh.wikipedia.org/wiki/%E4%BD%8D%E5%85%83%E7%B5%84%E9%A0%86%E5%BA%8F%E8%A8%98%E8%99%9F>UTF-8(編輯器預設值)

  • 使用 Tab 字元來縮排而不是空白字元。 (編輯器預設值)

縮排

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

使用兩個縮排等級來區分連續行與一般的程式碼區塊。

正確例 :

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.

每行字數限制

將每行程式碼控制在 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 Engine 風格。若不遵守這些規則會讓程式碼與內建的命名規範衝突,進而讓程式碼不一致。

函式與變數

函式名稱與變數使用 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).

區域變數

儘量在首次使用變數前定義區域變數。這樣一來在讀程式碼的時候就比較容易理解,而不需要為了找變數在哪裡定義的而往前翻太多。