W4 Material System

From Ciliz|W4

W4 Engine Wiki > Complex subsystems > Material System:

Scope[edit]

The W4 Framework contains a set of standard materials sufficient for the implementation of typical projects within the engine specialization. Further below we will examine the solutions used in the W4 Engine.

Note. If you still need to create your own material or apply material from a third-party resource, it is suggested to read the article W4_Material_Creation.

General concepts[edit]

Shader attributes[edit]

List of standard shader attributes (passed data) that can be used in W4 Engine:

vec3 w4_a_position;
vec3 w4_a_normal;
vec3 w4_a_tangent;
vec3 w4_a_bitangent;
vec2 w4_a_uv0;
vec2 w4_a_uv1;
vec2 w4_a_uv2;
vec2 w4_a_uv3;
vec4 w4_a_color;

#skinnedMesh
vec4 w4_a_boneId0;
vec4 w4_a_boneId1;
vec4 w4_a_boneWeight0;
vec4 w4_a_boneWeight1;

Shader uniforms[edit]

Uniforms (parameters, which are used to work with the shader) can be either set from code or loaded from a material file (see W4_Material_File_Format).

Standard parameters that are initially present in W4 Engine:

float w4_u_time;
mat4 w4_u_projection;
mat4 w4_u_view;
mat4 w4_u_projectionView;
vec3 w4_u_eyePosition; 
vec3 w4_u_ambientDirection;
vec4 w4_u_ambientColor;
vec2 w4_u_resolution;

mat4 w4_u_model;
mat3 w4_u_normalSpace;

sampler2D texture0;
sampler2D texture1;
sampler2D texture2;
sampler2D texture3;
sampler2D texture4; 
sampler2D texture5;

#skinnedMesh
mat4[28] w4_u_bones;

User parameters[edit]

In addition to the standard parameters described above, the material system allows to apply user parameters.

If the user adds some new parameter in the shader code, then this parameter is passed by the engine with no additional effort. This allows you to add a variety of shaders to your project.

Materials by object type[edit]

The W4 Engine material system generates materials itself, depending on the type of the object for which the material is used. For example, with the same source code for a custom shader, the resulting material for a regular mesh does not handle animations (they are not needed), but for a skinned mesh, it does. As a result, two specialized shaders are generated from one.

Similarly, having initially one source shader, a different shading can be applied for various model types (e.g. see W4_LIGHT_MODEL below).

Material system models[edit]

The current implementation of the W4 Engine contains several models for working with materials.

W4 LIGHT MODEL[edit]

W4_LIGHT_MODEL is a set of solutions for implementing lighting.

BuildIn shader defines[edit]

When using the W4_LIGHT_MODEL model, all default materials have the following parameters to customize:

  • W4_DIRECTIONAL_LIGHT - sets the availability of directional light sources;
  • W4_POINT_LIGHT - sets the availability of point light sources;
  • W4_SPOT_LIGHT - sets the availability of spot (projection) light sources;
  • W4_N_DIRECTIONAL_LIGHT - sets the quantity of directional light sources;
  • W4_N_POINT_LIGHT - sets the quantity of point light sources;
  • W4_N_SPOTS_LIGHT - sets the quantity of spot (projection) light sources;
  • W4_STATIC / W4_SKINNED - type of surface to drawn;
  • W4_NO_SHADOWS / W4_SHADOWS - does the surface display shadows or not.

There are also special parameters that add functionality to the material. These parameters are described in the material:

  • W4_LIGHT_MODEL N - W4_LIGHT_MODEL specifies the use of a standard lighting model, with N being the number of parameters of the lighting model.

How to use[edit]

Let's examine how to use W4_LIGHT_MODEL with the help of the Bump material example. In this case, the bump.mat file looks the following way:

{
    "vertexFile" : "resources/materials/shaders/bump.vs",
    "fragmentFile" : "resources/materials/shaders/bump.fs",
    "params" : {
        "texture0": {"type": "TEXTURE", "data": {"path": "resources/textures/bricks2.jpg", "type": "file"}, "sWrapping": "Repeat", "tWrapping": "Repeat"},
        "texture1": {"type": "TEXTURE", "data": {"path": "resources/textures/bricks2_normal.jpg", "type": "file"}, "sWrapping": "Repeat", "tWrapping": "Repeat"}
    },
    "defines": [
        "W4_LIGHT_MODEL 2"
    ]
}

