uniform sampler2D sSpecularColor;
#endif
+// For Light (Currently Directional Only)
+#define MAX_LIGHTS 5
+uniform mediump int uLightCount;
+uniform mediump vec3 uLightDirection[MAX_LIGHTS];
+uniform mediump vec3 uLightColor[MAX_LIGHTS];
+
+// For Shadow Map
+uniform lowp int uIsShadowEnabled;
+uniform sampler2D sShadowMap;
+in highp vec3 positionFromLightView;
+
//// For IBL
uniform sampler2D sbrdfLUT;
uniform samplerCube sDiffuseEnvSampler;
uniform samplerCube sSpecularEnvSampler;
uniform float uIblIntensity;
-uniform vec3 uYDirection;
+uniform mediump vec3 uYDirection;
uniform float uMaxLOD;
// For Alpha Mode.
// TODO: Multiple texture coordinate will be supported.
in mediump vec2 vUV;
in lowp mat3 vTBN;
-#ifdef COLOR_ATTRIBUTE
in lowp vec4 vColor;
-#endif
in highp vec3 vPositionToCamera;
out vec4 FragColor;
const float c_MinRoughness = 0.04;
+const float M_PI = 3.141592653589793;
+
+// These properties can be used for circular sampling for PCF
+
+// Percentage Closer Filtering to mitigate the banding artifacts.
+const int kPcfSampleCount = 9;
+
+const float kPi = 3.141592653589f;
+const float kInvSampleCount = 1.0 / float(kPcfSampleCount);
+const float kPcfTheta = 2.f * kPi * kInvSampleCount;
+const float kSinPcfTheta = sin(kPcfTheta);
+const float kCosPcfTheta = cos(kPcfTheta);
+
+uniform lowp int uEnableShadowSoftFiltering;
+uniform mediump float uShadowIntensity;
+uniform mediump float uShadowBias;
vec3 linear(vec3 color)
{
lowp float metallic = uMetallicFactor;
lowp float perceptualRoughness = uRoughnessFactor;
// If there isn't normal texture, use surface normal
- mediump vec3 n = normalize(vTBN[2].xyz);
+ highp vec3 n = normalize(vTBN[2].xyz);
#ifdef THREE_TEX
// The albedo may be defined from a base texture or a flat color
#ifdef BASECOLOR_TEX
lowp vec4 baseColor = texture(sAlbedoAlpha, vUV);
- baseColor = vec4(linear(baseColor.rgb), baseColor.w) * uColorFactor;
+ baseColor = vColor * vec4(linear(baseColor.rgb), baseColor.w) * uColorFactor;
#else // BASECOLOR_TEX
-#ifdef COLOR_ATTRIBUTE
lowp vec4 baseColor = vColor * uColorFactor;
-#else // COLOR_ATTRIBUTE
- lowp vec4 baseColor = uColorFactor;
-#endif // COLOR_ATTRIBUTE
#endif // BASECOLOR_TEX
#ifdef METALLIC_ROUGHNESS_TEX
#endif // NORMAL_TEX
#else // THREE_TEX
vec4 albedoMetal = texture(sAlbedoMetal, vUV);
-#ifdef COLOR_ATTRIBUTE
lowp vec4 baseColor = vec4(linear(albedoMetal.rgb), 1.0) * vColor * uColorFactor;
-#else // COLOR_ATTRIBUTE
- lowp vec4 baseColor = vec4(linear(albedoMetal.rgb), 1.0) * uColorFactor;
-#endif // COLOR_ATTRIBUTE
metallic = albedoMetal.METALLIC * metallic;
mediump vec3 v = normalize(vPositionToCamera); // Vector from surface point to camera
mediump float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
mediump vec3 reflection = -normalize(reflect(v, n));
- lowp vec3 brdf = linear(texture(sbrdfLUT, vec2(NdotV, 1.0 - perceptualRoughness)).rgb);
+ lowp vec3 brdf = texture(sbrdfLUT, vec2(NdotV, 1.0 - perceptualRoughness)).rgb;
vec3 Fr = max(vec3(1.0 - perceptualRoughness), f0) - f0;
vec3 k_S = f0 + Fr * pow(1.0 - NdotV, 5.0);
vec3 FssEss = specularWeight * (k_S * brdf.x + brdf.y);
lowp vec3 color = (diffuse + specular) * uIblIntensity;
+ // Punctual Light
+ if(uLightCount > 0)
+ {
+ // Compute reflectance.
+ lowp float reflectance = max(max(f0.r, f0.g), f0.b);
+ lowp float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
+ lowp float r = perceptualRoughness * perceptualRoughness;
+ lowp float attenuationV = 2.0 * NdotV / (NdotV + sqrt(r * r + (1.0 - r * r) * (NdotV * NdotV)));
+ mediump float roughnessSq = r * r;
+ lowp 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
+ mediump vec3 h = normalize(l+v); // Half vector between both l and v
+ mediump float VdotH = dot(v, h);
+ lowp vec3 specularReflection = f0 + (reflectance90 - f0) * pow(clamp(1.0 - VdotH, 0.0, 1.0), 5.0);
+
+ mediump float NdotL = clamp(dot(n, l), 0.001, 1.0);
+ lowp float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r * r + (1.0 - r * r) * (NdotL * NdotL)));
+ lowp float geometricOcclusion = attenuationL * attenuationV;
+
+ highp float NdotH = dot(n, h);
+ highp float f = (NdotH * roughnessSq - NdotH) * NdotH + 1.0;
+ lowp float microfacetDistribution = roughnessSq / (M_PI * f * f);;
+
+ // Calculation of analytical lighting contribution
+ lowp vec3 diffuseContrib = ( 1.0 - specularReflection ) * ( diffuseColorPunctual / M_PI );
+ lowp 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(uIsShadowEnabled) * uShadowIntensity > 0.0)
+ {
+ mediump float exposureFactor = 0.0;
+ if(uEnableShadowSoftFiltering > 0)
+ {
+ ivec2 texSize = textureSize(sShadowMap, 0);
+ mediump vec2 texelSize = vec2(1.0) / vec2(texSize.x, texSize.y);
+ mediump vec2 pcfSample = vec2(1.f, 0.f);
+ 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;
+ }
+ else
+ {
+ mediump float depthValue = texture(sShadowMap, positionFromLightView.xy).r;
+ exposureFactor = (depthValue < positionFromLightView.z - uShadowBias) ? 0.0 : 1.0;
+ }
+ color *= (1.0 - (1.0 - exposureFactor) * uShadowIntensity);
+ }
+
#ifdef OCCLUSION
lowp float ao = texture(sOcclusion, vUV).r;
color = mix(color, color * ao, uOcclusionStrength);