//////////////////////////////////////////////////
BLINN-PHONG LIGHTING MODEL
	Ambient = ambientMaterial * ambientLight 

	lamberFactor = max (dot (lightVec, normal), 0.0) 
	Diffuse = diffuseMaterial * diffuseLight * lamberFactor


	speculatCoef = pow (max (dot (halfVec, normal), 0.0), shininess)
	Specular = specularMaterial * specularLight * speculatCoef

	pixelColor = Ambient + (Diffuse + Specular) * Shadow


//////////////////////////////////////////////////
RIM LIGHT
	float cosTheta = abs( dot( normalize(vEye), N) );
	float fresnel = pow(1.0 - cosTheta, 4.0);

	...........................
	http://roxlu.com/2014/037/opengl-rim-shader
	uniform mat4 u_pm;
	uniform mat4 u_vm;
	 
	layout( location = 0 ) out vec4 fragcolor;
	 
	in vec3 v_norm;
	in vec4 v_pos; 
	 
	void main() {
	 
	  vec3 n = normalize(mat3(u_vm) * v_norm);      // convert normal to view space, u_vm (view matrix), is a rigid body transform.
	  vec3 p = vec3(u_vm * v_pos);                   // position in view space
	  vec3 v = normalize(-p);                       // eye vector
	  float vdn = 1.0 - max(dot(v, n), 0.0);        // the rim contribution
	 
	  fragcolor.a = 1.0;
	  fragcolor.rgb = vec3(smoothstep(0.6, 1.0, vdn));
	}

	...........................
	vec3 V = normalize( eye_position - world_pos);
	float rim = 1 - max(dot(V, world_normal), 0.0);
	rim = smoothstep(0.6, 1.0, rim);
	vec3 finalRim = RimColor * vec3(rim, rim, rim);


//////////////////////////////////////////////////
FOG
	vec3 vVertex = (gl_ModelViewMatrix * gl_Vertex).xyz;

	gl_FogFragCoord = length(vVertex);
	fogFactor = exp2(-gl_Fog.density *
					 gl_Fog.density  *
					 gl_FogFragCoord *
					 gl_FogFragCoord *
					 LOG2);
	fogFactor = clamp(fogFactor, 0.0, 1.0);


//////////////////////////////////////////////////
Instead of doing Matrix math like matProj * matView * matModel * vec3,
breaking it out per line using only vec will generate less ALU instructions, thus increasing performance a bit.
vec4 v = matModel * a_v;
v = matView * v
gl_position = matProj * v;
	http://www.geeks3d.com/20111028/how-to-compute-the-position-in-a-glsl-vertex-shader-opengl-matrix-gpu-shaderanalyzer-part-2/



RANDOM NUMBER GENERATOR FOR GLSL
//https://www.shadertoy.com/view/4dtBDX
//https://www.csee.umbc.edu/~olano/papers/GPUTEA.pdf
uvec2 encrypt(uvec2 v)
{
    uint k[4], sum = 0U, delta = 0x9e3779b9U;
    
    k[0] = 0xA341316CU;
    k[1] = 0xC8013EA4U;
    k[2] = 0xAD90777DU;
    k[3] = 0x7E95761EU;
    
    for(uint i = 0U; i < 4U; ++i)
    {
        sum += delta;
        v.x += ((v.y << 4) + k[0]) ^ (v.y + sum) ^ ((v.y >> 5) + k[1]);
        v.y += ((v.x << 4) + k[2]) ^ (v.x + sum) ^ ((v.x >> 5) + k[3]);
    }

    return v;
}


//////////////////////////////////////////////////
// optimized version (avoids dual quaternion - matrix conversion):
outputs dqsFast(inputs IN,
			uniform float4x4 modelViewProj,
			uniform float4x4 modelViewIT,
			uniform float2x4 boneDQ[100])
{
	outputs OUT;			
		
	float2x4 blendDQ = IN.weights.x*boneDQ[IN.matrixIndices.x];
	blendDQ += IN.weights.y*boneDQ[IN.matrixIndices.y];
	blendDQ += IN.weights.z*boneDQ[IN.matrixIndices.z];
	blendDQ += IN.weights.w*boneDQ[IN.matrixIndices.w];	
		
	float len = length(blendDQ[0]);
	blendDQ /= len;

	float3 position = IN.position.xyz + 2.0*cross(blendDQ[0].yzw, cross(blendDQ[0].yzw, IN.position.xyz) + blendDQ[0].x*IN.position.xyz);
	float3 trans = 2.0*(blendDQ[0].x*blendDQ[1].yzw - blendDQ[1].x*blendDQ[0].yzw + cross(blendDQ[0].yzw, blendDQ[1].yzw));
	position += trans;

	float3 inpNormal = IN.normal.xyz;
	float3 normal = inpNormal + 2.0*cross(blendDQ[0].yzw, cross(blendDQ[0].yzw, inpNormal) + blendDQ[0].x*inpNormal);

	OUT.hPosition = mul(modelViewProj, float4(position, 1.0));
	OUT.hNormal = mul(modelViewIT, float4(normal, 0.0));
		
	return OUT;
}