virtualx-engine-docs/_sources/tutorials/shaders/shaders_style_guide.rst.txt
2024-10-23 09:41:33 -07:00

355 lines
8.1 KiB
ReStructuredText

.. _doc_shaders_style_guide:
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 <https://github.com/godotengine/godot/blob/master/drivers/gles2/shaders/copy.glsl>`__.
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:
.. code-block:: glsl
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;
}
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 <https://en.wikipedia.org/wiki/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**:
.. code-block:: glsl
void fragment() {
COLOR = vec3(1.0, 1.0, 1.0);
}
**Bad**:
.. code-block:: glsl
void fragment() {
COLOR = vec3(1.0, 1.0, 1.0);
}
Use 2 indent levels to distinguish continuation lines from
regular code blocks.
**Good**:
.. code-block:: glsl
vec2 st = vec2(
atan(NORMAL.x, NORMAL.z),
acos(NORMAL.y));
**Bad**:
.. code-block:: glsl
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" <https://en.wikipedia.org/wiki/Indentation_style#Variant:_1TBS_(OTBS)>`_
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**:
.. code-block:: glsl
void fragment() {
if (true) {
// ...
}
}
**Bad**:
.. code-block:: glsl
void fragment()
{
if (true)
// ...
}
Blank lines
~~~~~~~~~~~
Surround function definitions with one (and only one) blank line:
.. code-block:: glsl
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**:
.. code-block:: glsl
void fragment() {
ALBEDO = vec3(1.0);
EMISSION = vec3(1.0);
}
**Bad**:
.. code-block:: glsl
void fragment() {
ALBEDO = vec3(1.0); EMISSION = vec3(1.0);
}
The only exception to that rule is the ternary operator:
.. code-block:: glsl
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 not code that you comment out.
This helps differentiate text comments from disabled code.
**Good**:
.. code-block:: glsl
// This is a comment.
//return;
**Bad**:
.. code-block:: glsl
//This is a comment.
// return;
Don't use multiline comment syntax if your comment can fit on a single line:
.. code-block:: glsl
/* This is another comment. */
.. note::
In the shader editor, to make the selected code a comment (or uncomment it),
press :kbd:`Ctrl + K`. This feature adds or removes ``//`` at the start of
the selected lines.
Whitespace
~~~~~~~~~~
Always use one space around operators and after commas. Also, avoid extraneous spaces
in function calls.
**Good**:
.. code-block:: glsl
COLOR.r = 5.0;
COLOR.r = COLOR.g + 0.1;
COLOR.b = some_function(1.0, 2.0);
**Bad**:
.. code-block:: glsl
COLOR.r=5.0;
COLOR.r = COLOR.g+0.1;
COLOR.b = some_function (1.0,2.0);
Don't use spaces to align expressions vertically:
.. code-block:: glsl
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.
**Good**:
.. code-block:: glsl
void fragment() {
ALBEDO.rgb = vec3(5.0, 0.1, 0.2);
}
**Bad**:
.. code-block:: glsl
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.
**Good**:
.. code-block:: glsl
COLOR.rgb = vec3(5.0, 0.1, 0.2);
**Bad**:
.. code-block:: glsl
COLOR.xyz = vec3(5.0, 0.1, 0.2);
Naming conventions
------------------
These naming conventions follow the Godot Engine style. Breaking these will make
your code clash with the built-in naming conventions, leading to inconsistent
code.
Functions and variables
~~~~~~~~~~~~~~~~~~~~~~~
Use snake\_case to name functions and variables:
.. code-block:: glsl
void some_function() {
float some_variable = 0.5;
}
Constants
~~~~~~~~~
Write constants with CONSTANT\_CASE, that is to say in all caps with an
underscore (\_) to separate words:
.. code-block:: glsl
const float GOLDEN_RATIO = 1.618;
Code order
----------
We suggest to organize shader code this way:
.. code-block:: glsl
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
We optimized the order to make it easy to read the code from top to bottom, to
help developers reading the code for the first time understand how it works, and
to avoid errors linked to the order of variable declarations.
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
~~~~~~~~~~~~~~~
Declare local variables as close as possible to their first use. This makes it
easier to follow the code, without having to scroll too much to find where the
variable was declared.