シェーダースタイルガイド

このスタイルガイドには、エレガントなシェーダーを作成するための規則が記載されています。目標はクリーンで読みやすいコードの作成を奨励し、プロジェクト、ディスカッション、チュートリアル全体の一貫性を促進することです。うまくいけば、これも自動フォーマットツールの開発に役立つでしょう。

Godotシェーダー言語はCスタイル言語およびGLSLに近いため、このガイドはGodot独自のGLSLフォーマットからインスピレーションを得ています。Godotのソース コードのGLSLファイルの例は こちら でご覧いただけます。

スタイルガイドは、厳密なルールブックを意味するものではありません。時には以下のガイドラインの一部を適用できない場合があります。その場合は、最善であるように判断を下すと共に、仲間の開発者に洞察を求めてください。

一般に、プロジェクト内およびチーム内でコードの一貫性を保つことは、このガイドに完璧に従うよりも重要です。

注釈

Godotに組み込まれているシェーダーエディタは、デフォルトでこれらの規則の多くを使用します。それはあなたの助けになるでしょう。

これらのガイドラインに基づいた完璧なシェーダーの例を次に示します:

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.gdshader

uniform sampler2D screen_texture : hint_screen_texture, filter_linear_mipmap;
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;
}

書式設定

エンコードと特殊文字

  • CRLFやCRではなく、改行(LF)文字を使用して改行します。(エディタのデフォルト)

  • 各ファイルの最後に1つの改行文字を使用します。(エディタのデフォルト)

  • バイトオーダーマークなしでUTF-8エンコーディングを使用します。(エディタのデフォルト)

  • インデントにはスペースの代わりにタブを使用します。(エディタのデフォルト)

インデント

各インデント レベルは、それを含むブロックより1タブ分大きくなければなりません。

良い例:

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

改行と空白行

一般的なインデントルールについては、"1TBSスタイル" <https://en.wikipedia.org/wiki/Indentation_style#Variant:_1TBS_(OTBS)>`_ に従ってください。これは制御ステートメントに関連付けられた中括弧{ }を同じ行に配置することを推奨しています。ステートメントが1行しかない場合でも、ステートメントには必ず中括弧{ }を使用してください。これによりリファクタリングが容易になり、 if ステートメントなどにさらに行を追加するときの間違いを避けることができます。

良い例:

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

悪い例:

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

空白行

関数定義を1つ (だけ) の空白行で囲みます:

void do_something() {
    // ...
}

void fragment() {
    // ...
}

論理セクションを区切るには、関数内で1行の空白行を使用します。

行の長さ

コードの個々の行を100文字以内に保ちます。

可能であれば、1行を80文字未満に保つようにしてください。これは小さなディスプレイでコードを読む場合や、2つのシェーダーを外部テキスト エディタで並べて開くときに役立ちます。たとえばリビジョンの差分を見る場合などです。

1行につき1つのステートメント

複数のステートメントを1行に結合しないようにします。

良い例:

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;

コメントが1行に収まる場合は、複数行コメント構文を使用しないようにします。

/* This is another comment. */

注釈

シェーダーエディタで、選択したコードをコメントにする (またはコメントを解除する) には、 Ctrl + K を押します。この機能は、選択した行の先頭に // を追加または削除します。

Documentation comments

Use the following format for documentation comments above uniforms, with two leading asterisks (/**) and follow-up asterisks on every line:

/**
 * This is a documentation comment.
 * These lines will appear in the inspector when hovering the shader parameter
 * named "Something".
 * You can use [b]BBCode[/b] [i]formatting[/i] in the comment.
 */
uniform int something = 1;

These comments will appear when hovering a property in the inspector. If you don't wish the comment to be visible in the inspector, use the standard comment syntax instead (// ... or /* ... */ with only one leading asterisk).

空白

演算子の前後およびコンマの後には常に1つのスペースを使用します。また関数呼び出しでは無関係なスペースを避けるようにします。

良い例:

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;

浮動小数点数 (実数)

整数部と小数部の両方に必ず少なくとも1桁を指定してください。これにより浮動小数点数と整数の区別が容易になるだけでなく、1より大きい数値と1未満の数値の区別も容易になります。

良い例:

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

悪い例:

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

ベクトル要素へのアクセス

色が含まれているベクトル要素にアクセスするときには r , g , b , a を使用します。色以外のものが含まれるベクトルの場合は x , y , z , w を使用します。これによりコードを読む人は、根底にあるデータが何を表しているのかをよりよく理解できるようになります。

良い例:

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;

プリプロセッサ定義

シェーダープリプロセッサー 定義は CONSTANT_CASE で記述する必要があります。これは関数内にネストされている場合でも、その前にインデントを付けずに記述する必要があります。

シェーダーエラーがコンソールに出力されるときにインデントの自然な流れを維持するには #if#ifdef#ifndef ブロック内に余分なインデントを 追加しないでください

良い例:

#define HEIGHTMAP_ENABLED

void fragment() {
    vec2 position = vec2(1.0, 2.0);

#ifdef HEIGHTMAP_ENABLED
    sample_heightmap(position);
#endif
}

悪い例:

#define heightmap_enabled

void fragment() {
    vec2 position = vec2(1.0, 2.0);

    #ifdef heightmap_enabled
        sample_heightmap(position);
    #endif
}

コードの順序

次のようにシェーダーコードを整理することをお勧めします:

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

順序を最適化して、コードを上から下に読みやすくし、コードを初めて読む開発者がどのように機能するかを理解しやすくし、変数宣言の順序に関連するエラーを回避します。

このコードの順序は、次の2つのルールに従います:

  1. 最初にメタデータとプロパティ、次にメソッドが続きます。

  2. "パブリック"は"プライベート"の前に来ます。シェーダー言語のコンテキストでは、"パブリック"とはユーザーが簡単に調整できるもの (Uniform) を指します。

ローカル変数

ローカル変数はできるだけ最初に使用する直前に宣言してください。これにより変数が宣言されている場所を見つけるためにスクロールしすぎずに、コードをたどるのが簡単になります。