BareGit
#version 300 es
// -*- mode: c; -*-

in vec4 a_position;
in vec3 a_normal;
in vec4 a_tangent;
in vec2 a_texcoord;

uniform mat4 u_projection;
uniform mat4 u_model_view;
uniform mat3 u_normal_matrix;

out vec2 v_texcoord;
out vec3 v_view_position;
out vec3 v_view_normal;
out vec3 v_view_tangent;
out vec3 v_view_bitangent;

// Normalize interpolant inputs without allowing malformed geometry to inject
// NaNs into every fragment covered by a primitive.
vec3 safeNormalize(vec3 value, vec3 fallback)
{
    float length_squared = dot(value, value);
    return length_squared < 0.00001
         ? fallback
         : value * inversesqrt(length_squared);
}

// Construct a fallback tangent perpendicular to the supplied normal.
vec3 orthogonalVector(vec3 normal)
{
    vec3 reference = abs(normal.z) < 0.999
                   ? vec3(0.0, 0.0, 1.0)
                   : vec3(0.0, 1.0, 0.0);
    return safeNormalize(cross(reference, normal), vec3(1.0, 0.0, 0.0));
}

void main()
{
    vec4 view_position = u_model_view * a_position;
    vec3 normal = safeNormalize(
        u_normal_matrix * a_normal, vec3(0.0, 0.0, 1.0));
    vec3 tangent = mat3(u_model_view) * a_tangent.xyz;

    // Re-orthogonalization suppresses interpolation and transform error before
    // constructing the UV-aligned bitangent used by the grating orientation.
    tangent = safeNormalize(
        tangent - normal * dot(normal, tangent), orthogonalVector(normal));

    gl_Position = u_projection * view_position;
    v_texcoord = a_texcoord;
    v_view_position = view_position.xyz;
    v_view_normal = normal;
    v_view_tangent = tangent;
    v_view_bitangent = a_tangent.w * safeNormalize(
        cross(normal, tangent), orthogonalVector(normal));
}