Skip to content

raylib generic uber shader and custom shaders

Ethan L. Y. Trithon edited this page Oct 6, 2020 · 14 revisions

Dealing with custom shaders and making them generic is not an easy task. There are many things to consider for a shader because, after all, shaders are responsible for processing all the data sent to the GPU (mesh, materials, textures, lighting) to generate the final frame.

Finding a unified generic shader to deal with all kinds of stuff is very complicated so, after analyzing some of the big engines out there, I decided to go for a custom uber-shader-based solution.

By default, raylib's shader struct is defined as:

typedef struct Shader {
    unsigned int id;   // Shader program id
    int *locs;         // Shader locations array (MAX_SHADER_LOCATIONS)
} Shader;

This struct provides an array to store shader locations, those locations can be accessed by position using predefined enum values for convenience:

// Shader location point type
typedef enum {
    LOC_VERTEX_POSITION = 0,
    LOC_VERTEX_TEXCOORD01,
    LOC_VERTEX_TEXCOORD02,
    LOC_VERTEX_NORMAL,
    LOC_VERTEX_TANGENT,
    LOC_VERTEX_COLOR,
    LOC_MATRIX_MVP,
    LOC_MATRIX_MODEL,
    LOC_MATRIX_VIEW,
    LOC_MATRIX_PROJECTION,
    LOC_VECTOR_VIEW,
    LOC_COLOR_DIFFUSE,
    LOC_COLOR_SPECULAR,
    LOC_COLOR_AMBIENT,
    LOC_MAP_ALBEDO,          // LOC_MAP_DIFFUSE
    LOC_MAP_METALNESS,       // LOC_MAP_SPECULAR
    LOC_MAP_NORMAL,
    LOC_MAP_ROUGHNESS,
    LOC_MAP_OCCUSION,
    LOC_MAP_EMISSION,
    LOC_MAP_HEIGHT,
    LOC_MAP_CUBEMAP,
    LOC_MAP_IRRADIANCE,
    LOC_MAP_PREFILTER,
    LOC_MAP_BRDF
} ShaderLocationIndex;

#define LOC_MAP_DIFFUSE      LOC_MAP_ALBEDO
#define LOC_MAP_SPECULAR     LOC_MAP_METALNESS

When loading a shader, raylib tries to bind by default the following vertex shader attributes:

// Default shader vertex attribute names to set location points
#define DEFAULT_SHADER_ATTRIB_NAME_POSITION    "vertexPosition"    // Bound by default to shader location: 0
#define DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD    "vertexTexCoord"    // Bound by default to shader location: 1
#define DEFAULT_SHADER_ATTRIB_NAME_NORMAL      "vertexNormal"      // Bound by default to shader location: 2
#define DEFAULT_SHADER_ATTRIB_NAME_COLOR       "vertexColor"       // Bound by default to shader location: 3
#define DEFAULT_SHADER_ATTRIB_NAME_TANGENT     "vertexTangent"     // Bound by default to shader location: 4
#define DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2   "vertexTexCoord2"   // Bound by default to shader location: 5

And also the following uniform location names are checked:

uniform mat4 mvp;             // VS: ModelViewProjection matrix
uniform mat4 projection;      // VS: Projection matrix
uniform mat4 view;            // VS: View matrix

uniform vec4 colDiffuse;      // FS: Diffuse color
uniform sampler2D texture0;   // FS: GL_TEXTURE0
uniform sampler2D texture1;   // FS: GL_TEXTURE1
uniform sampler2D texture2;   // FS: GL_TEXTURE2

Those are the attributes/uniform names used by the internal default shader. It's recommended to use that naming convention also on custom shaders to make sure everything will be automatically setup on LoadShader() but some of the default attribute names could re-defined in config.h in case it was necessary.

Shaders are also directly related to the Material struct:

// Material type (generic)
typedef struct Material {
    Shader shader;          // Material shader
    MaterialMap *maps;      // Material maps array (MAX_MATERIAL_MAPS)
    float *params;          // Material generic parameters (if required)
} Material;

Material supports by default a number of maps (texture and properties) that can be accessed for convenience using the provided values:

// Material map type
typedef enum {
    MAP_ALBEDO    = 0,       // MAP_DIFFUSE
    MAP_METALNESS = 1,       // MAP_SPECULAR
    MAP_NORMAL    = 2,
    MAP_ROUGHNESS = 3,
    MAP_OCCLUSION,
    MAP_EMISSION,
    MAP_HEIGHT,
    MAP_CUBEMAP,             // NOTE: Uses GL_TEXTURE_CUBE_MAP
    MAP_IRRADIANCE,          // NOTE: Uses GL_TEXTURE_CUBE_MAP
    MAP_PREFILTER,           // NOTE: Uses GL_TEXTURE_CUBE_MAP
    MAP_BRDF
} TexmapIndex;

#define MAP_DIFFUSE      MAP_ALBEDO
#define MAP_SPECULAR     MAP_METALNESS

When drawing, maps are internally bound or not depending on the availability:

// Default material loading example
Material material = LoadMaterialDefault();                      // Default shader assigned to material
material.maps[MAP_DIFFUSE].texture = LoadTexture("tex_diffuse.png");    // texture unit 0 activated (available in material shader)
material.maps[MAP_SPECULAR].texture = LoadTexture("tex_specular.png");  // texture unit 1 activated (available in material shader)

User can load any custom shader using provided Material and Shaderstructs:

Material material = { 0 };     // Empty material

material.shader = LoadShader("custom_shader.vs", "custom_shader.fs");

// Setup location points in case names are not predefined ones or more locations are required
// Use: GetShaderLocation() and SetShaderValue*() functions

material.maps[0].texture = LoadTexture("tex_albedo.png");
material.maps[1].texture = LoadTexture("tex_metalness.png");
material.maps[2].texture = LoadTexture("tex_normal.png");
Clone this wiki locally