struct deferredMaterialData { vec3 normal; vec3 baseColor; vec3 diffuse; vec3 position; vec3 vertex; vec3 specularReflectance; float reflectance; float metallic; float roughness; float clearCoatRoughness; float geometricRoughness; float ambientOcclusion; float shadowOcclusion; float eta; float refracted_NoV; float index; float alpha; }; float D_GGX(float NoH, float a) { float a2 = a * a; float f = (NoH * a2 - NoH) * NoH + 1.0; return a2 / (PI * f * f); } float D_GGX_Anisotropy(float NoH, vec3 h, vec3 x, vec3 y, float ax, float ay) { float XoH = dot(x, h); float YoH = dot(y, h); float d = XoH * XoH * (ax * ax) + YoH * YoH * (ay * ay) + NoH * NoH; return (ax * ay) / (PI * d * d); } vec3 F_Schlick(float VoH, vec3 specularReflectance, float f90) { return specularReflectance + (vec3(f90) - specularReflectance) * pow(1.0 - VoH, 5.0); } float V_SmithGGXCorrelated(float NoV, float NoL, float a) { float a2 = a * a; float GGXL = NoV * sqrt((-NoL * a2 + NoL) * NoL + a2); float GGXV = NoL * sqrt((-NoV * a2 + NoV) * NoV + a2); return 0.5 / (GGXV + GGXL); } float Fd_Lambert() { return 1.0 / PI; } float F_Schlick_Scalar(float VoH, float specularReflectance, float f90) { return specularReflectance + (f90 - specularReflectance) * pow(1.0 - VoH, 5.0); } float square(float v) { return v * v; } vec3 irradianceSH(vec3 n) { return vec3(0.754553530212464, 0.748541695286661, 0.790922541330174) + vec3(-0.083855089181764, 0.092536341322488, 0.322767327275582) * (n.y) + vec3(0.308154551673257, 0.366799355358085, 0.466705760819624) * (n.z) + vec3(-0.188887618191928, -0.277403749518126, -0.377844811540716) * (n.x) + vec3(-0.252782448589491, -0.316051613736677, -0.396141020484574) * (n.y * n.x) + vec3(0.071362454444021, 0.159789075773366, 0.29059362717571) * (n.y * n.z) + vec3(-0.031040420617065, -0.031141089772695, -0.031044001883204) * (3.0 * n.z * n.z - 1.0) + vec3(-0.161001896026477, -0.203649521035777, -0.246641086569566) * (n.z * n.x) + vec3(0.045710934605387, 0.048121779682969, 0.046326375668417) * (n.x * n.x - n.y * n.y); } vec2 prefilteredDFGKaris(float NoV, float roughness) { // see https://www.unrealengine.com/blog/physically-based-shading-on-mobile const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022); const vec4 c1 = vec4( 1.0, 0.0425, 1.040, -0.040); vec4 r = roughness * c0 + c1; float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y; return vec2(-1.04, 1.04) * a004 + r.zw; } float sRGBtoLinear(float c) { return (c <= 0.04045) ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4); } vec3 sRGBtoLinear(vec3 c) { return vec3(sRGBtoLinear(c.r), sRGBtoLinear(c.g), sRGBtoLinear(c.b)); } vec3 decodeEnvironmentMap(vec4 c) { return c.rgb;//;sRGBtoLinear(c.rgb) ; } float getSquareFalloffAttenuation(float distanceSquare) { float factor = distanceSquare * lightGeometry.x; float smoothFactor = max(1.0 - factor * factor, 0.0); return smoothFactor * smoothFactor; } float getPhotometricAttenuation(vec3 lightToPos, vec3 lightDir) { return 1.0; } float getAngleAttenuation(vec3 l, vec3 lightDir) { float cd = dot(lightDir, l); float attenuation = clamp(cd * lightGeometry.y + lightGeometry.z, 0.0, 1.0); return attenuation * attenuation; } vec3 beerLambert(float NoV, float NoL, vec3 alpha, float d) { return exp(alpha * -(d * ((NoL + NoV) / max(NoL * NoV, 1e-3)))); } vec3 fixCubemapLookup(vec3 v, float lod) { vec3 r = abs(v); float M = max(max(v.x, v.y), v.z); float scale = 1.0 - exp2(lod) * (1.0 / 256.0); if (v.x != M) v.x *= scale; if (v.y != M) v.y *= scale; if (v.z != M) v.z *= scale; return v; } vec3 evaluateSpecularIBL(vec3 r, float roughness) { float lod = 5.0 * roughness; r = fixCubemapLookup(r, lod); return decodeEnvironmentMap( texture(reflectionSampler, r, lod) ); //, lod * 3. } float computeSpecularambientOcclusion(float NoV, float ambientOcclusion, float roughness) { return clamp(pow(NoV + ambientOcclusion, exp2(-16.0 * roughness - 1.0)) - 1.0 + ambientOcclusion, 0.0, 1.0); } vec3 getSpecularDominantDirection(vec3 n, vec3 r, float roughness) { float s = 1.0 - roughness; return mix(n, r, s * (sqrt(s) + roughness)); } vec3 evaluateIBL( deferredMaterialData materialData ) { //, deferredLightData params vec3 normal = materialData.normal; vec3 vertex = materialData.vertex; float roughness = materialData.roughness; float NoV = max( dot(normal, vertex), 0.0 ); #if ANISOTROPY == 1 vec3 t = normalize( materialData.tangent ); vec3 b = normalize( cross(t, normal)); vec3 anisotropicTangent = cross(-vertex, b); vec3 anisotropicNormal = cross(anisotropicTangent, b); vec3 bentNormal = normalize(mix(normal, anisotropicNormal, anisotropy)); vec3 r = reflect( vertex, bentNormal ); #else vec3 r = reflect( -vertex, normal ); r = getSpecularDominantDirection(normal, r, roughness * roughness); #endif float NoR = max( dot(r, normal), 0.0); // specular indirect vec3 indirectSpecular = evaluateSpecularIBL( reflect(vertex, normal), roughness); // horizon occlusion, can be removed for performance //float horizon = min(1.0 + NoR, 1.0); //indirectSpecular *= horizon * horizon; vec2 env = prefilteredDFGKaris( NoV, materialData.roughness ); // we should multiply env.y by f90 for more accurate results vec3 specularColor = materialData.specularReflectance * env.x + env.y * (1.0 - clearCoat) * clamp(dot(materialData.specularReflectance, vec3(50.0 * 0.33)), 0.0, 1.0); // diffuse indirect vec3 indirectDiffuse = max(irradianceSH(normal), 0.0) * Fd_Lambert(); // ambient occlusion float ambientOcclusionFade = clamp(dot(normalize(normal), vertex), 0.0, 1.0); float ambientOcclusion = mix(1.0, materialData.ambientOcclusion, ambientOcclusionFade); indirectDiffuse *= ambientOcclusion; //indirectDiffuse *= 4.0; // TODO: Not really useful without SSambientOcclusion/HBambientOcclusion/etc. indirectSpecular *= computeSpecularambientOcclusion(NoV, ambientOcclusion, materialData.roughness); // clear coat float Fcc = F_Schlick_Scalar(NoV, 0.04, 1.0) * 0.2; #if ANISOTROPY == 1 // We used the bent normal for the base layer r = reflect(-vertex, normal); #endif vec3 indirectClearCoatSpecular = evaluateSpecularIBL(reflect(vertex, normal), materialData.clearCoatRoughness); vec3 clearCoatAbsorption = mix(vec3(1.0), beerLambert(materialData.refracted_NoV, materialData.refracted_NoV, clearCoatColor, clearCoatThickness), clearCoat); // indirect contribution vec3 color = (materialData.diffuse * indirectDiffuse + indirectSpecular * specularColor)//kaj materialData.diffuse * indirectDiffuse * (1.0 - Fcc) * clearCoatAbsorption + indirectClearCoatSpecular * Fcc; #if TRANSLUCENT_MATERIAL == 1 indirectDiffuse = max(irradianceSH(-vertex), 0.0) * Fd_Lambert(); vec3 tL = -vertex + normal * translucencyDistortion; float tD = pow(clamp(dot(vertex, -tL), 0.0, 1.0), translucencyPower) * translucencyScale; vec3 tT = (tD + translucencyAmbient) * texture(translucencyThicknessMap, outUV).r; color.rgb += materialData.diffuse * indirectDiffuse * tT; #endif return color; } vec3 getNormal(vec3 n) { return normalize(n); } vec3 evaluateLight( deferredMaterialData params ) { vec3 n = params.normal; vec3 v = params.vertex; vec3 l; float NoL; float energy; float attenuation; vec3 lightDir = normalize(lightDirection); float linearRoughness = params.roughness * params.roughness; vec3 r = reflect(-v, n); if (lightType == 1.0) { l = -lightDir; // Disc area light vec3 sunDir = -lightDir; float e = sin(radians(0.53)); float d = cos(radians(0.53)); float DoR = dot(sunDir, r); vec3 s = r - DoR * sunDir; l = DoR < d ? normalize(d * sunDir + normalize(s) * e) : r; NoL = dot(n, l); energy = 1.0; attenuation = 1.0; } else if (lightType == 0.0) { vec3 posToLight = lightPosition - params.position; float distanceSquare = dot(posToLight, posToLight); l = normalize(posToLight); NoL = dot(n, l); energy = 50.0; attenuation = getSquareFalloffAttenuation(distanceSquare); attenuation *= 1.0 / max(distanceSquare, 1e-4); attenuation *= getPhotometricAttenuation(-l, lightDir); // if (lightGeometry.w >= 1.0) { // attenuation *= getAngleAttenuation(l, -lightDir); // } } vec3 h = normalize(v + l); NoL = clamp(NoL, 0.0, 1.0); float NoV = abs(dot(n, v)) + 1e-5; float NoH = clamp(dot(n, h), 0.0, 1.0); float LoH = clamp(dot(l, h), 0.0, 1.0); // specular BRDF #if ANISOTROPY == 1 vec3 t = normalize(v_tangent.xyz); vec3 b = normalize(cross(t, n)); float aspect = inversesqrt(1.0 - anisotropy * 0.9); float ax = 1.0 / (linearRoughness * aspect); float ay = aspect / linearRoughness; float D = D_GGX_Anisotropy(NoH, h, t, b, ax, ay); #else float D = D_GGX(NoH, linearRoughness); #endif vec3 F = F_Schlick(LoH, params.specularReflectance, clamp(dot(params.specularReflectance, vec3(50.0 * 0.33)), 0.0, 1.0)); float V = V_SmithGGXCorrelated(NoV, NoL, linearRoughness); vec3 Fr = (D * V) * F; // diffuse BRDF vec3 Fd = params.diffuse * Fd_Lambert();// * 3.6 // clear coat float linearClearCoatRoughness = params.clearCoatRoughness * params.clearCoatRoughness; float Dcc = D_GGX(NoH, linearClearCoatRoughness); float Fcc = F_Schlick_Scalar(LoH, 0.04, 1.0) * clearCoat; float Vcc = V_SmithGGXCorrelated(NoV, NoL, linearClearCoatRoughness); float FrCC = Dcc * Vcc * Fcc; vec3 refracted_l = -refract(l, n, params.eta); float refracted_NoL = clamp(dot(n, refracted_l), 0.0, 1.0); vec3 clearCoatAbsorption = mix(vec3(1.0), beerLambert(params.refracted_NoV, refracted_NoL, clearCoatColor, clearCoatThickness), clearCoat); // direct contribution vec3 color = (attenuation * NoL) * lightColor * lightIntensity * energy * ((Fd + Fr) * (1.0 - Fcc) * clearCoatAbsorption + FrCC); #if TRANSLUCENT_MATERIAL == 1 vec3 tL = l + n * translucencyDistortion; float tD = exp2(clamp(dot(v, -tL), 0.0, 1.0) * translucencyPower - translucencyPower) * translucencyScale; vec3 tT = attenuation * lightIntensity * (tD + translucencyAmbient) * texture(translucencyThicknessMap, outUV).r; color.rgb += Fd * lightColor * tT; #endif // micro-shadowing float aperture = 2.0 * params.ambientOcclusion * params.ambientOcclusion * params.shadowOcclusion; float microShadow = clamp(abs(dot(l, n)) + aperture - 1.0, 0.0, 1.0); color.rgb *= microShadow; return color; } vec4 physically_based_shading( deferredMaterialData materialData ) { //deferredLightData lightData; materialData.diffuse = (1.0 - metallic) * materialData.baseColor; // Geometric AA vec3 ndFdx = dFdx( materialData.normal ); vec3 ndFdy = dFdy( materialData.normal ); materialData.geometricRoughness = pow( max( dot(ndFdx, ndFdx), dot(ndFdy, ndFdy) ), 0.333); materialData.roughness = max(materialData.roughness, materialData.geometricRoughness); materialData.specularReflectance = 0.16 * materialData.reflectance * materialData.reflectance * (1.0 - metallic) + materialData.baseColor * materialData.metallic; materialData.clearCoatRoughness = max( mix(0.089, 0.6, clearCoatRoughness), materialData.geometricRoughness); materialData.eta = 1.0 / clearCoatIOR; materialData.vertex = normalize( cameraPosition - materialData.position ); // v vec3 refracted_vertex = -refract(materialData.vertex, materialData.normal, materialData.eta); materialData.refracted_NoV = clamp(dot(materialData.normal, refracted_vertex), 0.0, 1.0); float alpha = 1.0; // indirect lighting vec3 color = evaluateIBL( materialData ); color *= environmentLuminance; color += evaluateLight(materialData); return vec4(color, alpha); }