Lib Normal - Shader API

lib-normal.glsl

Public Functions: normalBlend normalBlendOriented normalFade normalUnpack normalFromBaseNormal normalFromNormal normalFromHeight getTSNormal computeWSBaseNormal computeWSNormal

Import from library


import lib-defines.glsl

import lib-sparse.glsl

All engine parameters useful for normal-centric operations.


//: param auto channel_height

uniform SamplerSparse height_texture;

//: param auto channel_normal

uniform SamplerSparse normal_texture;

//: param auto texture_normal

uniform SamplerSparse base_normal_texture;

//: param auto normal_blending_mode

uniform int normal_blending_mode;

Used to invert the Y axis of the normal map


//: param auto normal_y_coeff

uniform float base_normal_y_coeff;

Empirically determined by our artists…


const float HEIGHT_FACTOR = 400.0;

Perform the blending between 2 normal maps

This is based on Whiteout blending http://blog.selfshadow.com/publications/blending-in-detail/


vec3 normalBlend(vec3 baseNormal, vec3 overNormal)

{

  return normalize(vec3(

    baseNormal.xy + overNormal.xy,

    baseNormal.z  * overNormal.z));

}

Perform a detail oriented blending between 2 normal maps

This is based on Detail Oriented blending http://blog.selfshadow.com/publications/blending-in-detail/


vec3 normalBlendOriented(vec3 baseNormal, vec3 overNormal)

{

  baseNormal.z += 1.0;

  overNormal.xy = -overNormal.xy;

  return normalize(baseNormal * dot(baseNormal,overNormal) -

    overNormal*baseNormal.z);

}

Returns a normal flattened by an attenuation factor


vec3 normalFade(vec3 normal,float attenuation)

{

  if (attenuation<1.0 && normal.z<1.0)

  {

    float phi = attenuation * acos(normal.z);

    normal.xy *= 1.0/sqrt(1.0-normal.z*normal.z) * sin(phi);

    normal.z = cos(phi);

  }



  return normal;

}

Unpack a normal w/ alpha channel


vec3 normalUnpack(vec4 normal_alpha, float y_coeff)

{

  if (normal_alpha.a == 0.0 || normal_alpha.xyz == vec3(0.0)) {

    return vec3(0.0, 0.0, 1.0);

  }



  // Attenuation in function of alpha

  vec3 normal = normal_alpha.xyz/normal_alpha.a * 2.0 - vec3(1.0);

  normal.y *= y_coeff;

  normal.z = max(1e-3, normal.z);

  normal = normalize(normal);

  normal = normalFade(normal, normal_alpha.a);



  return normal;

}

Unpack a normal w/ alpha channel, no Y invertion


vec3 normalUnpack(vec4 normal_alpha)

{

  return normalUnpack(normal_alpha, 1.0);

}

Compute the tangent space normal from document’s height channel


vec3 normalFromHeight(SparseCoord coord, float height_force)

{

  // Normal computation using height map



  // Determine gradient offset in function of derivatives

  vec2 dfd = max(coord.dfdx,coord.dfdy);

  dfd = max(dfd,height_texture.size.zw);



  vec2 dfdx,dfdy;

  textureSparseQueryGrad(dfdx, dfdy, height_texture, coord);

  float h_r  = textureGrad(height_texture.tex, coord.tex_coord+vec2( dfd.x,  0    ), dfdx, dfdy).r;

  float h_l  = textureGrad(height_texture.tex, coord.tex_coord+vec2(-dfd.x,  0    ), dfdx, dfdy).r;

  float h_t  = textureGrad(height_texture.tex, coord.tex_coord+vec2(     0,  dfd.y), dfdx, dfdy).r;

  float h_b  = textureGrad(height_texture.tex, coord.tex_coord+vec2(     0, -dfd.y), dfdx, dfdy).r;

  float h_rt = textureGrad(height_texture.tex, coord.tex_coord+vec2( dfd.x,  dfd.y), dfdx, dfdy).r;

  float h_lt = textureGrad(height_texture.tex, coord.tex_coord+vec2(-dfd.x,  dfd.y), dfdx, dfdy).r;

  float h_rb = textureGrad(height_texture.tex, coord.tex_coord+vec2( dfd.x, -dfd.y), dfdx, dfdy).r;

  float h_lb = textureGrad(height_texture.tex, coord.tex_coord+vec2(-dfd.x, -dfd.y), dfdx, dfdy).r;



  vec2 dh_dudv = (0.5 * height_force) / dfd * vec2(

    2.0*(h_l-h_r)+h_lt-h_rt+h_lb-h_rb,

    2.0*(h_b-h_t)+h_rb-h_rt+h_lb-h_lt);



  return normalize(vec3(dh_dudv, HEIGHT_FACTOR));

}

Helper to compute the tangent space normal from base normal and a height value, and an optional detail normal.


vec3 getTSNormal(SparseCoord coord, vec3 normalFromHeight)

{

  vec3 normal = normalBlendOriented(

    normalUnpack(textureSparse(base_normal_texture, coord), base_normal_y_coeff),

    normalFromHeight);



  if (normal_texture.is_set) {

    vec3 channelNormal = normalUnpack(textureSparse(normal_texture, coord));

    if (normal_blending_mode == BlendingMode_Replace) {

      normal = normalBlendOriented(normalFromHeight, channelNormal);

    } else if (normal_blending_mode == BlendingMode_NM_Combine) {

      normal = normalBlendOriented(normal, channelNormal);

    }

  }



  return normal;

}

Helper to compute the tangent space normal from base normal and height, and an optional detail normal.


vec3 getTSNormal(SparseCoord coord)

{

  float height_force = 1.0;

  vec3 normalH = normalFromHeight(coord, height_force);

  return getTSNormal(coord, normalH);

}

Helper to compute the world space normal from tangent space base normal.


vec3 computeWSBaseNormal(SparseCoord coord, vec3 tangent, vec3 bitangent, vec3 normal)

{

  vec3 normal_vec = normalUnpack(textureSparse(normal_texture, coord), base_normal_y_coeff);

  return normalize(

    normal_vec.x * tangent +

    normal_vec.y * bitangent +

    normal_vec.z * normal

  );

}

Helper to compute the world space normal from tangent space normal given by getTSNormal helpers, and local frame of the mesh.


vec3 computeWSNormal(SparseCoord coord, vec3 tangent, vec3 bitangent, vec3 normal)

{

  vec3 normal_vec = getTSNormal(coord);

  return normalize(

    normal_vec.x * tangent +

    normal_vec.y * bitangent +

    normal_vec.z * normal

  );

}
recommendation-more-help
4517c71e-0531-47f5-b14d-d3b9de4d0104