Shader Style Guide

From FlightGear wiki
Jump to navigation Jump to search

FlightGear uses shaders written in GLSL. If you are creating a new shader from scratch, you should be following the style of this documentation. For an introduction to shader programming, see Howto:Shader programming in FlightGear.

Files

  • Vertex, fragment, geometry and compute shaders should have the following file extensions, respectively: .vert, .frag, .geom and .comp (ex: shading_opaque.frag).
  • Spaces in the file name must be substituted by an underscore '_'. You must not use a dash '-'. For example, shader_opaque.frag is correct but shader-opaque.frag is not.
  • Every shader file must contain one and only one main() function. If a shader does not contain a main() function, it is considered a shader library, and its file extension must be .glsl.

Shader libraries

If a function can be used by several shaders, put it on a separate .glsl library file. Shader libraries can be used with other shaders by linking them together. For example, in the Effect file you can write:

<program>
    <fragment-shader>Shaders/HDR/shading.frag</fragment-shader> <!-- Contains the main() function -->
    <fragment-shader>Shaders/HDR/math.glsl</fragment-shader>    <!-- Contains some utility functions that can be used in shading.frag, but no main() -->
</program>

To use the functions contained in math.glsl from shading.frag, you must declare the signatures of the functions you intend to use at the top of the file. For example, if the math.glsl library contains a function named saturate(), in shading.frag you must write:

float saturate(float x);

Due to how OpenGL shader linking works, we can only share functions between shader files. If you want to share a constant across multiple files, create a new library file and write a function that returns the constant. For example:

float M_PI() {
    return 3.14159265358979323846;
}

Keep in mind that structs cannot be shared across shader files, so make sure they are only used locally.

Naming

  • Naming should follow snake_case convention. The only exceptions are type names (ex: PointLight) and the fragColor output variable.
  • Do not use reserved keywords like sampler.
  • Single letter vectors should be a capital letter (ex: N for Normal, instead of n).
  • Sampler resource names should have the _tex suffix (ex: uniform sampler2D color_tex;).

Space prefix

Variable names can have a prefix depending on the space they are in:

  • ws_ for World Space.
  • vs_ for View or Eye Space.
  • ls_ for Local or Model Space.
  • cs_ for Clip Space.
  • ndc_ for NDC coordinates (clip coordinates after perspective division).

File structure

The structure of a shader file should follow this order:

#version 330 core
/*
 * Small description of what the shader does.
 * Try to cite as many resources as possible. Shader code is known to be hard to understand without a reference implementation or any math.
 */

// #defines from the corresponding Effect file
#pragma import_defines(MY_OWN_DEFINE MY_OTHER_DEFINE)

// Fragment writes or vertex attributes
layout(location = 0) out vec4 fragColor;

// Inputs/outputs
in vec2 texcoord;

// Custom uniforms (defined in the .eff file)
uniform sampler2D color_tex;

// Predefined uniforms (from C++)
uniform mat4 fg_ProjectionMatrix;

// Constants
const float MY_CONSTANT = 1.0;

// Function signatures of linked shader libraries
float M_PI();

// Local functions
vec3 my_local_function(in vec3 input, out vec3 output)
{
    // ...
}

// ENTRY POINT
void main()
{
#ifdef MY_OWN_DEFINE
    // ...
#else
    // ...
#endif
}