| @ -1,70 +1,114 @@ | |||
| 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, the shader is responsible for processing all the data sent to the GPU (e.g. mesh, materials, textures, lighting) to generate the final frame. | |||
| 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, the shader is 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 and, 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 supports the following data: | |||
| By default, raylib's shader struct is defined as: | |||
| ```c | |||
| typedef struct Shader { | |||
| unsigned int id; // Shader program id | |||
| // Vertex attributes locations (default locations) | |||
| int vertexLoc; // Vertex attribute location point (default-location = 0) | |||
| int texcoordLoc; // Texcoord attribute location point (default-location = 1) | |||
| int texcoord2Loc; // Texcoord2 attribute location point (default-location = 5) | |||
| int normalLoc; // Normal attribute location point (default-location = 2) | |||
| int tangentLoc; // Tangent attribute location point (default-location = 4) | |||
| int colorLoc; // Color attibute location point (default-location = 3) | |||
| // Uniform locations | |||
| int mvpLoc; // ModelView-Projection matrix uniform location point (vertex shader) | |||
| int tintColorLoc; // Diffuse color uniform location point (fragment shader) | |||
| // Texture map locations (generic for any kind of map) | |||
| int mapTexture0Loc; // Map texture uniform location point (default-texture-unit = 0) | |||
| int mapTexture1Loc; // Map texture uniform location point (default-texture-unit = 1) | |||
| int mapTexture2Loc; // Map texture uniform location point (default-texture-unit = 2) | |||
| unsigned int id; // Shader program id | |||
| int locs[MAX_SHADER_LOCATIONS]; // Shader locations array | |||
| } Shader; | |||
| ``` | |||
| As you can see, most of the location points are pre-defined **on shader loading**; custom shaders developed for raylib must follow those conventions. | |||
| This struct provides an array to store shader locations, those locations can be accessed by position using predefined values for convenience: | |||
| ```c | |||
| // 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; | |||
| On shader load, the following fixed location names are expected for maps: | |||
| #define LOC_MAP_DIFFUSE LOC_MAP_ALBEDO | |||
| #define LOC_MAP_SPECULAR LOC_MAP_METALNESS | |||
| ``` | |||
| On shader loading, the following location names are checked: | |||
| ```glsl | |||
| uniform sampler2D texture0; // GL_TEXTURE0 | |||
| uniform sampler2D texture1; // GL_TEXTURE1 | |||
| uniform sampler2D texture2; // GL_TEXTURE2 | |||
| 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 | |||
| ``` | |||
| Shaders are also directly related to the Material struct: | |||
| ```c | |||
| // Material type | |||
| typedef struct Material { | |||
| Shader shader; // Standard shader (supports 3 map textures) | |||
| Texture2D texDiffuse; // Diffuse texture (bound to shader mapTexture0Loc) | |||
| Texture2D texNormal; // Normal texture (bound to shader mapTexture1Loc) | |||
| Texture2D texSpecular; // Specular texture (bound to shader mapTexture2Loc) | |||
| Color colDiffuse; // Diffuse color | |||
| Color colAmbient; // Ambient color | |||
| Color colSpecular; // Specular color | |||
| float glossiness; // Glossiness level (Ranges from 0 to 1000) | |||
| // Material type (generic) | |||
| typedef struct Material { | |||
| Shader shader; // Material shader | |||
| MaterialMap maps[MAX_MATERIAL_MAPS]; // Material maps | |||
| float *params; // Material generic parameters (if required) | |||
| } Material; | |||
| ``` | |||
| Where three texture maps (texDiffuse, texNormal, texSpecular) will bind to shader location points. | |||
| When drawing, textures are internally bound or not depending on the selected shader and material: | |||
| Material support by default a number of maps (texture and properties) that can be accessed for convenience using the provided values: | |||
| ```c | |||
| // Default material loading example | |||
| Material material = LoadDefaultMaterial(); // Default shader assigned to material (supports only diffuse map) | |||
| material.texDiffuse = LoadTexture("wood_diffuse.png"); // texture unit 0 activated (available in material shader) | |||
| material.texSpecular = LoadTexture("wood_specular.png"); // texture unit 2 activated (available in material shader) | |||
| // 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 | |||
| ``` | |||
| // Standard material loading example | |||
| Material material = LoadStandardMaterial(); // Standard shader assigned to material (supports diffuse, normal and specular maps) | |||
| material.texDiffuse = LoadTexture("wood_diffuse.png"); // texture unit 0 activated (available in material shader) | |||
| material.texSpecular = LoadTexture("wood_specular.png"); // texture unit 2 NOT activated (not available in material shader) | |||
| When drawing, maps are internally bound or not depending on the availability: | |||
| ```c | |||
| // Default material loading example | |||
| Material material = LoadMaterialDefault(); // Default shader assigned to material | |||
| material.maps[MAP_DIFFUSE] = LoadTexture("tex_diffuse.png"); // texture unit 0 activated (available in material shader) | |||
| material.maps[MAP_SPECULAR] = LoadTexture("tex_specular.png"); // texture unit 1 activated (available in material shader) | |||
| ``` | |||
| Despite its name on material struct (`texDiffuse`, `texNormal`, `texSpecular`), the user is free to use those maps in any way inside the **custom** shader. | |||
| User can load any custom shader using provided `Material` and `Shader`structs: | |||
| ```c | |||
| 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] = LoadTexture("tex_albedo.png"); | |||
| material.maps[1] = LoadTexture("tex_metalness.png"); | |||
| material.maps[2] = LoadTexture("tex_normal.png"); | |||
| ``` | |||