########################
Assembling New Materials
########################


Our new material equation gathers all the calculations made before. It manages:

    * A diffuse + specular sun lighting and shadowing
    * A Fresnel based reflectance model of a HDR environment map, modulated by the ambient occlusion layer
    * A diffuse skylight model with ambient occlusion
    * A simple energy preservation model
    * A transparency color

All these shading steps are gathered into one single shader for performance reasons. So we'll write a custom shader here. Our data flow is described below:

.. figure:: shading_dataflow.png
    :align: center
    
    **The data flow in our custom shader.**


********************************
Custom CAD Shader Vertex Program
********************************

Not that much to say on it. It's mainly a pass-through shader that does world space conversions of all input data received:

.. code:: cpp
        
    str.Temp( "position" );
    str.VertexTransform( "position", "state.matrix.program[1]", "vertex.attrib[0]" );
    str.Add( "MOV result.texcoord[0], position;\n" );

    // Output the WCS vertex normal:
    str.VectorTransform( "result.texcoord[1]", "state.matrix.program[1].invtrans", "vertex.attrib[2]" );


Nevertheless, it has to prepare the coordinate system for a custom shadow mapping operation:

.. code:: cpp
        
    // Calculate the position into the sunlight shadow map referential:
    str.VertexTransform( "result.texcoord[2]", 0, "position" );

This uses direct shadow mapping access, defined by ``RED::RenderShaderParameter::REF_SHADOW_MAP_TEX`` and ``RED::RenderShaderParameter::REF_SHADOW_MAP_FRUSTUM_MATX``.

*******************************
Custom CAD Shader Pixel Program
*******************************

The first part of the program is dedicated to retrieving all our parameters. We need our world space position, normal, reflection vector and material parameters to do all our calculations:

.. code:: cpp

    // Accessing our geometry parameters:
    str.Temp( "position" );
    str.Temp( "normal" );
    str.Add( "MOV position, fragment.texcoord[0];\n" );
    str.Normalize( "normal", "fragment.texcoord[1]" );

    // Accessing our object shading parameters:
    str.Param( "object_diffuse_color", 0 );
    str.Param( "object_specular_color", 1 );
    str.Param( "object_shininess", 2 );
    str.Param( "object_reflection_color", 3 );
    str.Param( "object_reflection_ior", 4 );

    // Calculating our eye vector:
    str.Param( "eye_position", 5 );
    str.Temp( "eye_dir" );
    str.Add( "ADD eye_dir, eye_position, -position;\n" );
    str.Normalize( "eye_dir", "eye_dir" );


Then, the shader is just a collection of all effects assembled together. We compute the Fresnel reflectance term:

.. code:: cpp

    // Calculate Fresnel reflectance 'r' term:
    // Compute first r0 = ( ( n1 - n2 ) / ( n1 + n2 ) ) ^ 2, based on Schlick's formula.
    // In a direct shader, our initial IOR is the air ( 1.0 ).
    str.Temp( "r0" );
    str.Add( "ADD r0.x, {1}.x, -object_reflection_ior.w;\n" );
    str.Add( "ADD r0.y, {1}.x, object_reflection_ior.w;\n" );
    str.Add( "RCP r0.y, r0.y;\n" );
    str.Add( "MUL r0, r0.x, r0.y;\n" );
    str.Add( "MUL r0, r0, r0;\n" );

    // Total internal reflections: sin( n2 ) = ( ( 1.0 - dotne ^ 2 ) ^ 0.5 ) * n1 / n2
    // If sin( n2 ) > 1.0, force the reflectance to 1.0.
    str.Temp( "sin2" );
    str.Temp( "dotne" );
    str.Add( "DP3_SAT dotne, eye_dir, normal;\n" );
    str.Add( "MAD sin2.x, dotne.x, -dotne.x, { 1 }.x;\n" );
    str.Add( "RSQ sin2.x, sin2.x;\n" );
    str.Add( "RCP sin2.x, sin2.x;\n" );
    str.Add( "ADD sin2.y, object_reflection_ior.w, { 1e-6 }.x;\n" );
    str.Add( "RCP sin2.y, sin2.y;\n" );
    str.Add( "MUL sin2.x, sin2.x, sin2.y;\n" );
    str.Add( "SGE sin2.x, sin2.x, { 1 }.x;\n" );
    str.Add( "ADD_SAT r0, r0, sin2.x;\n" );

    // Turn dotne into pow( ( 1.0 - dotne ), 5 );
    str.Temp( "powne" );
    str.Add( "ADD powne, { 1 }.x, -dotne;\n" );
    str.Add( "MUL powne.x, powne.x, powne.x;\n" );
    str.Add( "MUL powne.x, powne.x, powne.x;\n" );
    str.Add( "MUL powne, powne.x, powne.y;\n" );

    // r = r0 + ( 1 - r0 ) * ( 1.0 - dotne ) ^ 5 is our reflectance value.
    str.Temp( "r" );
    str.Add( "ADD r, { 1 }.x, -r0.x;\n" );
    str.Add( "MAD r, r, powne, r0;\n" );


