#

lib-normal.glsl

Public Functions: normalBlend normalBlendOriented normalFade normalUnpack normalFromBaseNormal normalFromNormal normalFromHeight

All engine parameters useful for normal-centric operations.

//: param auto channel_height
uniform sampler2D height_texture;
//: param auto channel_normal
uniform sampler2D normal_texture;
//: param auto channel_normal_is_set
uniform bool normal_texture_is_set;
//: param auto channel_height_size
uniform vec4 height_size; // width, height, width_inv, height_inv
//: param auto texture_normal
uniform sampler2D base_normal_texture;
#

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)
{
	// Attenuation in function of alpha
	vec3 normal = normal_alpha.a==0.0 ?
		vec3(0.0,0.0,1.0) :
		normal_alpha.xyz/normal_alpha.a * 2.0 - vec3(1.0);
	normal = normalize(normal);
	normal = normalFade(normal,normal_alpha.a);

	return normal;
}
#

Return the tangent space normal from mesh's base normal map

vec3 normalFromBaseNormal(vec2 tex_coord)
{
	// Base normal from normal imported for the mesh
	vec3 baseNormal = texture2D(base_normal_texture, tex_coord).xyz * 2.0 - vec3(1.0);
	baseNormal.y *= base_normal_y_coeff;
	return baseNormal;
}
#

Return the tangent space normal from document's normal map channel

vec3 normalFromNormal(vec2 tex_coord)
{
	// Normal from normal document's normal channel if present
	return normal_texture_is_set ?
		normalUnpack(texture2D(normal_texture, tex_coord)) :
		vec3(0.0,0.0,1.0);
}
#

Compute the tangent space normal from document's height channel

vec3 normalFromHeight(vec2 tex_coord, float height_force)
{
	vec2 dudv = height_size.zw * 0.25;

	// Normal computation using height map
	float h_right  = texture2D(height_texture, tex_coord + vec2(dudv.x, 0.0) ).r;
	float h_left   = texture2D(height_texture, tex_coord + vec2(-dudv.x, 0.0) ).r;
	float h_top    = texture2D(height_texture, tex_coord + vec2(0.0,  dudv.y) ).r;
	float h_bottom = texture2D(height_texture, tex_coord + vec2(0.0, -dudv.y) ).r;

	vec2 dh_dudv = vec2(
		h_left - h_right,
		h_bottom - h_top
		) / dudv;

	return normalize(vec3(
		dh_dudv * height_force,
		HEIGHT_FACTOR
		));
}