//FILM GRAIN

float4 TESR_FilmGrain_Width;
float4 TESR_FilmGrain_Height;
float4 TESR_FilmGrain_GrainPower;
float4 TESR_FilmGrain_GrainColored;
float4 TESR_FilmGrain_GrainColorAmount;
float4 TESR_FilmGrain_GrainSize;
float4 TESR_FilmGrain_GrainLuma;

float4 TESR_GameTime;

sampler2D TESR_RenderedBuffer : register(s0) = sampler_state {
	AddressU  = CLAMP;
	AddressV  = CLAMP;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = NONE;
};

struct VSOUT {
	float4 vertPos : POSITION;
	float2 UVCoord : TEXCOORD0;
};

struct VSIN {
	float4 vertPos : POSITION0;
	float2 UVCoord : TEXCOORD0;
};

VSOUT FrameVS(VSIN IN) {
	VSOUT OUT = (VSOUT)0.0f;
	OUT.vertPos = IN.vertPos;
	OUT.UVCoord = IN.UVCoord;
	return (OUT);
}

float4 rnm( in float2 tc ) {
	float noise  = sin( dot( float3( tc.x, tc.y, TESR_GameTime.x ), float3( 12.9898, 78.233, 0.0025216 ))) * 43758.5453;
    float noiseR = frac( noise ) * 2.0 - 1.0;
    float noiseG = frac( noise * 1.2154 ) * 2.0 - 1.0; 
    float noiseB = frac( noise * 1.3453 ) * 2.0 - 1.0;
    float noiseA = frac( noise * 1.3647 ) * 2.0 - 1.0;
    
    return float4( noiseR, noiseG, noiseB, noiseA );
}

float fade( in float t ) {

    return t * t * t * ( t * ( t * 6.0 - 15.0 ) + 10.0 );
}

float pnoise3D( in float3 p ) {
	static const float permTexUnit = 1.0 / 256.0;
	static const float permTexUnitHalf = 0.5 / 256.0;
    float3 pi = permTexUnit * floor( p ) + permTexUnitHalf;
    float3 pf = frac( p );
    float perm00 = rnm( pi.xy ).a;
    float3 grad000 = rnm( float2( perm00, pi.z )).rgb * 4.0 - 1.0;
    float n000 = dot( grad000, pf );
    float3 grad001 = rnm( float2( perm00, pi.z + permTexUnit )).rgb * 4.0 - 1.0;
    float n001 = dot( grad001, pf - float3( 0.0, 0.0, 1.0 ));
    float perm01 = rnm( pi.xy + float2( 0.0, permTexUnit )).a;
    float3 grad010 = rnm( float2( perm01, pi.z )).rgb * 4.0 - 1.0;
    float n010 = dot( grad010, pf - float3( 0.0, 1.0, 0.0 ));
    float3 grad011 = rnm( float2( perm01, pi.z + permTexUnit )).rgb * 4.0 - 1.0;
    float n011 = dot( grad011, pf - float3( 0.0, 1.0, 1.0 ));
    float perm10 = rnm( pi.xy + float2( permTexUnit, 0.0 )).a;
    float3 grad100 = rnm( float2( perm10, pi.z )).rgb * 4.0 - 1.0;
    float n100 = dot( grad100, pf - float3( 1.0, 0.0, 0.0 ));
    float3 grad101 = rnm( float2( perm10, pi.z + permTexUnit )).rgb * 4.0 - 1.0;
    float n101 = dot( grad101, pf - float3( 1.0, 0.0, 1.0 ));
    float perm11 = rnm( pi.xy + float2( permTexUnit, permTexUnit )).a;
    float3 grad110 = rnm( float2( perm11, pi.z )).rgb * 4.0 - 1.0;
    float n110 = dot( grad110, pf - float3( 1.0, 1.0, 0.0 ));
    float3 grad111 = rnm( float2( perm11, pi.z + permTexUnit )).rgb * 4.0 - 1.0;
    float n111 = dot( grad111, pf - float3( 1.0, 1.0, 1.0 ));
    float4 n_x = lerp( float4( n000, n001, n010, n011 ), float4( n100, n101, n110, n111 ), fade( pf.x ));
    float2 n_xy = lerp( n_x.xy, n_x.zw, fade( pf.y ));
    float n_xyz = lerp( n_xy.x, n_xy.y, fade( pf.z ));

    return n_xyz;
}

float2 coordRot( in float2 tc, in float angle ) {
    float aspectr = TESR_FilmGrain_Width.r / TESR_FilmGrain_Height.r;
    float rotX = (( tc.x * 2.0 - 1.0 ) * aspectr * cos( angle )) - (( tc.y * 2.0 - 1.0 ) * sin( angle ));
    float rotY = (( tc.y * 2.0 - 1.0) * cos( angle )) + (( tc.x * 2.0 - 1.0) * aspectr * sin( angle ));
    rotX = (( rotX / aspectr ) * 0.5 + 0.5 );
    rotY = rotY * 0.5 + 0.5;
	
    return float2( rotX, rotY );
}

float4 GrainPass( VSOUT IN, float2 texcoord : TEXCOORD0 ) : COLOR {
	int colored = TESR_FilmGrain_GrainColored.r;
    float3 rotOffset = float3( 1.425, 3.892, 5.835 );
    float2 rotCoordsR = coordRot( texcoord, TESR_GameTime.x + rotOffset.x );
    float2 rot = rotCoordsR * float2( TESR_FilmGrain_Width.r / TESR_FilmGrain_GrainSize.r, TESR_FilmGrain_Height.r / TESR_FilmGrain_GrainSize.r);
    float pNoise = pnoise3D( float3( rot.x, rot.y, 0.0 ));
    float3 noise = float3( pNoise, pNoise, pNoise );
    if ( colored == 1 ) {
        float2 rotCoordsG = coordRot( texcoord, TESR_GameTime.y + rotOffset.y );
        float2 rotCoordsB = coordRot( texcoord, TESR_GameTime.z + rotOffset.z );
        noise.g = lerp( noise.r, pnoise3D( float3( rotCoordsG * float2( TESR_FilmGrain_Width.r / TESR_FilmGrain_GrainSize.r, TESR_FilmGrain_Height.r / TESR_FilmGrain_GrainSize.r ), 1.0 )), TESR_FilmGrain_GrainColorAmount.r );
        noise.b = lerp( noise.r, pnoise3D( float3( rotCoordsB * float2( TESR_FilmGrain_Width.r / TESR_FilmGrain_GrainSize.r, TESR_FilmGrain_Height.r / TESR_FilmGrain_GrainSize.r ), 2.0 )), TESR_FilmGrain_GrainColorAmount.r );
    }
    float3 col = tex2D( TESR_RenderedBuffer, texcoord ).rgb;
    float3 lumcoeff = float3( 0.299, 0.587, 0.114 );
    float luminance = lerp( 0.0, dot( col, lumcoeff ), TESR_FilmGrain_GrainLuma.r );
    float lum = smoothstep( 0.2, 0.0, luminance );
    lum += luminance;
    float2 thepow = pow( lum, 4.0 );
    noise = lerp( noise, float3( 0.0, 0.0, 0.0 ), pow( lum, 4.0 ));
    col += noise * TESR_FilmGrain_GrainPower.r;
   
    return float4( col, 1.0 );
}

technique {
	pass {
		VertexShader = compile vs_3_0 FrameVS();
		PixelShader = compile ps_3_0 GrainPass();
	}
}