Then, apply energy preservation. We make sure that our total reflectance + diffusion does not exceed 1.0. The transparency is not added into this model, but could be taken into consideration too. It's just a visual choice we made here.

.. code:: cpp
        
    // Apply energy preservation:
    str.Temp( "diffuse_color" );
    str.Temp( "specular_color" );
    str.Temp( "reflection_color" );
    str.Temp( "max_diffuse_color" );
    str.Add( "MUL specular_color, object_specular_color, r;\n" );
    str.Add( "MUL reflection_color, object_reflection_color, r;\n" );
    str.Add( "ADD_SAT max_diffuse_color, { 1 }.x, -reflection_color;\n" );
    str.Add( "MIN diffuse_color, object_diffuse_color, max_diffuse_color;\n" );

Then, we calculate the sun lighting and shadows to which we apply a gaussian blur filter to smooth them:

.. code:: cpp 

    // Sunlight diffuse contribution:
    str.Param( "sun_dir", 6 );
    str.Temp( "dotnl" );
    str.Add( "DP3_SAT dotnl, sun_dir, normal;\n" );

    // Sunlight specular contribution:
    str.Temp( "dotsr" );
    str.Temp( "ref_dir" );
    str.Add( "MUL ref_dir, dotne, normal;\n" );
    str.Add( "MAD ref_dir, { 2 }.x, ref_dir, -eye_dir;\n" );
    str.Add( "DP3_SAT dotsr, sun_dir, ref_dir;\n" );
    str.Add( "POW dotsr.x, dotsr.x, object_shininess.x;\n" );

    // Cumulated sun lighting:
    str.Temp( "sun_color" );
    str.Add( "MUL sun_color, diffuse_color, dotnl;\n" );
    str.Add( "MAD sun_color, specular_color, dotsr.x, sun_color;\n" );

    // Get sun shadows:
    str.Temp( "shadows" );
    str.ShadowMapGaussianBlur( "shadows", SHADOW_MAP_BLUR, "fragment.texcoord[2]", "texture[0]", iresmgr->GetPlatform() );
    str.Add( "MUL sun_color, sun_color, shadows.x;\n" );


Then, we manage environmental lighting thanks to our reflection direction calculated earlier:

.. code:: cpp
        
    // Environmental lighting contribution:
    str.Temp( "env_color" );
    str.Add( "TEX env_color, ref_dir, texture[1], CUBE;\n" );
    str.Add( "RCP env_color.w, env_color.w;\n" );
    str.Add( "MUL env_color, env_color, env_color.w;\n" );
    str.Add( "MUL env_color, env_color, reflection_color;\n" );


And get the skylight contribution, that modulates all our previous contributions:

.. code:: cpp
        
    // Skylight contribution:
    str.Temp( "ao" );
    str.Temp( "ao_color" );
    str.Add( "MOV ao, fragment.texcoord[3];\n" );
    str.SkylightDiffuseLighting( "ao_color", "normal", NULL, "texture[2]" );
    str.Add( "MUL ao_color, ao_color, ao.x;\n" );
    str.Add( "MUL ao_color, ao_color, diffuse_color;\n" );
    str.Add( "MUL env_color, env_color, ao.x;\n" );


To finally assemble our output color:

.. code:: cpp
        
    // Sum all our color contributions:
    str.Temp( "color" );
    str.Add( "ADD color, sun_color, env_color;\n" );
    str.Add( "ADD color, color, ao_color;\n" );
    str.Add( "MOV result.color, color;\n" );
    str.Add( "MOV result.color.w, { 1 }.x;\n" );