The vertex shader does not change:

attribute vec3 w4_a_normal;
attribute vec3 w4_a_tangent;
attribute vec2 w4_a_uv0;

varying vec3 v_position;
varying vec3 v_eyePosition;
varying vec3 v_normal;
varying vec3 v_tangent;

varying mat3 v_TBN;
varying vec2 v_TexCoord;

void w4_main()
{
    vec4 pos = w4_getVertexPosition();

    v_position = pos.xyz;
    v_normal  = w4_u_normalSpace * w4_a_normal;
    v_tangent = w4_u_normalSpace * w4_a_tangent;
    v_eyePosition  = w4_u_eyePosition;

    vec3 T = normalize(v_tangent);
    vec3 N = normalize(v_normal);
    vec3 B = normalize(cross(T, N));

    v_TBN = mat3(T, B, N);

    v_TexCoord = w4_a_uv0;
    gl_Position = w4_u_projectionView * pos;
}

Whereas the fragment shader has some features:

uniform sampler2D texture0;
uniform sampler2D texture1;
uniform sampler2D texture2;
uniform vec3 mainColor;

varying vec3 v_eyePosition;
varying vec3 v_normal;
varying vec3 v_position;
varying vec3 v_tangent;
varying mat3 v_TBN;

varying vec2 v_TexCoord;

const float shininess = 32.0;

void w4_userLightModel(inout vec3 results[2], vec3 vectorToLight, vec3 vectorToEye, vec3 N, vec3 lColor, float fading)
{
    w4_BlinnLightModel(results, vectorToLight, vectorToEye, N, lColor, fading, shininess);
}

void w4_main()
{
   vec4 diffColor = texture2D(texture0, v_TexCoord);

   vec3 bumpMap = texture2D(texture1, v_TexCoord).rgb;
   bumpMap = normalize(bumpMap * 2.0 - 1.0);
   bumpMap = normalize(v_TBN * bumpMap);

   vec3 N = normalize ( bumpMap );

   vec3 lightFactors[2];
   w4_calculateLightFactor(lightFactors, v_position, v_eyePosition, N);
   gl_FragColor = vec4(diffColor.rgb * (lightFactors[0] + lightFactors[1]), 1.);
}

When using the W4_LIGHT_MODEL parameter, you can apply the w4_calculateLightFactor function. This function calculates the lighting value, taking into account (consideration) all the necessary light sources. Signature:

void w4_calculateLightFactor(inout vec3 results[W4_LIGHT_MODEL], vec3 pos, vec3 eye, vec3 normal)

The function returns the value through the input parameter results (W4_LIGHT_MODEL dimensional array)

In order for the function to work, a custom lighting model must be implemented:

void w4_userLightModel(inout vec3 results[W4_LIGHT_MODEL], vec3 vectorToLight, vec3 vectorToEye, vec3 N, vec3 lColor, float fading);

In our case, the standard function is used for calculating lighting according to the Blinn model

void w4_BlinnLightModel(inout vec3 results[W4_LIGHT_MODEL], vec3 vectorToLight, vec3 vectorToEye, vec3 N, vec3 lColor, float fading, float shininess);

In the example above, we have value 2 specified in "W4_LIGHT_MODEL". This is explained by the fact that different lighting models may require a different number of returned values. For instance, the Blinn model operates with two or more return values. In results [0], diffuse light components are stored, and in results [1] - specular.

The following are standard supported lighting models:

void w4_LambertLightModel(inout vec3 results[W4_LIGHT_MODEL], vec3 vectorToLight, vec3 vectorToEye, vec3 N, vec3 lColor, float fading); //Lambert lighting

void w4_BlinnLightModel(inout vec3 results[W4_LIGHT_MODEL], vec3 vectorToLight, vec3 vectorToEye, vec3 N, vec3 lColor, float fading, float shininess); //Blinn lighting