+ // Diffuse Light
+ lowp vec3 diffuseColor = mix(baseColor.rgb, vec3(0), metallic);
+#ifdef SL_VERSION_LOW
+ lowp vec3 irradiance = linear(textureCube(sDiffuseEnvSampler, n * uYDirection).rgb);
+#else
+ lowp vec3 irradiance = linear(TEXTURE(sDiffuseEnvSampler, n * uYDirection).rgb);
+#endif
+ float Ems = (1.0 - (brdf.x + brdf.y));
+ vec3 F_avg = specularWeight * (f0 + (1.0 - f0) / 21.0);
+ vec3 FmsEms = Ems * FssEss * F_avg / (1.0 - F_avg * Ems);
+ vec3 k_D = diffuseColor * (1.0 - FssEss + FmsEms);
+ lowp vec3 diffuse = (FmsEms + k_D) * irradiance;
+
+ lowp vec3 color = (diffuse + specular) * uIblIntensity;
+
+ // Punctual Light
+ if(uLightCount > 0)
+ {
+ // Compute reflectance.
+ highp float reflectance = max(max(f0.r, f0.g), f0.b);
+ highp float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
+ highp float r = perceptualRoughness * perceptualRoughness;
+ highp float attenuationV = 2.0 * NdotV / (NdotV + sqrt(r * r + (1.0 - r * r) * (NdotV * NdotV)));
+ highp float roughnessSq = r * r;
+ highp vec3 diffuseColorPunctual = baseColor.rgb * (vec3(1.0) - f0);
+ diffuseColorPunctual *= ( 1.0 - metallic );
+
+ for(int i = 0; i < uLightCount; ++i)
+ {
+ highp vec3 l = normalize(-uLightDirection[i]); // Vector from surface point to light
+ highp vec3 h = normalize(l+v); // Half vector between both l and v
+ highp float VdotH = dot(v, h);
+ highp vec3 specularReflection = f0 + (reflectance90 - f0) * pow(clamp(1.0 - VdotH, 0.0, 1.0), 5.0);
+
+ highp float NdotL = clamp(dot(n, l), 0.001, 1.0);
+ highp float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r * r + (1.0 - r * r) * (NdotL * NdotL)));
+ highp float geometricOcclusion = attenuationL * attenuationV;
+
+ highp float NdotH = dot(n, h);
+ highp float f = (NdotH * roughnessSq - NdotH) * NdotH + 1.0;
+ highp float microfacetDistribution = roughnessSq / (M_PI * f * f);;
+
+ // Calculation of analytical lighting contribution
+ highp vec3 diffuseContrib = ( 1.0 - specularReflection ) * ( diffuseColorPunctual / M_PI );
+ highp vec3 specContrib = specularReflection * geometricOcclusion * microfacetDistribution / ( 4.0 * NdotL * NdotV );
+
+ // Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law)
+ color += NdotL * uLightColor[i] * (diffuseContrib + specContrib);
+ }
+ }
+
+ if(float(uIsShadowReceiving) * float(uIsShadowEnabled) * uShadowIntensity > 0.0)
+ {
+ mediump float exposureFactor = 0.0;
+
+ highp vec3 l = normalize(-uLightDirection[uShadowLightIndex]);
+ highp float NdotL = dot(n, l);
+ if(uEnableShadowSoftFiltering > 0)
+ {
+#ifdef SL_VERSION_LOW
+ ivec2 texSize = ivec2(uShadowMapWidth, uShadowMapHeight);
+#else
+ ivec2 texSize = textureSize(sShadowMap, 0);
+#endif
+ mediump vec2 texelSize = vec2(1.0) / vec2(texSize.x, texSize.y);
+ mediump vec2 pcfSample = vec2(1.0, 0.0);
+ for (int i = 0; i < kPcfSampleCount; ++i)
+ {
+ pcfSample = vec2(kCosPcfTheta * pcfSample.x - kSinPcfTheta * pcfSample.y,
+ kSinPcfTheta * pcfSample.x + kCosPcfTheta * pcfSample.y);
+ lowp float depthValue = TEXTURE(sShadowMap, positionFromLightView.xy + pcfSample * texelSize).r;
+ exposureFactor += (depthValue < positionFromLightView.z - uShadowBias) ? 0.0 : 1.0;
+ }
+ exposureFactor *= kInvSampleCount;
+
+ // Blend filtered shadow and shadow from fragment normal to allow soft filtering nearby where the NdotL is zero.
+ highp float shadowFactor = clamp((NdotL + 0.5) * 2.0, 0.0f, 1.0);
+ exposureFactor = mix(0.0, exposureFactor, shadowFactor);
+ }
+ else
+ {
+ if(NdotL > 0.0)
+ {
+ mediump float depthValue = TEXTURE(sShadowMap, positionFromLightView.xy).r;
+ exposureFactor = (depthValue < positionFromLightView.z - uShadowBias) ? 0.0 : 1.0;
+ }
+ }
+
+ color *= (1.0 - (1.0 - exposureFactor) * uShadowIntensity);
+ }