//GGX area light approximation from Horizon Zero Dawn
float GetNoHSquared(float radiusTan,float NoL,float NoV,float VoL)
{
    float radiusCos=1./sqrt(1.+radiusTan*radiusTan);
    
    float RoL=2.*NoL*NoV-VoL;
    if(RoL>=radiusCos)
    return 1.;
    
    float rOverLengthT=radiusCos*radiusTan/sqrt(1.-RoL*RoL);
    float NoTr=rOverLengthT*(NoV-RoL*NoL);
    float VoTr=rOverLengthT*(2.*NoV*NoV-1.-RoL*VoL);
    
    float triple=sqrt(clamp(1.-NoL*NoL-NoV*NoV-VoL*VoL+2.*NoL*NoV*VoL,0.,1.));
    
    float NoBr=rOverLengthT*triple,VoBr=rOverLengthT*(2.*triple*NoV);
    float NoLVTr=NoL*radiusCos+NoV+NoTr,VoLVTr=VoL*radiusCos+1.+VoTr;
    float p=NoBr*VoLVTr,q=NoLVTr*VoLVTr,s=VoBr*NoLVTr;
    float xNum=q*(-.5*p+.25*VoBr*NoLVTr);
    float xDenom=p*p+s*((s-2.*p))+NoLVTr*((NoL*radiusCos+NoV)*VoLVTr*VoLVTr+
    q*(-.5*(VoLVTr+VoL*radiusCos)-.5));
    float twoX1=2.*xNum/(xDenom*xDenom+xNum*xNum);
    float sinTheta=twoX1*xDenom;
    float cosTheta=1.-twoX1*xNum;
    NoTr=cosTheta*NoTr+sinTheta*NoBr;
    VoTr=cosTheta*VoTr+sinTheta*VoBr;
    
    float newNoL=NoL*radiusCos+NoTr;
    float newVoL=VoL*radiusCos+VoTr;
    float NoH=NoV+newNoL;
    float HoH=2.*newVoL+2.;
    return max(NoH*NoH/HoH,0.);
}

float GGX(vec3 normal,vec3 viewPos,vec3 lightVec,float smoothness,float f0,float sunSize){
    float roughness=1.-smoothness;
    if(roughness<.05)roughness=.05;
    roughness*=roughness;roughness*=roughness;
    
    vec3 halfVec=normalize(lightVec-viewPos);
    
    float dotLH=clamp(dot(halfVec,lightVec),0.,1.);
    float dotNL=clamp(dot(normal,lightVec),0.,1.);
    float dotNH=GetNoHSquared(sunSize,dotNL,dot(normal,-viewPos),dot(-viewPos,lightVec));
    
    float denom=dotNH*roughness-dotNH+1.;
    float D=roughness/(3.141592653589793*denom*denom);
    float F=exp2((-5.55473*dotLH-6.98316)*dotLH)*(1.-f0)+f0;
    float k2=roughness*.25;
    
    float specular=max(dotNL*dotNL*D*F/(dotLH*dotLH*(1.-k2)+k2),0.);
    specular=max(specular,0.)*(1.-roughness*(1.-.025*f0));
    specular=specular/(.125*specular+1.);
    
    return specular;
}

vec3 GetMetalCol(float f0){
    int metalidx=int(f0*255.);
    
    if(metalidx==230)return vec3(.24867,.22965,.21366);
    if(metalidx==231)return vec3(.88140,.57256,.11450);
    if(metalidx==232)return vec3(.81715,.82021,.83177);
    if(metalidx==233)return vec3(.27446,.27330,.27357);
    if(metalidx==234)return vec3(.84430,.48677,.22164);
    if(metalidx==235)return vec3(.36501,.35675,.37653);
    if(metalidx==236)return vec3(.42648,.37772,.31138);
    if(metalidx==237)return vec3(.91830,.89219,.83662);
    return vec3(1.);
}

vec3 GetSpecularHighlight(float smoothness,float metalness,float f0,vec3 specularColor,
vec3 rawAlbedo,vec3 shadow,vec3 normal,vec3 viewPos){
    if(dot(shadow,shadow)<.001)return vec3(0.);
    
    #ifdef END
    smoothness*=.4;
    #endif
    
    float specular=GGX(normal,normalize(viewPos),lightVec,smoothness,f0,
.025*sunVisibility+.05);
specular*=(1.-sqrt(rainStrength))*shadowFade;

specularColor=pow(specularColor,vec3(1.-.5*metalness));
#if MATERIAL_FORMAT==0
if(metalness>.5){
    if(f0<1.)specularColor*=GetMetalCol(f0);
    else specularColor*=rawAlbedo;
}
#else
specularColor*=pow(rawAlbedo,vec3(metalness));
#endif

return specular*specularColor*shadow;
}