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
);
}