layout( std140 ) uniform model {
	mat4 M;
};

layout( std140 ) uniform view {
	mat4 V;
	mat4 P;
	vec3 camera_pos;
};

layout( std140 ) uniform light_view {
	mat4 lightV;
	mat4 lightP;
};

layout( std140 ) uniform v_cold {
	vec3 light_pos;
};

uniform sampler2D shadowmap;
uniform sampler2D dither_noise;

struct VSOut {
	vec4 light_space_position;
	vec3 position;
	vec3 normal;
	vec3 colour;
};

#ifdef VERTEX_SHADER

in vec3 position;
in vec3 normal;
in vec3 colour;

out VSOut v2f;

void main() {
	vec4 world_space = M * vec4( position, 1.0 );
	gl_Position = P * V * world_space;
	v2f.light_space_position = lightP * lightV * world_space;
	v2f.position = world_space.xyz;
	v2f.normal = mat3( M ) * normal;
	v2f.colour = colour;
}

#else

in VSOut v2f;
out vec4 screen_colour;

// http://the-witness.net/news/2013/09/shadow-mapping-summary-part-1/
vec2 get_shadow_offsets( vec3 N, vec3 L ) {
	float cos_alpha = dot_sat( N, L );
	float offset_scale_N = sqrt( 1.0 - cos_alpha * cos_alpha );
	float offset_scale_L = offset_scale_N / cos_alpha;
	return vec2( offset_scale_N, min( 5.0, offset_scale_L ) );
}

void main() {
	vec3 light_colour = vec3( 400.0, 400.0, 400.0 );
	vec3 lightdir = normalize( light_pos - v2f.position );
	vec3 to_camera = normalize( camera_pos - v2f.position );

	vec3 light_ndc = v2f.light_space_position.xyz / v2f.light_space_position.w;
	vec3 light_norm = light_ndc * 0.5 + 0.5;
	vec2 offsets = get_shadow_offsets( v2f.normal, lightdir );
	float bias = 0.00002 + 0.00001 * offsets.x + 0.00005 * offsets.y;

	float shadow = 0;

	if( light_norm.z > 1.0 ) {
		shadow = 1.0;
	}
	else {
		vec2 inv_shadowmap_size = 1.0 / textureSize( shadowmap, 0 );
		for( int x = -1; x <= 1; x++ ) {
			for( int y = -1; y <= 1; y++ ) {
				vec2 offset = vec2( x, y ) * inv_shadowmap_size;
				float shadow_depth = texture( shadowmap, light_norm.xy + offset ).r;
				if( light_norm.z - bias <= shadow_depth ) {
					shadow += 1.0 / 9.0;
				}
			}
		}
	}

	vec3 noise = texture( dither_noise, gl_FragCoord.xy / textureSize( dither_noise, 0 ) ).xxx;
	noise = ( noise - vec3( 0.5 ) ) / 128.0;

	vec3 ambient = vec3( 0.03 ) * v2f.colour;
	vec3 Lo = cook_torrance_brdf( to_camera, v2f.position, v2f.normal, light_pos, light_colour, v2f.colour, 0.75, 0.0 );

	screen_colour = vec4( reinhard_tonemap( ambient + Lo * shadow + noise ), 1.0 );
}

#endif
