#ifndef ALLIN13DSHADER_BRDF #define ALLIN13DSHADER_BRDF /************/ #define MEDIUMP_FLT_MAX 65504.0 #define saturateMediump(x) min(x, MEDIUMP_FLT_MAX) #define MIN_ROUGHNESS 0.01 struct BDRFPerLightData { float3 H; float3 L; float3 lightColor; float distanceAttenuation; float3 shadowColor; float3 correctedLightColor; float rawNdotL; float correctedRawNdotL; float NdotL; float TdotL; float BdotL; float NdotH; float TdotH; float BdotH; float VdotH; float LdotH; float LdotH_2; float LdotV; float3 F; float3 kS; float3 kD; }; struct BDRFCommonData { float3 N; float3 T; float3 B; float3 V; float NdotV; float TdotV; float BdotV; float metallic; float smoothness; float roughness; float roughness_2; float cubeLod; float3 F0; float2 mainUV; float3 positionWS; float2 normalizedScreenSpaceUV; }; float D_GGX_Anisotropic(float NoH, const float3 h, const float3 t, const float3 b, float at, float ab) { //TODO: Pass TdotH and BdotH through parameters float ToH = dot(t, h); float BoH = dot(b, h); float a2 = at * ab; float3 v = float3(ab * ToH, at * BoH, a2 * NoH); float v2 = dot(v, v); float w2 = a2 / v2; return a2 * w2 * w2 * (1.0 / ALLIN13DSHADER_PI); } float V_SmithGGXCorrelated_Anisotropic(float at, float ab, float ToV, float BoV, float ToL, float BoL, float NoV, float NoL) { float lambdaV = NoL * length(float3(at * ToV, ab * BoV, NoV)); float lambdaL = NoV * length(float3(at * ToL, ab * BoL, NoL)); float v = 0.5 / (lambdaV + lambdaL); return saturateMediump(v); } /************/ float DistributionGGX(float a, float NdotH) { float a2 = max(a*a, MIN_ROUGHNESS * MIN_ROUGHNESS); float NdotH2 = NdotH*NdotH; float num = a2; float denom = (NdotH2 * (a2 - 1.0) + 1.0); denom = ALLIN13DSHADER_PI * denom * denom; return num / denom; } float GeometrySchlickGGX(float NdotV, float roughness) { float r = (roughness + 1.0); float k = (r*r) / 8.0; float num = NdotV; float denom = NdotV * (1.0 - k) + k; return num / denom; } float GeometrySmith(float NdotV, float NdotL, float roughness) { float ggx2 = GeometrySchlickGGX(NdotV, roughness); float ggx1 = GeometrySchlickGGX(NdotL, roughness); return ggx1 * ggx2; } float3 fresnelSchlick(float3 F0, float VdotH) { float OneMinusVdotH = 1 - VdotH; float OneMinusVdotH_5 = OneMinusVdotH * OneMinusVdotH * OneMinusVdotH * OneMinusVdotH * OneMinusVdotH; return F0 + (1.0 - F0) * OneMinusVdotH_5; } float3 fresnelSchlickRoughness(float cosTheta, float3 F0, float roughness) { float oneMinusRoughness = 1.0 - roughness; float oneMinusCosTheta = 1.0 - cosTheta; return F0 + (max(oneMinusRoughness, F0) - F0) * pow(clamp(oneMinusCosTheta, 0.0, 1.0), 5.0); } inline float3 FresnelLerp (float3 F0, float3 F90, float cosA) { float t = Pow_5 (1 - cosA); // ala Schlick interpoliation return lerp (F0, F90, t); } //Cook Torrance float3 SpecularTerm(float a, float roughness, float3 F, float NdotH, float NdotV, float NdotL, float VdotH) { float D = DistributionGGX(a, NdotH); float G = GeometrySmith(NdotV, NdotL, roughness); float3 numerator = 4.0 * D * G * F; float denominator = 4 * NdotV * NdotL; denominator = max(denominator, 0.0001); float3 res = numerator / denominator; return res; } float3 SpecularAnisoTerm( float at, float ab, float3 F, float NdotH, float NdotV, float NdotL, float TdotV, float BdotV, float TdotL, float BdotL, float TdotH, float BdotH, float3 H, float3 T, float3 B) { float D = D_GGX_Anisotropic(NdotH, H, T, B, at, ab); float V = V_SmithGGXCorrelated_Anisotropic( at, ab, TdotV, BdotV, TdotL, BdotL, NdotV, NdotL); float3 res = D * V * F; return res; } float3 SpecularIBL(float3 positionWS, float2 normalizedScreenSpaceUV, float3 normalWS, float3 viewDirWS, float cubeLod) { float3 res = 0; #ifdef REFLECTIONS_ON res = GetSkyColor(positionWS, normalizedScreenSpaceUV, normalWS, viewDirWS, cubeLod); #endif return res; } float3 DiffuseTerm(float LdotH, float LdotV, float NdotL, float roughness, float3 colorDiffuse) { float LdotH_2 = LdotH * LdotH; float f0 = 1.0; float f90 = 0.5 + 2*(roughness * LdotH_2); float3 fDiffuse = lerp(f0, f90, NdotL) * lerp(f0, f90, LdotV); float3 res = (colorDiffuse / ALLIN13DSHADER_PI) * fDiffuse; return res; } float3 DiffuseTerm02(float NdotV, float NdotL, float LdotH, float perceptualRoughness, float3 albedo) { float fd90 = 0.5 + 2 * LdotH * LdotH * perceptualRoughness; // Two schlick fresnel term float lightScatter = (1 + (fd90 - 1) * Pow_5(1 - NdotL)); float viewScatter = (1 + (fd90 - 1) * Pow_5(1 - NdotV)); float3 res = (albedo /*/ ALLIN13DSHADER_PI*/) * lightScatter * viewScatter; return res; } float3 IndirectLighting(float3 albedo, float3 specularColor, EffectsData effectsData, BDRFCommonData commonData, AllIn1GI gi) { float3 N = commonData.N; float3 V = commonData.V; //float3 ambientColor = GetAmbientColor(effectsData); float3 diffuse = gi.diffuse * albedo; float3 F = fresnelSchlickRoughness(commonData.NdotV, commonData.F0, commonData.roughness); float3 kS = F; float3 specular = 0; #ifdef REFLECTIONS_ON float3 specularIBL = SpecularIBL(commonData.positionWS, commonData.normalizedScreenSpaceUV, N, V, commonData.cubeLod); //float reflectionLuminance = GetLuminance(float4(specularIBL, 1.0)); //specularIBL = lerp(diffuse, specularIBL, smoothstep(0.0, 0.1, reflectionLuminance * (1 - commonData.roughness))); //TODO: Intermediate _Metallic values looks weird float F_factor = 1 - commonData.metallic; specular = specularIBL * lerp(1.0, F, F_factor); #else specular = lerp(diffuse * 0.25, diffuse, (1 - commonData.roughness)); #endif float3 kD = 1.0 - kS; kD *= 1.0 - commonData.metallic; float3 indirectDiffuse = kD * diffuse; //TODO: Some problems with specularColor float3 indirectSpecular = specular * specularColor; float3 res = indirectDiffuse + indirectSpecular; return res; } float3 DirectDiffuse_PBR(float3 albedo, BDRFCommonData commonData, BDRFPerLightData perLightData, AllIn1LightData lightData) { float3 diffuseTerm = DiffuseTerm02(commonData.NdotV, perLightData.NdotL, perLightData.LdotH, commonData.roughness, albedo); float correctedNdotL = saturate(perLightData.correctedRawNdotL); float3 directDiffuse = 0; #if defined(_LIGHTMODEL_CLASSIC) directDiffuse = diffuseTerm * perLightData.kD * perLightData.correctedLightColor * correctedNdotL; #elif defined(_LIGHTMODEL_HALFLAMBERT) float NdotL = saturate(perLightData.correctedRawNdotL); float halfLambertTerm = (NdotL * ACCESS_PROP_FLOAT(_HalfLambertWrap)) + (1 - ACCESS_PROP_FLOAT(_HalfLambertWrap)); float halfLambertTerm_2 = halfLambertTerm * halfLambertTerm; directDiffuse = diffuseTerm * halfLambertTerm_2 * perLightData.correctedLightColor; #elif defined(_LIGHTMODEL_FAKEGI) float fakeGI = (correctedNdotL * ACCESS_PROP_FLOAT(_HardnessFakeGI)) + 1.0 - ACCESS_PROP_FLOAT(_HardnessFakeGI); directDiffuse = diffuseTerm * fakeGI * perLightData.correctedLightColor; #elif defined(_LIGHTMODEL_TOON) directDiffuse = diffuseTerm * perLightData.kD * perLightData.correctedLightColor * correctedNdotL; float toonFactor = smoothstep(ACCESS_PROP_FLOAT(_ToonCutoff), ACCESS_PROP_FLOAT(_ToonCutoff) + ACCESS_PROP_FLOAT(_ToonSmoothness), correctedNdotL); directDiffuse *= toonFactor; #elif defined(_LIGHTMODEL_TOONRAMP) float3 rampLight = SAMPLE_TEX2D_LOD(_ToonRamp, float4((perLightData.correctedRawNdotL * 0.5) + 0.5, 0, 0, 0)).rgb; directDiffuse = diffuseTerm * perLightData.kD * perLightData.correctedLightColor * rampLight; //float diffuseTermLuminance = GetLuminance(directDiffuse); //directDiffuse = SAMPLE_TEX2D(_ToonRamp, float2(diffuseTermLuminance, diffuseTermLuminance)).rgb; #endif return directDiffuse; } #ifdef SPECULAR_ON float3 DirectSpecular_PBR(float3 specularColor, BDRFCommonData commonData, BDRFPerLightData perLightData, AllIn1LightData lightData, float4 specularMap) { float3 specularTerm = 0; //float4 specularMap = SAMPLE_TEX2D(_SpecularMap, commonData.mainUV); #if defined(_SPECULARMODEL_CLASSIC) specularTerm = SpecularTerm(commonData.roughness_2, commonData.roughness, perLightData.F, perLightData.NdotH, commonData.NdotV, perLightData.NdotL, perLightData.VdotH); #elif defined(_SPECULARMODEL_TOON) specularTerm = SpecularTerm(commonData.roughness_2, commonData.roughness, perLightData.F, perLightData.NdotH, commonData.NdotV, perLightData.NdotL, perLightData.VdotH); specularTerm = smoothstep(ACCESS_PROP_FLOAT(_SpecularToonCutoff), ACCESS_PROP_FLOAT(_SpecularToonCutoff) + ACCESS_PROP_FLOAT(_SpecularToonSmoothness), specularTerm); #elif defined(_SPECULARMODEL_ANISOTROPIC) || defined(_SPECULARMODEL_ANISOTROPICTOON) //https://google.github.io/filament/Filament.html?utm_source=chatgpt.com#materialsystem/anisotropicmodel float aniso = ACCESS_PROP_FLOAT(_Anisotropy); float at = max((1 - ACCESS_PROP_FLOAT(_AnisoShininess)) * (1.0 + aniso), 0.001); float ab = max((1 - ACCESS_PROP_FLOAT(_AnisoShininess)) * (1.0 - aniso), 0.001); specularTerm = SpecularAnisoTerm(at, ab, perLightData.F, perLightData.NdotH, commonData.NdotV, perLightData.NdotL, commonData.TdotV, commonData.BdotV, perLightData.TdotL, perLightData.BdotL, perLightData.TdotH, perLightData.BdotH, perLightData.H, commonData.T, commonData.B); specularTerm = saturate(specularTerm); #if defined(_SPECULARMODEL_ANISOTROPICTOON) float specularSmoothness = max(ACCESS_PROP_FLOAT(_SpecularToonSmoothness), 0.001); specularTerm = smoothstep(ACCESS_PROP_FLOAT(_SpecularToonCutoff), ACCESS_PROP_FLOAT(_SpecularToonCutoff) + specularSmoothness, specularTerm); #endif #endif specularTerm *= specularMap.r; float3 directSpecular = specularTerm * specularColor * ACCESS_PROP_FLOAT(_SpecularAtten) * perLightData.correctedLightColor * perLightData.NdotL; return directSpecular; } #endif float3 DirectLighting_PBR(float3 albedo, BDRFCommonData commonData, BDRFPerLightData perLightData, AllIn1LightData lightData, float4 specularMap) { float3 diffuseTerm = DirectDiffuse_PBR(albedo, commonData, perLightData, lightData); float3 specularColor = lerp(1.0, albedo, commonData.metallic); float3 specularTerm = 0; #ifdef SPECULAR_ON specularTerm = DirectSpecular_PBR(specularColor, commonData, perLightData, lightData, specularMap); #endif float3 directLight = diffuseTerm + specularTerm; return directLight; } BDRFCommonData CreateCommonBDRFData(float3 albedo, EffectsData effectsData) { BDRFCommonData res; res.N = effectsData.normalWS; res.T = effectsData.tangentWS; res.B = effectsData.bitangentWS; res.V = normalize(_WorldSpaceCameraPos.xyz - effectsData.vertexWS); res.metallic = effectsData.metallic; float smoothness = effectsData.smoothness; #ifdef _METALLIC_MAP_ON float4 metallicMapColor = SAMPLE_TEX2D(_MetallicMap, effectsData.mainUV); float metallicMapValue = metallicMapColor.r; res.metallic *= metallicMapValue; res.metallic = saturate(res.metallic); float glossMapValue = metallicMapColor.a; smoothness *= glossMapValue; smoothness = saturate(smoothness); #endif res.smoothness = lerp(0, 0.95, smoothness); res.roughness = (1 - res.smoothness); res.roughness_2 = res.roughness * res.roughness; res.cubeLod = res.roughness * 8; res.NdotV = max(dot(res.N, res.V), 0.0); res.TdotV = max(dot(res.T, res.V), 0.0); res.BdotV = max(dot(res.B, res.V), 0.0); float3 f0 = 0.04; f0 = lerp(f0, albedo, res.metallic); res.F0 = f0; res.mainUV = effectsData.mainUV; res.positionWS = effectsData.vertexWS; res.normalizedScreenSpaceUV = effectsData.normalizedScreenSpaceUV; return res; } BDRFPerLightData CreatePerLightData(BDRFCommonData commonData, AllIn1LightData lightData, float isAdditionalLight) { BDRFPerLightData res; float oneMinusMetallic = 1 - commonData.metallic; res.L = lightData.lightDir; res.lightColor = lightData.lightColor.rgb; res.distanceAttenuation = lightData.distanceAttenuation; res.shadowColor = lightData.shadowColor.rgb; res.correctedLightColor = lightData.lightColor.rgb * lightData.distanceAttenuation * lightData.shadowColor.rgb; res.H = normalize(commonData.V + res.L); res.NdotH = max(dot(commonData.N, res.H), 0.0); res.rawNdotL = dot(commonData.N, res.L); res.correctedRawNdotL = res.rawNdotL; #if !defined(FORWARD_ADD_PASS) #if defined(_LIGHTMODEL_HALFLAMBERT) || defined(_LIGHTMODEL_FAKEGI) || defined(_LIGHTMODEL_TOONRAMP) res.correctedLightColor = isAdditionalLight > 0 ? res.correctedLightColor : res.lightColor; res.correctedRawNdotL = isAdditionalLight > 0 ? res.correctedRawNdotL : res.correctedRawNdotL * GetLuminance(lightData.shadowColor.rgb); #endif #endif res.NdotL = max(res.rawNdotL, 0.0); res.TdotL = max(dot(commonData.T, res.L), 0.0); res.BdotL = max(dot(commonData.B, res.L), 0.0); res.VdotH = max(dot(commonData.V, res.H), 0.0); res.TdotH = max(dot(commonData.T, res.H), 0.0); res.BdotH = max(dot(commonData.B, res.H), 0.0); res.LdotV = max(dot(res.L, commonData.V), 0.0); res.LdotH = max(dot(res.L, res.H), 0.0); res.LdotH_2 = res.LdotH * res.LdotH; res.F = fresnelSchlick(commonData.F0, res.VdotH); res.kS = res.F; res.kD = (1.0 - res.kS) * oneMinusMetallic; return res; } float3 CalculateLighting_PBR(float3 albedo, float alpha, EffectsData effectsData, AllIn1GI gi) { BDRFCommonData commonData = CreateCommonBDRFData(albedo, effectsData); float3 specularColor = lerp(1.0, albedo, commonData.metallic); AllIn1LightData mainLightData = GetMainLightData(effectsData.vertexWS, effectsData, gi); float3 directLighting = albedo; float4 specularMap = float4(1, 1, 1, 1); #ifdef ALLIN1_USE_LIGHT_LAYERS uint meshRenderingLayers = AllIn1GetMeshRenderingLayer(); directLighting = 0; if (IsMatchingLightLayer(mainLightData.layerMask, meshRenderingLayers)) { directLighting = albedo; #endif BDRFPerLightData perLightData_mainLight = CreatePerLightData(commonData, mainLightData, 0.0); #ifdef SPECULAR_ON specularMap = SAMPLE_TEX2D(_SpecularMap, commonData.mainUV); #endif directLighting = DirectLighting_PBR(albedo, commonData, perLightData_mainLight, mainLightData, specularMap); #ifdef ALLIN1_USE_LIGHT_LAYERS } #endif #if defined(ADDITIONAL_LIGHT_LOOP) && !defined(_LIGHTMODEL_FASTLIGHTING) // Additional light loop for non-main directional lights. This block is specific to Forward+. #if USE_CLUSTER_LIGHT_LOOP UNITY_LOOP for (uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++) { AllIn1LightData additionalLightData = GetPointLightData(lightIndex, effectsData.vertexWS, effectsData.normalWS, effectsData, gi); #ifdef ALLIN1_USE_LIGHT_LAYERS if (IsMatchingLightLayer(additionalLightData.layerMask, meshRenderingLayers)) { #endif BDRFPerLightData perLightData_additionalLight = CreatePerLightData(commonData, additionalLightData, 1.0); directLighting += DirectLighting_PBR(albedo, commonData, perLightData_additionalLight, additionalLightData, specularMap); #ifdef ALLIN1_USE_LIGHT_LAYERS } #endif } #endif uint numAdditionalLights = NUM_ADDITIONAL_LIGHTS; LIGHT_LOOP_BEGIN_ALLIN13D(numAdditionalLights, effectsData) AllIn1LightData additionalLightData = GetPointLightData(lightIndex, effectsData.vertexWS, effectsData.normalWS, effectsData, gi); #ifdef ALLIN1_USE_LIGHT_LAYERS uint meshRenderingLayers = AllIn1GetMeshRenderingLayer(); if (IsMatchingLightLayer(additionalLightData.layerMask, meshRenderingLayers)) { #endif BDRFPerLightData perLightData_additionalLight = CreatePerLightData(commonData, additionalLightData, 1.0); directLighting += DirectLighting_PBR(albedo, commonData, perLightData_additionalLight, additionalLightData, specularMap); #ifdef ALLIN1_USE_LIGHT_LAYERS } #endif LIGHT_LOOP_END_ALLIN13D #endif float2 ssaoFactor = GetSSAO(effectsData.normalizedScreenSpaceUV.xy, alpha); float3 res = directLighting * ssaoFactor.x; //We add IndirectLighting only once #ifndef FORWARD_ADD_PASS #if defined(_CUSTOM_SHADOW_COLOR_ON) float shadowT = saturate(mainLightData.realtimeShadow + 1.0 - global_shadowColor.a); res = lerp(global_shadowColor, res, shadowT); #endif float3 ao = 1.0; #ifdef _AOMAP_ON ao = GetAOMapTerm(effectsData.mainUV); #endif float3 indirectLighting = IndirectLighting(albedo, specularColor, effectsData, commonData, gi); res += (indirectLighting) * ao * ssaoFactor.y; #endif return res; } #endif