#ifndef UNIVERSAL_SKINLIGHTING_INCLUDED #define UNIVERSAL_SKINLIGHTING_INCLUDED TEXTURE2D(_SkinLUT); SAMPLER(sampler_SkinLUT); float4 _SkinLUT_TexelSize; half3 GlobalIllumination(BRDFData brdfData, half3 bakedGI, half occlusion, float3 positionWS, half3 normalWS, half3 viewDirectionWS, float2 normalizedScreenSpaceUV, half specOcclusion) { half3 reflectVector = reflect(-viewDirectionWS, normalWS); half fresnelTerm = Pow4(1.0 - saturate(dot(normalWS, viewDirectionWS))); half3 indirectDiffuse = bakedGI * occlusion; half3 indirectSpecular = GlossyEnvironmentReflection( reflectVector, positionWS, brdfData.perceptualRoughness, occlusion, normalizedScreenSpaceUV) * specOcclusion; half3 color = EnvironmentBRDF(brdfData, indirectDiffuse, indirectSpecular, fresnelTerm); // Debug if (IsOnlyAOLightingFeatureEnabled()) { color = occlusion.xxx; // "Base white" for AO debug lighting mode // Lux: We return occlusion here } return color; } inline half GammaToLinearSpace(half sRGB) { // Approximate version from http://chilliant.blogspot.com.au/2012/08/srgb-approximations-for-hlsl.html return sRGB * (sRGB * (sRGB * 0.305306011h + 0.682171111h) + 0.012522878h); } half3 LightingPhysicallyBasedSkin(BRDFData brdfData, half3 lightColor, half3 lightDirectionWS, half lightAttenuation, half3 normalWS, half3 viewDirectionWS, half NdotL, half NdotLUnclamped, half curvature, half skinMask) { //half3 radiance = lightColor * NdotL; half3 diffuseLighting = brdfData.diffuse * SAMPLE_TEXTURE2D_LOD(_SkinLUT, sampler_SkinLUT, float2( (NdotLUnclamped * 0.5 + 0.5), curvature), 0).rgb; diffuseLighting = lerp(brdfData.diffuse * NdotL, diffuseLighting, skinMask); // return ( DirectBDRF_Lux(brdfData, normalWS, lightDirectionWS, viewDirectionWS) * NdotL + diffuseLighting ) * lightColor * lightAttenuation; #ifndef _SPECULARHIGHLIGHTS_OFF half specularTerm = DirectBRDFSpecular(brdfData, normalWS, lightDirectionWS, viewDirectionWS); return ( specularTerm * brdfData.specular * NdotL + diffuseLighting ) * lightColor * lightAttenuation; #else return diffuseLighting * lightColor * lightAttenuation; #endif } half3 LightingPhysicallyBasedSkin(BRDFData brdfData, Light light, half3 normalWS, half3 viewDirectionWS, half NdotL, half NdotLUnclamped, half curvature, half skinMask) { return LightingPhysicallyBasedSkin(brdfData, light.color, light.direction, light.distanceAttenuation * light.shadowAttenuation, normalWS, viewDirectionWS, NdotL, NdotLUnclamped, curvature, skinMask); } half4 URPSkinFragmentPBR(InputData inputData, SurfaceData surfaceData, half4 translucency, half AmbientReflection, half3 subsurfaceColor, half curvature, half skinMask, half maskbyshadowstrength, half backScatter) { BRDFData brdfData; InitializeBRDFData(surfaceData, brdfData); #if defined(DEBUG_DISPLAY) half4 debugColor; if (CanDebugOverrideOutputColor(inputData, surfaceData, brdfData, debugColor)) { return debugColor; } #endif half4 shadowMask = CalculateShadowMask(inputData); AmbientOcclusionFactor aoFactor = CreateAmbientOcclusionFactor(inputData, surfaceData); uint meshRenderingLayers = GetMeshRenderingLayer(); Light mainLight = GetMainLight(inputData, shadowMask, aoFactor); // NOTE: We don't apply AO to the GI here because it's done in the lighting calculation below... MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI); //MixRealtimeAndBakedGI(mainLight, diffuseNormalWS, inputData.bakedGI); LightingData lightingData = CreateLightingData(inputData, surfaceData); lightingData.giColor = GlobalIllumination( brdfData, inputData.bakedGI, aoFactor.indirectAmbientOcclusion, inputData.positionWS, inputData.normalWS, inputData.viewDirectionWS, inputData.normalizedScreenSpaceUV, AmbientReflection ); // Backscattering #if defined(_BACKSCATTER) && !defined(DEBUG_DISPLAY) lightingData.giColor += backScatter * SampleSH(-inputData.normalWS) * surfaceData.albedo * aoFactor.indirectAmbientOcclusion * translucency.x * subsurfaceColor * skinMask; #endif #if defined(_LIGHT_LAYERS) if (IsMatchingLightLayer(mainLight.layerMask, meshRenderingLayers)) #endif { half3 mainLightColor = mainLight.color; half NdotLUnclamped = dot(inputData.normalWS, mainLight.direction); half NdotL = saturate( dot(inputData.normalWS, mainLight.direction) ); lightingData.mainLightColor = LightingPhysicallyBasedSkin(brdfData, mainLight, inputData.normalWS, inputData.viewDirectionWS, NdotL, NdotLUnclamped, curvature, skinMask); // Subsurface Scattering half transPower = translucency.y; half3 transLightDir = mainLight.direction + inputData.normalWS * translucency.w; half transDot = dot( transLightDir, -inputData.viewDirectionWS ); transDot = exp2(saturate(transDot) * transPower - transPower); lightingData.mainLightColor += skinMask * subsurfaceColor * transDot * (1.0 - saturate(NdotLUnclamped)) * mainLightColor * lerp(1.0, mainLight.shadowAttenuation, translucency.z) * translucency.x; } #if defined(_ADDITIONAL_LIGHTS) uint pixelLightCount = GetAdditionalLightsCount(); #if USE_FORWARD_PLUS for (uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++) { FORWARD_PLUS_SUBTRACTIVE_LIGHT_CHECK Light light = GetAdditionalLight(lightIndex, inputData, shadowMask, aoFactor); #ifdef _LIGHT_LAYERS if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers)) #endif { half3 lightColor = light.color; half NdotLUnclamped = dot(inputData.normalWS, light.direction); half NdotL = saturate( dot(inputData.normalWS, light.direction) ); lightingData.additionalLightsColor += LightingPhysicallyBasedSkin(brdfData, light, inputData.normalWS, inputData.viewDirectionWS, NdotL, NdotLUnclamped, curvature, skinMask); // Subsurface Scattering int index = lightIndex; half4 shadowParams = GetAdditionalLightShadowParams(index); #if !defined(ADDITIONAL_LIGHT_CALCULATE_SHADOWS) lightColor *= lerp(1, 0, maskbyshadowstrength); #else // half isPointLight = shadowParams.z; lightColor *= lerp(1, shadowParams.x, maskbyshadowstrength); #endif half transPower = translucency.y; half3 transLightDirA = light.direction + inputData.normalWS * translucency.w; half transDotA = dot( transLightDirA, -inputData.viewDirectionWS ); transDotA = exp2(saturate(transDotA) * transPower - transPower); lightingData.additionalLightsColor += skinMask * subsurfaceColor * transDotA * (1.0 - saturate(NdotLUnclamped)) * lightColor * lerp(1.0, light.shadowAttenuation, translucency.z) * light.distanceAttenuation * translucency.x; } } #endif LIGHT_LOOP_BEGIN(pixelLightCount) Light light = GetAdditionalLight(lightIndex, inputData, shadowMask, aoFactor); #if defined(_LIGHT_LAYERS) if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers)) #endif { half3 lightColor = light.color; half NdotLUnclamped = dot(inputData.normalWS, light.direction); half NdotL = saturate( dot(inputData.normalWS, light.direction) ); lightingData.additionalLightsColor += LightingPhysicallyBasedSkin(brdfData, light, inputData.normalWS, inputData.viewDirectionWS, NdotL, NdotLUnclamped, curvature, skinMask); // Subsurface Scattering int index = lightIndex; //int index = GetPerObjectLightIndex(lightIndex); half4 shadowParams = GetAdditionalLightShadowParams(index); #if !defined(ADDITIONAL_LIGHT_CALCULATE_SHADOWS) lightColor *= lerp(1, 0, maskbyshadowstrength); #else // half isPointLight = shadowParams.z; lightColor *= lerp(1, shadowParams.x, maskbyshadowstrength); #endif half transPower = translucency.y; half3 transLightDirA = light.direction + inputData.normalWS * translucency.w; half transDotA = dot( transLightDirA, -inputData.viewDirectionWS ); transDotA = exp2(saturate(transDotA) * transPower - transPower); lightingData.additionalLightsColor += skinMask * subsurfaceColor * transDotA * (1.0 - saturate(NdotLUnclamped)) * lightColor * lerp(1.0, light.shadowAttenuation, translucency.z) * light.distanceAttenuation * translucency.x; } LIGHT_LOOP_END #endif #ifdef _ADDITIONAL_LIGHTS_VERTEX lightingData.vertexLightingColor += inputData.vertexLighting * brdfData.diffuse; #endif return CalculateFinalColor(lightingData, surfaceData.alpha); } #endif #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/EntityLighting.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/ImageBasedLighting.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl" // Ref: https://knarkowicz.wordpress.com/2018/01/04/cloth-shading/ real D_CharlieNoPI_Lux(real NdotH, real roughness) { float invR = rcp(roughness); float cos2h = NdotH * NdotH; float sin2h = 1.0 - cos2h; // Note: We have sin^2 so multiply by 0.5 to cancel it return (2.0 + invR) * PositivePow(sin2h, invR * 0.5) / 2.0; } /* real D_Charlie_Lux(real NdotH, real roughness) { return INV_PI * D_CharlieNoPI_Lux(NdotH, roughness); } */ // We use V_Ashikhmin instead of V_Charlie in practice for game due to the cost of V_Charlie real V_Ashikhmin_Lux(real NdotL, real NdotV) { // Use soft visibility term introduce in: Crafting a Next-Gen Material Pipeline for The Order : 1886 return 1.0 / (4.0 * (NdotL + NdotV - NdotL * NdotV)); } // A diffuse term use with fabric done by tech artist - empirical real FabricLambertNoPI_Lux(real roughness) { return lerp(1.0, 0.5, roughness); } real FabricLambert_Lux(real roughness) { return INV_PI * FabricLambertNoPI_Lux(roughness); } // --------- struct AdditionalData { half3 tangentWS; half3 bitangentWS; float partLambdaV; half roughnessT; half roughnessB; half3 anisoReflectionNormal; }; half3 DirectBDRF_LuxCloth(BRDFData brdfData, AdditionalData addData, half3 normalWS, half3 lightDirectionWS, half3 viewDirectionWS, half NdotL) { #ifndef _SPECULARHIGHLIGHTS_OFF float3 lightDirectionWSFloat3 = float3(lightDirectionWS); float3 halfDir = SafeNormalize(lightDirectionWSFloat3 + float3(viewDirectionWS)); float NoH = saturate(dot(float3(normalWS), halfDir)); half LoH = half(saturate(dot(lightDirectionWSFloat3, halfDir))); half NdotV = saturate(dot(normalWS, viewDirectionWS )); float3 tangentWS = float3(addData.tangentWS); float3 bitangentWS = float3(addData.bitangentWS); float TdotH = dot(tangentWS, halfDir); float TdotL = dot(tangentWS, lightDirectionWSFloat3); float BdotH = dot(bitangentWS, halfDir); float BdotL = dot(bitangentWS, lightDirectionWSFloat3); half3 F = F_Schlick(brdfData.specular, LoH); // 1.91: was float3 //float TdotV = dot(tangentWS, viewDirectionWS); //float BdotV = dot(bitangentWS, viewDirectionWS); float DV = DV_SmithJointGGXAniso( TdotH, BdotH, NoH, NdotV, TdotL, BdotL, NdotL, addData.roughnessT, addData.roughnessB, addData.partLambdaV ); half3 specularLighting = F * DV; //return float4(normalWS, 1); return specularLighting + brdfData.diffuse; #else return brdfData.diffuse; #endif } half3 LightingPhysicallyBased_LuxCloth(BRDFData brdfData, AdditionalData addData, half3 lightColor, half3 lightDirectionWS, half lightAttenuation, half3 normalWS, half3 viewDirectionWS, half NdotL) { half3 radiance = lightColor * (lightAttenuation * NdotL); return DirectBDRF_LuxCloth(brdfData, addData, normalWS, lightDirectionWS, viewDirectionWS, NdotL) * radiance; } half3 LightingPhysicallyBased_LuxCloth(BRDFData brdfData, AdditionalData addData, Light light, half3 normalWS, half3 viewDirectionWS, half NdotL) { return LightingPhysicallyBased_LuxCloth(brdfData, addData, light.color, light.direction, light.distanceAttenuation * light.shadowAttenuation, normalWS, viewDirectionWS, NdotL); } // As we need both normals here - otherwise kept in sync with latest URP function half3 GlobalIllumination_LuxAniso(BRDFData brdfData, BRDFData brdfDataClearCoat, float clearCoatMask, half3 bakedGI, half occlusion, float3 positionWS, half3 anisoReflectionNormal, half3 normalWS, half3 viewDirectionWS, float2 normalizedScreenSpaceUV) { half3 reflectVector = reflect(-viewDirectionWS, anisoReflectionNormal); half NoV = saturate(dot(normalWS, viewDirectionWS)); half fresnelTerm = Pow4(1.0 - NoV); half3 indirectDiffuse = bakedGI; half3 indirectSpecular = GlossyEnvironmentReflection(reflectVector, positionWS, brdfData.perceptualRoughness, 1.0h, normalizedScreenSpaceUV); half3 color = EnvironmentBRDF(brdfData, indirectDiffuse, indirectSpecular, fresnelTerm); if (IsOnlyAOLightingFeatureEnabled()) { color = half3(1,1,1); // "Base white" for AO debug lighting mode } #if defined(_CLEARCOAT) || defined(_CLEARCOATMAP) half3 coatIndirectSpecular = GlossyEnvironmentReflection(reflectVector, positionWS, brdfDataClearCoat.perceptualRoughness, 1.0h, normalizedScreenSpaceUV); // TODO: "grazing term" causes problems on full roughness half3 coatColor = EnvironmentBRDFClearCoat(brdfDataClearCoat, clearCoatMask, coatIndirectSpecular, fresnelTerm); // Blend with base layer using khronos glTF recommended way using NoV // Smooth surface & "ambiguous" lighting // NOTE: fresnelTerm (above) is pow4 instead of pow5, but should be ok as blend weight. half coatFresnel = kDielectricSpec.x + kDielectricSpec.a * fresnelTerm; return (color * (1.0 - coatFresnel * clearCoatMask) + coatColor) * occlusion; #else return color * occlusion; #endif } half4 LuxURPClothFragmentPBR(InputData inputData, SurfaceData surfaceData, half3 tangentWS, half anisotropy, half4 translucency, half metallic) { BRDFData brdfData; InitializeBRDFData(surfaceData, brdfData); // Do not apply energy conservtion half oneMinusReflectivity = OneMinusReflectivityMetallic(surfaceData.metallic); half reflectivity = half(1.0) - oneMinusReflectivity; half3 brdfDiffuse = surfaceData.albedo * oneMinusReflectivity; half3 brdfSpecular = lerp(kDieletricSpec.rgb * surfaceData.specular, surfaceData.albedo * metallic, surfaceData.metallic); //half3 brdfSpecular = lerp(float3(1,0,0), float3(0,1,0), surfaceData.metallic); brdfData.diffuse = brdfDiffuse; brdfData.specular = brdfSpecular; // Debugging #if defined(DEBUG_DISPLAY) half4 debugColor; if (CanDebugOverrideOutputColor(inputData, surfaceData, brdfData, debugColor)) { return debugColor; } #endif AdditionalData addData; tangentWS = Orthonormalize(tangentWS, inputData.normalWS); addData.tangentWS = tangentWS; addData.bitangentWS = cross(inputData.normalWS, tangentWS); // We do not apply ClampRoughnessForAnalyticalLights here addData.roughnessT = brdfData.roughness * (1 + anisotropy); addData.roughnessB = brdfData.roughness * (1 - anisotropy); float TdotV = dot(addData.tangentWS, inputData.viewDirectionWS); float BdotV = dot(addData.bitangentWS, inputData.viewDirectionWS); float NdotV = dot(inputData.normalWS, inputData.viewDirectionWS); // partLambdaV should be 0.0f in case of cotton wool addData.partLambdaV = GetSmithJointGGXAnisoPartLambdaV(TdotV, BdotV, NdotV, addData.roughnessT, addData.roughnessB); half3 grainDirWS = (anisotropy >= 0.0) ? addData.bitangentWS : addData.tangentWS; half stretch = abs(anisotropy) * saturate(1.5h * sqrt(brdfData.perceptualRoughness)); addData.anisoReflectionNormal = GetAnisotropicModifiedNormal(grainDirWS, inputData.normalWS, inputData.viewDirectionWS, stretch); half iblPerceptualRoughness = brdfData.perceptualRoughness * saturate(1.2 - abs(anisotropy)); // Override perceptual roughness for ambient specular reflections brdfData.perceptualRoughness = iblPerceptualRoughness; // Only used for reflections - so we skip it /*float3 preFGD = SAMPLE_TEXTURE2D_LOD(_PreIntegratedLUT, sampler_PreIntegratedLUT, float2(NdotV, brdfData.perceptualRoughness), 0).xyz; // Denormalize the value preFGD.y = preFGD.y / (1 - preFGD.y); half3 specularFGD = preFGD.yyy * fresnel0; // z = FabricLambert half3 diffuseFGD = preFGD.z; half reflectivity = preFGD.y;*/ half4 shadowMask = CalculateShadowMask(inputData); AmbientOcclusionFactor aoFactor = CreateAmbientOcclusionFactor(inputData, surfaceData); uint meshRenderingLayers = GetMeshRenderingLayer(); Light mainLight = GetMainLight(inputData, shadowMask, aoFactor); half3 mainLightColor = mainLight.color; MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI); LightingData lightingData = CreateLightingData(inputData, surfaceData); // NOTE: We use addData.anisoReflectionNormal here! lightingData.giColor = GlobalIllumination_LuxAniso( brdfData, brdfData, // brdfDataClearCoat 0, // surfaceData.clearCoatMask, inputData.bakedGI, aoFactor.indirectAmbientOcclusion, inputData.positionWS, addData.anisoReflectionNormal, inputData.normalWS, inputData.viewDirectionWS, inputData.normalizedScreenSpaceUV ); half NdotL; #if defined(_LIGHT_LAYERS) if (IsMatchingLightLayer(mainLight.layerMask, meshRenderingLayers)) #endif { NdotL = saturate(dot(inputData.normalWS, mainLight.direction )); lightingData.mainLightColor = LightingPhysicallyBased_LuxCloth(brdfData, addData, mainLight, inputData.normalWS, inputData.viewDirectionWS, NdotL); } #ifdef _ADDITIONAL_LIGHTS uint pixelLightCount = GetAdditionalLightsCount(); #if USE_FORWARD_PLUS for (uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++) { FORWARD_PLUS_SUBTRACTIVE_LIGHT_CHECK Light light = GetAdditionalLight(lightIndex, inputData, shadowMask, aoFactor); #ifdef _LIGHT_LAYERS if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers)) #endif { half3 lightColor = light.color; NdotL = saturate(dot(inputData.normalWS, light.direction )); lightingData.additionalLightsColor += LightingPhysicallyBased_LuxCloth(brdfData, addData, light, inputData.normalWS, inputData.viewDirectionWS, NdotL); } } #endif LIGHT_LOOP_BEGIN(pixelLightCount) Light light = GetAdditionalLight(lightIndex, inputData, shadowMask, aoFactor); #if defined(_LIGHT_LAYERS) if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers)) #endif { half3 lightColor = light.color; NdotL = saturate(dot(inputData.normalWS, light.direction )); lightingData.additionalLightsColor += LightingPhysicallyBased_LuxCloth(brdfData, addData, light, inputData.normalWS, inputData.viewDirectionWS, NdotL); } LIGHT_LOOP_END #endif #ifdef _ADDITIONAL_LIGHTS_VERTEX lightingData.vertexLightingColor += inputData.vertexLighting * brdfData.diffuse; #endif //return float4(inputData.normalTS, 1); return CalculateFinalColor(lightingData, surfaceData.alpha); //return brdfData.perceptualRoughness; }