#define saturate(x) clamp(x, 0.0, 1.0) #define PI 3.14159265359 // OrenNayar diffuse vec3 getDiffuse( vec3 diffuseColor, float roughness4, float NoV, float NoL, float VoH ) { float VoL = 2.0 * VoH - 1.0; float c1 = 1.0 - 0.5 * roughness4 / (roughness4 + 0.33); float cosri = VoL - NoV * NoL; float c2 = 0.45 * roughness4 / (roughness4 + 0.09) * cosri * ( cosri >= 0.0 ? min( 1.0, NoL / NoV ) : NoL ); return diffuseColor / PI * ( NoL * c1 + c2 ); } // GGX Normal distribution float getNormalDistribution( float roughness4, float NoH ) { float d = ( NoH * roughness4 - NoH ) * NoH + 1.0; return roughness4 / ( d*d ); } // Smith GGX geometric shadowing from "Physically-Based Shading at Disney" float getGeometricShadowing( float roughness4, float NoV, float NoL, float VoH, vec3 L, vec3 V ) { float gSmithV = NoV + sqrt( NoV * (NoV - NoV * roughness4) + roughness4 ); float gSmithL = NoL + sqrt( NoL * (NoL - NoL * roughness4) + roughness4 ); return 1.0 / ( gSmithV * gSmithL ); } // Fresnel term vec3 getFresnel( vec3 specularColor, float VoH ) { vec3 specularColorSqrt = sqrt( clamp( vec3(0.0, 0.0, 0.0), vec3(0.99, 0.99, 0.99), specularColor ) ); vec3 n = ( 1.0 + specularColorSqrt ) / ( 1.0 - specularColorSqrt ); vec3 g = sqrt( n * n + VoH * VoH - 1.0 ); return 0.5 * pow( (g - VoH) / (g + VoH), vec3(2.0) ) * ( 1.0 + pow( ((g+VoH)*VoH - 1.0) / ((g-VoH)*VoH + 1.0), vec3(2.0) ) ); } const float A = 0.15; const float B = 0.50; const float C = 0.10; const float D = 0.20; const float E = 0.02; const float F = 0.30; vec3 Uncharted2Tonemap( vec3 x ) { return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F; } // From "I'm doing it wrong" // http://imdoingitwrong.wordpress.com/2011/01/31/light-attenuation/ float getAttenuation( vec3 lightPosition, vec3 vertexPosition, float lightRadius ) { float r = lightRadius; vec3 L = lightPosition - vertexPosition; float dist = length(L); float d = max( dist - r, 0.0 ); L /= dist; float denom = d / r + 1.0; float attenuation = 1.0 / (denom*denom); float cutoff = 0.0052; attenuation = (attenuation - cutoff) / (1.0 - cutoff); attenuation = max(attenuation, 0.0); return attenuation; } vec3 getPbrPointLight(vec3 normal, vec3 position, vec3 lightPosition, vec3 lightColor, float lightRadius) { // vLightPosition = ( uViewMatrix * vec4( uLightPosition, 1.0 ) ).xyz; vec3 N = normalize( normal ); vec3 L = normalize( lightPosition - position ); vec3 V = normalize( -position ); vec3 H = normalize(V + L); float NoL = saturate( dot( N, L ) ); float NoV = saturate( dot( N, V ) ); float VoH = saturate( dot( V, H ) ); float NoH = saturate( dot( N, H ) ); // deduce the diffuse and specular color from the baseColor and how metallic the material is vec3 diffuseColor = uBaseColor - uBaseColor * uMetallic; vec3 specularColor = mix( vec3( 0.8 * uSpecular ), uBaseColor, uMetallic ); // compute the brdf terms float distribution = getNormalDistribution( uRoughness, NoH ); vec3 fresnel = getFresnel( specularColor, VoH ); float geom = getGeometricShadowing( uRoughness, NoV, NoL, VoH, L, V ); // get the specular and diffuse and combine them vec3 diffuse = getDiffuse( diffuseColor, uRoughness, NoV, NoL, VoH ); vec3 specular = NoL * ( distribution * fresnel * geom ); vec3 color = lightColor * ( diffuse + specular ); // get the light attenuation from its radius float attenuation = getAttenuation( lightPosition, position, lightRadius ); color *= attenuation; return color; }