#ifndef LIGHTWEIGHT_SKINLIGHTING_INCLUDED #define LIGHTWEIGHT_SKINLIGHTING_INCLUDED #if !defined(SHADERGRAPH_PREVIEW) || defined(LIGHTWEIGHT_LIGHTING_INCLUDED) // As we do not have access to the vertex lights we will make the shader always sample add lights per pixel #if defined(_ADDITIONAL_LIGHTS_VERTEX) #undef _ADDITIONAL_LIGHTS_VERTEX #define _ADDITIONAL_LIGHTS #endif #endif //TEXTURE2D(_SkinLUT); SAMPLER(sampler_SkinLUT); float4 _SkinLUT_TexelSize; #if !defined(SHADERGRAPH_PREVIEW) || defined(LIGHTWEIGHT_LIGHTING_INCLUDED) half3 DirectBDRF_Lux(BRDFData brdfData, half3 normalWS, half3 lightDirectionWS, half3 viewDirectionWS) { #ifndef _SPECULARHIGHLIGHTS_OFF float3 halfDir = SafeNormalize(lightDirectionWS + viewDirectionWS); float NoH = saturate(dot(normalWS, halfDir)); half LoH = saturate(dot(lightDirectionWS, halfDir)); // GGX Distribution multiplied by combined approximation of Visibility and Fresnel // BRDFspec = (D * V * F) / 4.0 // D = roughness² / ( NoH² * (roughness² - 1) + 1 )² // V * F = 1.0 / ( LoH² * (roughness + 0.5) ) // See "Optimizing PBR for Mobile" from Siggraph 2015 moving mobile graphics course // https://community.arm.com/events/1155 // Final BRDFspec = roughness² / ( NoH² * (roughness² - 1) + 1 )² * (LoH² * (roughness + 0.5) * 4.0) // We further optimize a few light invariant terms // brdfData.normalizationTerm = (roughness + 0.5) * 4.0 rewritten as roughness * 4.0 + 2.0 to a fit a MAD. float d = NoH * NoH * brdfData.roughness2MinusOne + 1.00001f; half LoH2 = LoH * LoH; half specularTerm = brdfData.roughness2 / ((d * d) * max(0.1h, LoH2) * brdfData.normalizationTerm); // On platforms where half actually means something, the denominator has a risk of overflow // clamp below was added specifically to "fix" that, but dx compiler (we convert bytecode to metal/gles) // sees that specularTerm have only non-negative terms, so it skips max(0,..) in clamp (leaving only min(100,...)) #if defined (SHADER_API_MOBILE) || defined (SHADER_API_SWITCH) specularTerm = specularTerm - HALF_MIN; specularTerm = clamp(specularTerm, 0.0, 100.0); // Prevent FP16 overflow on mobiles #endif half3 color = specularTerm * brdfData.specular; // + brdfData.diffuse; return color; #else return 0; //brdfData.diffuse; #endif } half3 GlobalIllumination_Lux(BRDFData brdfData, half3 bakedGI, half occlusion, half3 normalWS, half3 viewDirectionWS, half specOccluison) { half fresnelTerm = 0; half3 indirectSpecular = 0; if(specOccluison > 0) { half3 reflectVector = reflect(-viewDirectionWS, normalWS); fresnelTerm = Pow4(1.0 - saturate(dot(normalWS, viewDirectionWS))); indirectSpecular = GlossyEnvironmentReflection(reflectVector, brdfData.perceptualRoughness, occlusion) * specOccluison; } half3 indirectDiffuse = bakedGI * occlusion; return EnvironmentBRDF(brdfData, indirectDiffuse, indirectSpecular, fresnelTerm); } 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; } 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); } #endif void Lighting_half( // Base inputs half3 positionWS, half3 viewDirectionWS, // Normal inputs half3 normalWS, half3 tangentWS, half3 bitangentWS, bool enableNormalMapping, bool enableDiffuseNormalMapping, bool enableBackScattering, bool useVertexNormal, // Surface description half3 albedo, half metallic, half3 specular, half smoothness, half occlusion, half3 emission, half alpha, half4 translucency, half AmbientReflection, half3 subsurfaceColor, half curvature, half skinMask, half maskbyshadowstrength, half backScattering, Texture2D normalMap, SamplerState sampler_Normal, float2 UV, float bumpScale, float diffuseBias, // Lightmapping float2 lightMapUV, // Final lit color out half3 MetaAlbedo, out half3 FinalLighting, out half3 MetaSpecular ) { #if defined(SHADERGRAPH_PREVIEW) || ( !defined(LIGHTWEIGHT_LIGHTING_INCLUDED) && !defined(UNIVERSAL_LIGHTING_INCLUDED) ) FinalLighting = albedo; MetaAlbedo = half3(0,0,0); MetaSpecular = half3(0,0,0); #else half3 diffuseNormalWS; if (enableNormalMapping) { half3x3 ToW = half3x3(tangentWS.xyz, bitangentWS.xyz, normalWS.xyz); half4 sampleNormal = SAMPLE_TEXTURE2D(normalMap, sampler_Normal, UV); half3 normalTS = UnpackNormalScale(sampleNormal, bumpScale); // Get specular normal half3 snormalWS = TransformTangentToWorld(normalTS, ToW); snormalWS = NormalizeNormalPerPixel(snormalWS); // Get diffuse normal if(enableDiffuseNormalMapping) { half4 sampleNormalDiffuse = SAMPLE_TEXTURE2D_BIAS(normalMap, sampler_Normal, UV, diffuseBias); // Do not manually unpack the normal map as it might use RGB. half3 diffuseNormalTS = UnpackNormal(sampleNormalDiffuse); // Get diffuseNormalWS diffuseNormalWS = TransformTangentToWorld(diffuseNormalTS, ToW); diffuseNormalWS = NormalizeNormalPerPixel(diffuseNormalWS); } else { diffuseNormalWS = (useVertexNormal) ? normalWS : snormalWS; } // Set specular normal normalWS = snormalWS; } else { normalWS = NormalizeNormalPerPixel(normalWS); diffuseNormalWS = normalWS; } viewDirectionWS = SafeNormalize(viewDirectionWS); // GI Lighting half3 bakedGI; #ifdef LIGHTMAP_ON lightMapUV = lightMapUV * unity_LightmapST.xy + unity_LightmapST.zw; bakedGI = SAMPLE_GI(lightMapUV, half3(0,0,0), diffuseNormalWS); #else bakedGI = SampleSH(diffuseNormalWS); #endif BRDFData brdfData; InitializeBRDFData(albedo, metallic, specular, smoothness, alpha, brdfData); float4 clipPos = TransformWorldToHClip(positionWS); // Get Shadow Sampling Coords #if SHADOWS_SCREEN float4 shadowCoord = ComputeScreenPos(clipPos); #else float4 shadowCoord = TransformWorldToShadowCoord(positionWS); #endif Light mainLight = GetMainLight(shadowCoord); half3 mainLightColor = mainLight.color; // SSAO #if defined(_SCREEN_SPACE_OCCLUSION) float4 ndc = clipPos * 0.5f; float2 normalized = float2(ndc.x, ndc.y * _ProjectionParams.x) + ndc.w; normalized /= clipPos.w; normalized *= _ScreenParams.xy; // We could also use IN.Screenpos(default) --> ( IN.Screenpos.xy * _ScreenParams.xy) // HDRP 10.1 normalized = GetNormalizedScreenSpaceUV(normalized); AmbientOcclusionFactor aoFactor = GetScreenSpaceAmbientOcclusion(normalized); mainLight.color *= aoFactor.directAmbientOcclusion; occlusion = min(occlusion, aoFactor.indirectAmbientOcclusion); #endif FinalLighting = GlobalIllumination_Lux(brdfData, bakedGI, occlusion, normalWS, viewDirectionWS, AmbientReflection); // Backscattering if (enableBackScattering) { FinalLighting += backScattering * SampleSH(-diffuseNormalWS) * albedo * occlusion * translucency.x * subsurfaceColor * skinMask; } MixRealtimeAndBakedGI(mainLight, normalWS, bakedGI, half4(0, 0, 0, 0)); half NdotLUnclamped = dot(diffuseNormalWS, mainLight.direction); half NdotL = saturate( dot(normalWS, mainLight.direction) ); FinalLighting += LightingPhysicallyBasedSkin(brdfData, mainLight, normalWS, viewDirectionWS, NdotL, NdotLUnclamped, curvature, skinMask); // Subsurface Scattering half transPower = translucency.y; half3 transLightDir = mainLight.direction + normalWS * translucency.w; half transDot = dot( transLightDir, -viewDirectionWS ); transDot = exp2(saturate(transDot) * transPower - transPower); FinalLighting += skinMask * subsurfaceColor * transDot * (1.0 - saturate(NdotLUnclamped)) * mainLightColor * lerp(1.0h, mainLight.shadowAttenuation, translucency.z) * translucency.x; // URP 10 half4 shadowMask = half4(1, 1, 1, 1); #ifdef _ADDITIONAL_LIGHTS uint pixelLightCount = GetAdditionalLightsCount(); for (uint i = 0u; i < pixelLightCount; ++i) { //Light light = GetAdditionalLight(i, inputData.positionWS); // Get index upfront as we need it for GetAdditionalLightShadowParams(); int index = GetPerObjectLightIndex(i); // Light light = GetAdditionalPerObjectLight(index, positionWS); // here; shadowAttenuation = 1.0; // URP 10: We have to use the new GetAdditionalLight function Light light = GetAdditionalLight(i, positionWS, shadowMask); half3 lightColor = light.color; #if defined(_SCREEN_SPACE_OCCLUSION) light.color *= aoFactor.directAmbientOcclusion; #endif half NdotLUnclamped = dot(diffuseNormalWS, light.direction); NdotL = saturate( dot(normalWS, light.direction) ); FinalLighting += LightingPhysicallyBasedSkin(brdfData, light, normalWS, viewDirectionWS, NdotL, NdotLUnclamped, curvature, skinMask); // Transmission half4 shadowParams = GetAdditionalLightShadowParams(index); lightColor *= lerp(1, shadowParams.x, maskbyshadowstrength); // shadowParams.x == shadow strength, which is 0 for point lights transLightDir = light.direction + normalWS * translucency.w; transDot = dot( transLightDir, -viewDirectionWS ); transDot = exp2(saturate(transDot) * transPower - transPower); FinalLighting += skinMask * subsurfaceColor * transDot * (1.0 - NdotL) * lightColor * lerp(1.0h, light.shadowAttenuation, translucency.z) * light.distanceAttenuation * translucency.x; } #endif #ifdef _ADDITIONAL_LIGHTS_VERTEX // FinalLighting += inputData.vertexLighting * brdfData.diffuse; #endif FinalLighting += emission; // Set Albedo for meta pass #if defined(LIGHTWEIGHT_META_PASS_INCLUDED) || defined(UNIVERSAL_META_PASS_INCLUDED) FinalLighting = half3(0,0,0); MetaAlbedo = albedo; MetaSpecular = specular; #else MetaAlbedo = half3(0,0,0); MetaSpecular = half3(0,0,0); #endif #endif } // Unity 2019.1. needs a float version void Lighting_float( // Base inputs half3 positionWS, half3 viewDirectionWS, // Normal inputs half3 normalWS, half3 tangentWS, half3 bitangentWS, bool enableNormalMapping, bool enableDiffuseNormalMapping, bool enableBackScattering, bool useVertexNormal, // Surface description half3 albedo, half metallic, half3 specular, half smoothness, half occlusion, half3 emission, half alpha, half4 translucency, half AmbientReflection, half3 subsurfaceColor, half curvature, half skinMask, half maskbyshadowstrength, half backScattering, Texture2D normalMap, SamplerState sampler_Normal, float2 UV, float bumpScale, float diffuseBias, // Lightmapping float2 lightMapUV, // Final lit color out half3 MetaAlbedo, out half3 FinalLighting, out half3 MetaSpecular ) { Lighting_half( positionWS, viewDirectionWS, normalWS, tangentWS, bitangentWS, enableNormalMapping, enableDiffuseNormalMapping, enableBackScattering, useVertexNormal, albedo, metallic, specular, smoothness, occlusion, emission, alpha, translucency, AmbientReflection, subsurfaceColor, curvature, skinMask, maskbyshadowstrength, backScattering, normalMap, sampler_Normal, UV, bumpScale, diffuseBias, lightMapUV, MetaAlbedo, FinalLighting, MetaSpecular ); } #endif