123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- // NOTE: Based on URP Lighting.hlsl which replaced some half3 with floats to avoid lighting artifacts on mobile
- // Hair lighting functions renamed to solves problems with LWRP 6.x
- #ifndef LIGHTWEIGHT_HAIRLIGHTING_INCLUDED
- #define LIGHTWEIGHT_HAIRLIGHTING_INCLUDED
- #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"
- // From HDRP -----------------------------------------
- float RoughnessToBlinnPhongSpecularExponent_Lux(float roughness)
- {
- return clamp(2 * rcp(roughness * roughness) - 2, FLT_EPS, rcp(FLT_EPS));
- }
- //http://web.engr.oregonstate.edu/~mjb/cs519/Projects/Papers/HairRendering.pdf
- float3 ShiftTangent_Lux(float3 T, float3 N, float shift)
- {
- return normalize(T + N * shift);
- }
- // Note: this is Blinn-Phong, the original paper uses Phong.
- float3 D_KajiyaKay_Lux(float3 T, float3 H, float specularExponent)
- {
- float TdotH = dot(T, H);
- float sinTHSq = saturate(1.0 - TdotH * TdotH);
- float dirAttn = saturate(TdotH + 1.0); // Evgenii: this seems like a hack? Do we really need this?
- // Note: Kajiya-Kay is not energy conserving.
- // We attempt at least some energy conservation by approximately normalizing Blinn-Phong NDF.
- // We use the formulation with the NdotL.
- // See http://www.thetenthplanet.de/archives/255.
- float n = specularExponent;
- float norm = (n + 2) * rcp(2 * PI);
- return dirAttn * norm * PositivePow(sinTHSq, 0.5 * n);
- }
- /*
- #if (_USE_LIGHT_FACING_NORMAL)
- // The Kajiya-Kay model has a "built-in" transmission, and the 'NdotL' is always positive.
- float cosTL = dot(bsdfData.hairStrandDirectionWS, L);
- float sinTL = sqrt(saturate(1 - cosTL * cosTL));
- float NdotL = sinTL; // Corresponds to the cosine w.r.t. the light-facing normal
- #else
- // Double-sided Lambert.
- float NdotL = dot(bsdfData.normalWS, L);
- #endif
- */
- // From HDRP END -----------------------------------------
- /*
- This is input data:
- struct InputData
- {
- float3 positionWS;
- half3 normalWS;
- half3 viewDirectionWS;
- float4 shadowCoord;
- half fogCoord;
- half3 vertexLighting;
- half3 bakedGI;
- };
- */
- // Ref: Donald Revie - Implementing Fur Using Deferred Shading (GPU Pro 2)
- // The grain direction (e.g. hair or brush direction) is assumed to be orthogonal to the normal.
- // The returned normal is NOT normalized.
- half3 ComputeGrainNormal_Lux(half3 grainDir, half3 V)
- {
- half3 B = cross(-V, grainDir);
- return cross(B, grainDir);
- }
- // Fake anisotropic by distorting the normal.
- // The grain direction (e.g. hair or brush direction) is assumed to be orthogonal to N.
- // Anisotropic ratio (0->no isotropic; 1->full anisotropy in tangent direction)
- half3 GetAnisotropicModifiedNormal_Lux(half3 grainDir, half3 N, half3 V, half anisotropy)
- {
- half3 grainNormal = ComputeGrainNormal_Lux(grainDir, V);
- return lerp(N, grainNormal, anisotropy);
- }
- half3 GlobalIlluminationHair_Lux(
- //BRDFData brdfData,
- half3 albedo,
- half3 specular,
- half roughness,
- half perceptualRoughness,
- half occlusion,
-
- half3 bakedGI,
- half3 normalWS,
- half3 viewDirectionWS,
- half3 bitangentWS,
- half ambientReflection
- )
- {
- // We do not handle backfaces properly yet.
- half NdotV = dot(normalWS, viewDirectionWS);
- half s = sign(NdotV);
- // Lets fix this for reflections?
- //NdotV = s * NdotV;
- // Strengthen occlusion on backfaces
- //occlusion = lerp(occlusion * 0.5, occlusion, saturate(1 + s));
- // We do not "fix" the reflection vector. This gives us some scattering like reflections
- //half3 reflectNormalWS = GetAnisotropicModifiedNormal_Lux(s * bitangentWS, s * normalWS, viewDirectionWS, 0.6h);
- half3 reflectNormalWS = GetAnisotropicModifiedNormal_Lux(bitangentWS, normalWS, viewDirectionWS, 0.6h);
- half3 reflectVector = reflect(-viewDirectionWS, reflectNormalWS);
- half fresnelTerm = Pow4(1.0 - saturate(NdotV) );
- // ??? perceptualRoughness *= saturate(1.2 - 0.8); //abs(bsdfData.anisotropy));
- half3 indirectDiffuse = bakedGI * occlusion;
- half3 indirectSpecular = GlossyEnvironmentReflection(reflectVector, perceptualRoughness, occlusion) * ambientReflection;
- // EnvironmentBRDFHair
- half3 c = indirectDiffuse * albedo;
- float surfaceReduction = 1.0 / (roughness * roughness + 1.0);
- half reflectivity = ReflectivitySpecular(specular);
- half grazingTerm = saturate( (1.0h - roughness) + reflectivity);
- c += surfaceReduction * indirectSpecular * lerp(specular, grazingTerm, fresnelTerm);
- return c;
- }
- half3 LightingHair_Lux(
- half3 albedo,
- half3 specular,
- Light light,
-
- half3 normalWS,
- half geomNdotV,
- half3 viewDirectionWS,
- half roughness1,
- half roughness2,
- half3 t1,
- half3 t2,
- half3 specularTint,
- half3 secondarySpecularTint,
- half rimTransmissionIntensity
- )
- {
- half NdotL = dot(normalWS, light.direction);
- half LdotV = dot(light.direction, viewDirectionWS);
- float invLenLV = rsqrt(max(2.0 * LdotV + 2.0, FLT_EPS));
- half3 halfDir = (light.direction + viewDirectionWS) * invLenLV;
- half3 hairSpec1 = specularTint * D_KajiyaKay_Lux(t1, halfDir, roughness1);
- #if defined(_SECONDARYLOBE)
- half3 hairSpec2 = secondarySpecularTint * D_KajiyaKay_Lux(t2, halfDir, roughness2);
- #endif
- float NdotH = saturate(dot(normalWS, halfDir));
- half LdotH = saturate(dot(light.direction, halfDir));
- half3 F = F_Schlick(specular, LdotH);
- // Reflection
- half3 specR = 0.25h * F * (hairSpec1
- #if defined(_SECONDARYLOBE)
- + hairSpec2
- #endif
- ) * saturate(NdotL) * saturate(geomNdotV * HALF_MAX);
- // Transmission // Yibing's and Morten's hybrid scatter model hack.
- half scatterFresnel1 = pow(saturate(-LdotV), 9.0h) * pow(saturate(1.0h - geomNdotV * geomNdotV), 12.0h);
- // This looks shitty (using 20)
- //half scatterFresnel2 = saturate(PositivePow((1.0h - geomNdotV), 20.0h));
- half scatterFresnel2 = saturate(Pow4(1.0h - geomNdotV));
- half transmission = scatterFresnel1 + rimTransmissionIntensity * scatterFresnel2;
- half3 specT = albedo * transmission;
- half3 diffuse = albedo * saturate(NdotL);
- // combine
- half3 result = (diffuse + specR + specT) * light.color * light.distanceAttenuation * light.shadowAttenuation;
- return result;
- }
- half4 LuxURPHairFragment(
- InputData inputData,
- half3 tangentWS,
- half3 albedo,
- half3 specular,
- half occlusion,
- half3 emission,
- half3 noise,
- half specularShift,
- half3 specularTint,
- half perceptualRoughness,
- half secondarySpecularShift,
- half3 secondarySpecularTint,
- half secondaryPerceptualRoughness,
- half rimTransmissionIntensity,
- half ambientReflection
- )
- {
- // ShadowMask: To ensure backward compatibility we have to avoid using shadowMask input, as it is not present in older shaders
- #if defined(SHADOWS_SHADOWMASK) && defined(LIGHTMAP_ON)
- half4 shadowMask = inputData.shadowMask;
- #elif !defined (LIGHTMAP_ON)
- half4 shadowMask = unity_ProbesOcclusion;
- #else
- half4 shadowMask = half4(1, 1, 1, 1);
- #endif
- // TODO: Simplify this...
- perceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(perceptualRoughness); // * saturate(noise.r * 2) );
- half roughness1 = PerceptualRoughnessToRoughness(perceptualRoughness);
- half pbRoughness1 = RoughnessToBlinnPhongSpecularExponent_Lux(roughness1);
- #if defined(_SECONDARYLOBE)
- secondaryPerceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(secondaryPerceptualRoughness); // * saturate(noise.r * 2) );
- half roughness2 = PerceptualRoughnessToRoughness(secondaryPerceptualRoughness);
- half pbRoughness2 = RoughnessToBlinnPhongSpecularExponent_Lux(roughness2);
- #else
- secondaryPerceptualRoughness = 0;
- half roughness2 = 0;
- half pbRoughness2 = 0;
- #endif
- half geomNdotV = dot(inputData.normalWS, inputData.viewDirectionWS);
- // Adjust tangentWS in case normal mapping is enabled
- #if defined(_NORMALMAP)
- tangentWS = Orthonormalize(tangentWS, inputData.normalWS);
- #endif
- // Always calculate bitangent WS
- half3 bitangentWS = cross(inputData.normalWS, tangentWS);
- #if defined(_STRANDDIR_BITANGENT)
- half3 strandDirWS = bitangentWS;
- #else
- half3 strandDirWS = tangentWS;
- #endif
- half3 t1 = ShiftTangent_Lux(strandDirWS, inputData.normalWS, specularShift);
- #if defined(_SECONDARYLOBE)
- half3 t2 = ShiftTangent_Lux(strandDirWS, inputData.normalWS, secondarySpecularShift);
- #else
- half3 t2 = 0;
- #endif
- // Start Lighting
- // (From HDRP) Note: For Kajiya hair we currently rely on a single cubemap sample instead of two, as in practice smoothness of both lobe aren't too far from each other.
- // and we take smoothness of the secondary lobe as it is often more rough (it is the colored one).
- // NOPE: We use primary!!!!!
- // Main Light
- Light light = GetMainLight(inputData.shadowCoord);
-
- // SSAO
- #if defined(_SCREEN_SPACE_OCCLUSION)
- AmbientOcclusionFactor aoFactor = GetScreenSpaceAmbientOcclusion(inputData.normalizedScreenSpaceUV);
- // Does not play nicely with blend?!
- //light.color *= aoFactor.directAmbientOcclusion;
- occlusion = min(occlusion, aoFactor.indirectAmbientOcclusion);
- #endif
- half3 color = GlobalIlluminationHair_Lux(albedo, specular, roughness1, perceptualRoughness, occlusion, inputData.bakedGI, inputData.normalWS, inputData.viewDirectionWS, bitangentWS, ambientReflection);
- MixRealtimeAndBakedGI(light, inputData.normalWS, inputData.bakedGI, half4(0, 0, 0, 0));
- color += LightingHair_Lux(albedo, specular, light, inputData.normalWS, geomNdotV, inputData.viewDirectionWS, pbRoughness1, pbRoughness2, t1, t2, specularTint, secondarySpecularTint, rimTransmissionIntensity);
- // Additional Lights
- #ifdef _ADDITIONAL_LIGHTS
- uint pixelLightCount = GetAdditionalLightsCount();
- for (uint i = 0u; i < pixelLightCount; ++i) {
- // Light light = GetAdditionalPerObjectLight(index, positionWS); // here; shadowAttenuation = 1.0;
- // URP 10: We have to use the new GetAdditionalLight function
- Light light = GetAdditionalLight(i, inputData.positionWS, shadowMask);
- // Does not play nicely with blend?!
- #if defined(_SCREEN_SPACE_OCCLUSION)
- //light.color *= aoFactor.directAmbientOcclusion;
- #endif
- color += LightingHair_Lux(albedo, specular, light, inputData.normalWS, geomNdotV, inputData.viewDirectionWS, pbRoughness1, pbRoughness2, t1, t2, specularTint, secondarySpecularTint, rimTransmissionIntensity);
- }
- #endif
- #ifdef _ADDITIONAL_LIGHTS_VERTEX
- color += inputData.vertexLighting * albedo;
- #endif
- color += emission;
- return half4(color, 1);
- }
- #endif
|