diff --git a/features/Hair Specular/Shaders/Hair/Hair.hlsli b/features/Hair Specular/Shaders/Hair/Hair.hlsli index be1e6167c2..8fad10b14d 100644 --- a/features/Hair Specular/Shaders/Hair/Hair.hlsli +++ b/features/Hair Specular/Shaders/Hair/Hair.hlsli @@ -1,9 +1,10 @@ #ifndef __HAIR_DEPENDENCY_HLSL__ #define __HAIR_DEPENDENCY_HLSL__ +#include "Common/BRDF.hlsli" #include "Common/Math.hlsli" -#define MARSCHNER false +// #define MARSCHNER namespace Hair { @@ -20,19 +21,11 @@ namespace Hair return dirAtten * norm * pow(sinTH, 0.5 * n); } - float HairF0() + float3 HairF0() { const float n = 1.55; const float F0 = pow((1 - n) / (1 + n), 2); - return F0; - } - - // [Schlick et al. 1998, "An inexpensive brdf model for physically-based rendering."] - // https://doi.org/10.1111/1467-8659.1330233 - float Hair_F(float CosTheta) - { - const float F0 = HairF0(); - return F0 + (1 - F0) * pow(1 - CosTheta, 5); + return F0.xxx; } float3 ShiftTangent(float3 T, float3 N, float shift) @@ -42,110 +35,137 @@ namespace Hair // [Scheuermann 2004, "Hair Rendering and Shading"] // https://web.engr.oregonstate.edu/~mjb/cs557/Projects/Papers/HairRendering.pdf - void GetHairDirectLightScheuermann(out float3 dirDiffuse, out float3 dirSpecular, float3 T, float3 L, float3 V, float3 N, float3 lightColor, float shininess, float2 uv, float3 baseColor) + void GetHairDirectLightScheuermann(out float3 dirDiffuse, out float3 dirSpecular, float3 T, float3 L, float3 V, float3 N, float3 VN, float3 lightColor, float shininess, float2 uv, float3 baseColor) { const float3 H = normalize(L + V); - const float NdotL = saturate(dot(N, L)); + const float oNdotL = dot(N, L); + const float NdotL = saturate(oNdotL); const float NdotV = saturate(dot(N, V)); + const float VNdotV = dot(VN, V); + const float VNdotL = dot(VN, L); + const float HdotV = saturate(dot(H, V)); + const float HdotL = saturate(dot(H, L)); + const float wrapped = 0.5; - dirDiffuse = NdotL * lightColor / Math::PI; + // [Yibing Jiang 2016, "The Process of Creating Volumetric-based Materials in Uncharted 4"] + // https://advances.realtimerendering.com/s2016 + dirDiffuse = saturate(oNdotL + wrapped) / (1 + wrapped); + float3 scatterColor = lerp(float3(0.992, 0.808, 0.518), baseColor, 0.5); + dirDiffuse = saturate(scatterColor + NdotL) * dirDiffuse * lightColor; - float3 TshiftPrimary = T; - float3 TshiftSecondary = T; + float3 TshiftPrimary; + float3 TshiftSecondary; if (SharedData::hairSpecularSettings.EnableTangentShift) { - const float shift = TexTangentShift.SampleLevel(SampColorSampler, uv, SharedData::MipBias).x - 0.5; - TshiftPrimary = ShiftTangent(T, N, shift + SharedData::hairSpecularSettings.PrimaryShift); - TshiftSecondary = ShiftTangent(T, N, shift + SharedData::hairSpecularSettings.SecondaryShift); + const float shift = TexTangentShift.SampleLevel(SampColorSampler, uv, 0).x - 0.5; + TshiftPrimary = ShiftTangent(T, N, shift + SharedData::hairSpecularSettings.PrimaryTangentShift); + TshiftSecondary = ShiftTangent(T, N, shift + SharedData::hairSpecularSettings.SecondaryTangentShift); + } else { + TshiftPrimary = T; + TshiftSecondary = T; } const float3 specPrimary = D_KajiyaKay(TshiftPrimary, H, shininess); const float3 specSecondary = D_KajiyaKay(TshiftSecondary, H, shininess * 0.5); - const float F = Hair_F(saturate(dot(H, V))); - float3 specR = 0.25 * F * (specPrimary + specSecondary) * NdotL * saturate(NdotV * (3.4e+38)); - specR = Color::LinearToGamma(specR); - float scatterFresnel1 = pow(saturate(-dot(L, V)), 9) * pow(saturate(1 - NdotV * NdotV), 12); - float scatterFresnel2 = saturate(pow((1 - NdotV), 20)); - float3 specT = scatterFresnel1 + scatterFresnel2; - float3 specTerm = specR + specT * baseColor; + const float3 F = BRDF::F_Schlick(HairF0(), HdotL); + float3 specR = 0.25 * F * (specPrimary + specSecondary * scatterColor) * NdotL * saturate(VNdotV * (3.4e+38)); + float scatterFresnel1 = pow(saturate(-dot(L, V)), 9) * pow(saturate(1 - VNdotV * VNdotV), 12); + float scatterFresnel2 = saturate(pow((1 - VNdotV), 20)); + float3 specT = (scatterFresnel1 + scatterFresnel2 * scatterColor) * SharedData::hairSpecularSettings.Transmission; + float3 specTerm = specR + specT; + // specTerm = Color::LinearToGamma(specTerm); dirSpecular = specTerm * lightColor; } float Hair_g(float B, float Theta) { - const float DenominatorB = max(B, 0.01f); - return exp(-0.5 * pow(Theta, 2) / (B * B)) / (sqrt(2 * Math::PI) * DenominatorB); + return exp(-0.5 * Theta * Theta / (B * B)) / (sqrt(Math::TAU) * B); } // [Marschner et al. 2003, "Light reflection from human hair fibers."] // https://graphics.stanford.edu/papers/hair/hair-sg03final.pdf - float3 D_Marschner(float3 L, float3 V, float3 N, float roughness, float3 baseColor, float Area, float Backlit) + float3 D_Marschner(float3 L, float3 V, float3 N, float roughness, float3 baseColor, float area, float backlit) { - const float VoL = dot(V, L); - const float SinThetaL = dot(N, L); - const float SinThetaV = dot(N, V); - float CosThetaD = cos(0.5 * abs(asin(SinThetaV) - asin(SinThetaL))); + const float NdotL = dot(N, L); + const float NdotV = dot(N, V); + const float VdotL = dot(V, L); + + float cosThetaL = sqrt(max(0, 1 - NdotL * NdotL)); + float cosThetaV = sqrt(max(0, 1 - NdotV * NdotV)); + float cosThetaD = sqrt((1 + cosThetaL * cosThetaV + NdotV * NdotL) / 2.0); - const float3 Lp = L - SinThetaL * N; - const float3 Vp = V - SinThetaV * N; - const float CosPhi = dot(Lp, Vp) * rsqrt(dot(Lp, Lp) * dot(Vp, Vp) + 1e-4); - const float CosHalfPhi = sqrt(saturate(0.5 + 0.5 * CosPhi)); + const float3 Lp = L - NdotL * N; + const float3 Vp = V - NdotL * N; + const float cosPhi = dot(Lp, Vp) * rsqrt(dot(Lp, Lp) * dot(Vp, Vp) + 1e-4); + const float cosHalfPhi = sqrt(saturate(0.5 + 0.5 * cosPhi)); - float n = 1.55; - float n_prime = 1.19 / CosThetaD + 0.36 * CosThetaD; + float n_prime = 1.19 / cosThetaD + 0.36 * cosThetaD; - float Shift = 0.035; - float Alpha[] = { + const float Shift = 0.0499f; + const float Alpha[] = { -Shift * 2, Shift, - Shift * 4, + Shift * 4 }; - float B[] = { - Area + pow(roughness, 2), - Area + pow(roughness, 2) / 2, - Area + pow(roughness, 2) * 2, + area + roughness, + area + roughness / 2, + area + roughness * 2 }; - float3 R, TT, TRT = 0; - - { - const float sa = sin(Alpha[0]); - const float ca = cos(Alpha[0]); - float Shift = 2 * sa * (ca * CosHalfPhi * sqrt(1 - SinThetaV * SinThetaV) + sa * SinThetaV); + float hairIOR = 1.55; + float3 specularColor = HairF0(); + + float3 Tp; + float Mp, Np, Fp, a, h, f; + float ThetaH = NdotL + NdotV; + + float3 R, TT, TRT; + + // R + Mp = Hair_g(B[0], ThetaH - Alpha[0]); + Np = 0.25 * cosHalfPhi; + Fp = BRDF::F_Schlick(specularColor, sqrt(saturate(0.5 + 0.5 * VdotL))).x; + R = (Mp * Np) * (Fp * lerp(1, backlit, saturate(-VdotL))); + + // TT + Mp = Hair_g(B[1], ThetaH - Alpha[1]); + a = (1.55f / hairIOR) * rcp(n_prime); + h = cosHalfPhi * (1 + a * (0.6 - 0.8 * cosPhi)); + f = BRDF::F_Schlick(specularColor, cosThetaD * sqrt(saturate(1 - h * h))).x; + Fp = (1 - f) * (1 - f); + Tp = pow(abs(baseColor), 0.5 * sqrt(1 - (h * a) * (h * a)) / cosThetaD); + Np = exp(-3.65 * cosPhi - 3.98); + TT = (Mp * Np) * (Fp * Tp) * backlit; + + // TRT + Mp = Hair_g(B[2], ThetaH - Alpha[2]); + f = BRDF::F_Schlick(specularColor, cosThetaD * 0.5f).x; + Fp = (1 - f) * (1 - f) * f; + Tp = pow(abs(baseColor), 0.8 / cosThetaD); + Np = exp(17 * cosPhi - 16.78); + TRT = (Mp * Np) * (Fp * Tp); - float Mp = Hair_g(B[0] * sqrt(2.0) * CosHalfPhi, SinThetaL + SinThetaV - Shift); - float Np = 0.25 * CosHalfPhi; - float Fp = Hair_F(sqrt(saturate(0.5 + 0.5 * VoL))); - R = Mp * Np * Fp * 2 * lerp(1, Backlit, saturate(-VoL)); - } - - { - float Mp = Hair_g(B[1], SinThetaL + SinThetaV - Alpha[1]); - float a = 1 / n_prime; - float h = CosHalfPhi * (1 + a * (0.6 - 0.8 * CosPhi)); - float f = Hair_F(CosThetaD * sqrt(saturate(1 - h * h))); - - float Fp = pow(1 - f, 2); - float3 Tp = pow(baseColor, 0.5 * sqrt(1 - pow(h * a, 2)) / CosThetaD); - float Np = exp(-3.65 * CosPhi - 3.98); - - TT = Mp * Np * Fp * Tp * Backlit; - } - - { - float Mp = Hair_g(B[2], SinThetaL + SinThetaV - Alpha[2]); + return R + TT + TRT; + } - float f = Hair_F(CosThetaD * 0.5); - float Fp = pow(1 - f, 2) * f; - float3 Tp = pow(baseColor, 0.8 / CosThetaD); + float3 GetHairDiffuseAttenuationKajiyaKay(float3 N, float3 V, float3 L, float shadow, float3 baseColor) + { + float NdotL = dot(N, L); + float NdotV = dot(N, V); + float3 S = 0; - float Np = exp(17 * CosPhi - 16.78); + float diffuseKajiya = 1 - abs(NdotL); - TRT = Mp * Np * Fp * Tp; - } + float3 fakeN = normalize(V - N * NdotV); + const float wrap = 1; + float wrappedNdotL = saturate((dot(fakeN, L) + wrap) / ((1 + wrap) * (1 + wrap))); + float diffuseScatter = (1 / Math::PI) * lerp(wrappedNdotL, diffuseKajiya, 0.33); + float luma = Color::RGBToLuminance(baseColor); + float3 scatterTint = pow(abs(baseColor / luma), 1 - shadow); + S += sqrt(baseColor) * diffuseScatter * scatterTint; - return R + TT + TRT; + return S; } void GetHairDirectLightMarschner(out float3 dirDiffuse, out float3 dirSpecular, float3 T, float3 L, float3 V, float3 N, float3 lightColor, float shininess, float2 uv, float3 baseColor) @@ -153,29 +173,21 @@ namespace Hair lightColor *= Math::PI; dirDiffuse = 0; dirSpecular = 0; - const float roughness = 1 - 0.01 * shininess; - - dirSpecular += D_Marschner(L, V, N, roughness, baseColor, 0, 1) * lightColor; - } + const float roughness = pow(abs(2.0 / (shininess + 2.0)), 0.25); - void GetHairDirectLight(out float3 dirDiffuse, out float3 dirSpecular, float3 T, float3 L, float3 V, float3 N, float3 lightColor, float shininess, float2 uv, float3 baseColor) - { - if (!MARSCHNER) - GetHairDirectLightScheuermann(dirDiffuse, dirSpecular, T, L, V, N, lightColor, shininess, uv, baseColor); - else - GetHairDirectLightMarschner(dirDiffuse, dirSpecular, T, L, V, N, lightColor, shininess, uv, baseColor); + dirSpecular += D_Marschner(L, V, N, roughness, baseColor, 0, SharedData::hairSpecularSettings.Transmission) * lightColor; + dirDiffuse += GetHairDiffuseAttenuationKajiyaKay(N, V, L, 0, baseColor) * lightColor; + dirSpecular = max(dirSpecular, 0); + dirDiffuse = max(dirDiffuse, 0); } - // [Lazarov 2013, "Getting More Physical in Call of Duty: Black Ops II"] - // https://blog.selfshadow.com/publications/s2013-shading-course/lazarov/s2013_pbs_black_ops_2_slides_v2.pdf - float2 GetEnvBRDFApproxLazarov(float roughness, float NdotV) + void GetHairDirectLight(out float3 dirDiffuse, out float3 dirSpecular, float3 T, float3 L, float3 V, float3 N, float3 VN, float3 lightColor, float shininess, float2 uv, float3 baseColor) { - const float4 c0 = { -1, -0.0275, -0.572, 0.022 }; - const float4 c1 = { 1, 0.0425, 1.04, -0.04 }; - float4 r = roughness * c0 + c1; - float a004 = min(r.x * r.x, exp2(-9.28 * NdotV)) * r.x + r.y; - float2 AB = float2(-1.04, 1.04) * a004 + r.zw; - return AB; +#ifndef MARSCHNER + GetHairDirectLightScheuermann(dirDiffuse, dirSpecular, T, L, V, N, VN, lightColor, shininess, uv, baseColor); +#else + GetHairDirectLightMarschner(dirDiffuse, dirSpecular, T, L, V, N, lightColor, shininess, uv, baseColor); +#endif } float3 ShiftNormal(float3 T, float3 N, float shift) @@ -187,7 +199,7 @@ namespace Hair float3 ShiftWorldNormal(float3 T, float3 N, float n, float2 uv) { - const float shift = TexTangentShift.SampleLevel(SampColorSampler, uv, SharedData::MipBias).x - 0.5; + const float shift = TexTangentShift.SampleLevel(SampColorSampler, uv, 0).x - 0.5; float3 T_shifted = ShiftTangent(T, N, shift + n); float3 N_shifted = normalize(cross(T_shifted, cross(N, T_shifted))); return N_shifted; @@ -195,35 +207,36 @@ namespace Hair void GetHairIndirectSpecularLobeWeights(out float3 diffuseLobeWeight, out float3 specularLobeWeightPrimary, out float3 specularLobeWeightSecondary, float3 T, float3 N, float3 V, float3 VN, float shininess, float2 uv, float3 baseColor) { - const float roughnessPrimary = 1 - 0.01 * shininess; - const float roughnessSecondary = 1 - 0.005 * shininess; + const float roughnessPrimary = pow(abs(2.0 / (shininess + 2.0)), 0.25); + const float roughnessSecondary = pow(abs(2.0 / (shininess * 0.5 + 2.0)), 0.25); const float NdotV = saturate(dot(N, V)); - if (MARSCHNER) { - specularLobeWeightPrimary = 0; - float3 L = normalize(V - N * dot(V, N)); - float NdotL = dot(N, L); - float VdotL = dot(V, L); +#ifdef MARSCHNER + specularLobeWeightPrimary = 0; + specularLobeWeightSecondary = 0; + float3 L = normalize(V - N * dot(V, N)); + float NdotL = dot(N, L); + float VdotL = dot(V, L); - diffuseLobeWeight = D_Marschner(L, V, N, roughnessPrimary, baseColor * Math::PI, 0.2, 0); - return; - } + diffuseLobeWeight = D_Marschner(L, V, N, roughnessPrimary, baseColor * Math::PI, 0.2, 0); + return; +#else float NdotVshifted = NdotV; float NdotVshifted2 = NdotV; if (SharedData::hairSpecularSettings.EnableTangentShift) { - const float shift = TexTangentShift.SampleBias(SampColorSampler, uv, SharedData::MipBias).x - 0.5; - NdotVshifted = saturate(dot(ShiftNormal(T, N, shift + SharedData::hairSpecularSettings.PrimaryShift), V)); - NdotVshifted2 = saturate(dot(ShiftNormal(T, N, shift + SharedData::hairSpecularSettings.SecondaryShift), V)); + const float shift = TexTangentShift.SampleLevel(SampColorSampler, uv, 0).x - 0.5; + NdotVshifted = saturate(dot(ShiftNormal(T, N, shift + SharedData::hairSpecularSettings.PrimaryTangentShift), V)); + NdotVshifted2 = saturate(dot(ShiftNormal(T, N, shift + SharedData::hairSpecularSettings.SecondaryTangentShift), V)); } diffuseLobeWeight = baseColor; specularLobeWeightPrimary = 0; specularLobeWeightSecondary = 0; - const float2 specularBRDFPrimary = GetEnvBRDFApproxLazarov(roughnessPrimary, NdotVshifted); - const float2 specularBRDFSecondary = GetEnvBRDFApproxLazarov(roughnessSecondary, NdotVshifted2); + const float2 specularBRDFPrimary = BRDF::EnvBRDFApproxLazarov(roughnessPrimary, NdotVshifted); + const float2 specularBRDFSecondary = BRDF::EnvBRDFApproxLazarov(roughnessSecondary, NdotVshifted2); const float3 F0 = HairF0(); specularLobeWeightPrimary = F0 * specularBRDFPrimary.x + specularBRDFPrimary.y; @@ -240,6 +253,7 @@ namespace Hair horizon = horizon * horizon; specularLobeWeightPrimary *= horizon; specularLobeWeightSecondary *= horizon; +#endif } float3 Saturation(float3 color, float saturation) @@ -259,13 +273,13 @@ namespace Hair float3 N1 = N; float3 N2 = N; - const float roughnessPrimary = 1 - 0.01 * glossiness; - const float roughnessSecondary = 1 - 0.005 * glossiness; + const float roughnessPrimary = pow(abs(2.0 / (glossiness + 2.0)), 0.25); + const float roughnessSecondary = pow(abs(2.0 / (glossiness * 0.5 + 2.0)), 0.25); if (SharedData::hairSpecularSettings.EnableTangentShift) { - const float shift = TexTangentShift.SampleLevel(SampColorSampler, uv, SharedData::MipBias).x - 0.5; - N1 = ShiftNormal(T, N, shift + SharedData::hairSpecularSettings.PrimaryShift); - N2 = ShiftNormal(T, N, shift + SharedData::hairSpecularSettings.SecondaryShift); + const float shift = TexTangentShift.SampleLevel(SampColorSampler, uv, 0).x - 0.5; + N1 = ShiftNormal(T, N, shift + SharedData::hairSpecularSettings.PrimaryTangentShift); + N2 = ShiftNormal(T, N, shift + SharedData::hairSpecularSettings.SecondaryTangentShift); } # if defined(SKYLIGHTING) diff --git a/package/Shaders/Common/SharedData.hlsli b/package/Shaders/Common/SharedData.hlsli index 9c77625d66..56ca435a4b 100644 --- a/package/Shaders/Common/SharedData.hlsli +++ b/package/Shaders/Common/SharedData.hlsli @@ -149,17 +149,17 @@ namespace SharedData struct HairSpecularSettings { uint Enabled; - float Glossiness; + float HairGlossiness; float SpecularMult; float DiffuseMult; uint EnableTangentShift; - float PrimaryShift; - float SecondaryShift; - float Saturation; + float PrimaryTangentShift; + float SecondaryTangentShift; + float HairSaturation; float SpecularIndirectMult; float DiffuseIndirectMult; float BaseColorMult; - float pad; + float Transmission; }; struct TerrainVariationSettings diff --git a/package/Shaders/Lighting.hlsl b/package/Shaders/Lighting.hlsl index 68d4e2781a..9818b0451c 100644 --- a/package/Shaders/Lighting.hlsl +++ b/package/Shaders/Lighting.hlsl @@ -1841,7 +1841,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) if (SharedData::hairSpecularSettings.Enabled) { hairTint = lerp(1, TintColor.xyz, input.Color.y); baseColor.xyz *= hairTint; - baseColor.xyz = Hair::Saturation(baseColor.xyz, SharedData::hairSpecularSettings.Saturation); + baseColor.xyz = Hair::Saturation(baseColor.xyz, SharedData::hairSpecularSettings.HairSaturation); baseColor.xyz *= SharedData::hairSpecularSettings.BaseColorMult; } # endif @@ -2355,7 +2355,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) } else { # if defined(HAIR) && defined(CS_HAIR) if (SharedData::hairSpecularSettings.Enabled) - Hair::GetHairDirectLight(dirDiffuseColor, lightsSpecularColor, hairT, DirLightDirection, viewDirection, modelNormal.xyz, dirLightColor.xyz * dirDetailShadow, SharedData::hairSpecularSettings.Glossiness, uv, baseColor.xyz); + Hair::GetHairDirectLight(dirDiffuseColor, lightsSpecularColor, hairT, DirLightDirection, viewDirection, modelNormal.xyz, worldSpaceVertexNormal.xyz, dirLightColor.xyz * dirDetailShadow, SharedData::hairSpecularSettings.HairGlossiness, uv, baseColor.xyz); else { # if defined(SPECULAR) lightsSpecularColor = GetLightSpecularInput(input, DirLightDirection, viewDirection, modelNormal.xyz, dirLightColor.xyz * dirDetailShadow, shininess, uv); @@ -2433,7 +2433,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) # if defined(HAIR) && defined(CS_HAIR) if (SharedData::hairSpecularSettings.Enabled) { float3 lightSpecularColor = 0; - Hair::GetHairDirectLight(lightDiffuseColor, lightSpecularColor, hairT, normalizedLightDirection, viewDirection, modelNormal.xyz, lightColor, SharedData::hairSpecularSettings.Glossiness, uv, baseColor.xyz); + Hair::GetHairDirectLight(lightDiffuseColor, lightSpecularColor, hairT, normalizedLightDirection, viewDirection, modelNormal.xyz, worldSpaceVertexNormal.xyz, lightColor, SharedData::hairSpecularSettings.HairGlossiness, uv, baseColor.xyz); lightsSpecularColor += lightSpecularColor; } else { # if defined(SPECULAR) @@ -2514,7 +2514,12 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) SharedData::lightLimitFixSettings.EnableContactShadows && !(light.lightFlags & LightLimitFix::LightFlags::Simple) && shadowComponent != 0.0 && - lightAngle > 0.0) +# if defined(HAIR) && defined(CS_HAIR) + true +# else + lightAngle > 0.0 +# endif + ) { float3 normalizedLightDirectionVS = normalize(light.positionVS[eyeIndex].xyz - viewPosition.xyz); contactShadow = LightLimitFix::ContactShadows(viewPosition, screenNoise, normalizedLightDirectionVS, contactShadowSteps, eyeIndex); @@ -2595,7 +2600,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) # if defined(HAIR) && defined(CS_HAIR) && (defined(SKINNED) || !defined(MODELSPACENORMALS)) if (SharedData::hairSpecularSettings.Enabled) { float3 lightSpecularColor = 0; - Hair::GetHairDirectLight(lightDiffuseColor, lightSpecularColor, hairT, normalizedLightDirection, viewDirection, modelNormal.xyz, lightColor * contactShadow, SharedData::hairSpecularSettings.Glossiness, uv, baseColor.xyz); + Hair::GetHairDirectLight(lightDiffuseColor, lightSpecularColor, hairT, normalizedLightDirection, viewDirection, modelNormal.xyz, worldSpaceVertexNormal.xyz, lightColor * contactShadow, SharedData::hairSpecularSettings.HairGlossiness, uv, baseColor.xyz); lightsSpecularColor += lightSpecularColor; } else { # if defined(SPECULAR) @@ -2772,7 +2777,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) # if defined(EMAT) float complexMaterialRoughness = 1.0 - complexMaterialColor.y; - envRoughness = lerp(envRoughness, pow(complexMaterialRoughness, 1.5), complexMaterial); + envRoughness = lerp(envRoughness, pow(saturate(complexMaterialRoughness), 1.5), complexMaterial); F0 = lerp(F0, complexSpecular, complexMaterial); # endif @@ -2815,7 +2820,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) porosity = lerp(porosity, 0.0, saturate(sqrt(envMask))); # endif float wetnessDarkeningAmount = porosity * wetnessGlossinessAlbedo; - baseColor.xyz = lerp(baseColor.xyz, pow(baseColor.xyz, 1.0 + wetnessDarkeningAmount), 0.8); + baseColor.xyz = lerp(baseColor.xyz, pow(abs(baseColor.xyz), 1.0 + wetnessDarkeningAmount), 0.8); # endif float3 wetnessReflectance = WetnessEffects::GetWetnessAmbientSpecular(screenUV, wetnessNormal, worldSpaceVertexNormal, worldSpaceViewDirection, waterRoughnessSpecular) * wetnessGlossinessSpecular; @@ -2831,8 +2836,8 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) float3 indirectDiffuseLobeWeight, indirectSpecularLobeWeightPrim, indirectSpecularLobeWeightSec; if (SharedData::hairSpecularSettings.Enabled) vertexColor = 1; - Hair::GetHairIndirectSpecularLobeWeights(indirectDiffuseLobeWeight, indirectSpecularLobeWeightPrim, indirectSpecularLobeWeightSec, hairT, worldSpaceNormal.xyz, worldSpaceViewDirection, worldSpaceVertexNormal, SharedData::hairSpecularSettings.Glossiness, uv, baseColor.xyz); - indirectDiffuseLobeWeight *= SharedData::hairSpecularSettings.DiffuseIndirectMult * (1 / Math::PI); + Hair::GetHairIndirectSpecularLobeWeights(indirectDiffuseLobeWeight, indirectSpecularLobeWeightPrim, indirectSpecularLobeWeightSec, hairT, worldSpaceNormal.xyz, worldSpaceViewDirection, worldSpaceVertexNormal, SharedData::hairSpecularSettings.HairGlossiness, uv, baseColor.xyz); + indirectDiffuseLobeWeight *= SharedData::hairSpecularSettings.DiffuseIndirectMult; indirectSpecularLobeWeightPrim *= SharedData::hairSpecularSettings.SpecularIndirectMult; indirectSpecularLobeWeightSec *= SharedData::hairSpecularSettings.SpecularIndirectMult; # endif // CS_HAIR @@ -2935,14 +2940,12 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) if (SharedData::hairSpecularSettings.Enabled) # if defined(SKYLIGHTING) { - float3 indirectSpecular = Hair::GetHairDynamicCubemapSpecularIrradiance(uv, screenUV, hairT, worldSpaceNormal, worldSpaceVertexNormal, worldSpaceViewDirection, SharedData::hairSpecularSettings.Glossiness, indirectSpecularLobeWeightPrim, indirectSpecularLobeWeightSec, skylightingSH); - indirectSpecular = Color::LinearToGamma(indirectSpecular); + float3 indirectSpecular = Hair::GetHairDynamicCubemapSpecularIrradiance(uv, screenUV, hairT, worldSpaceNormal, worldSpaceVertexNormal, worldSpaceViewDirection, SharedData::hairSpecularSettings.HairGlossiness, indirectSpecularLobeWeightPrim, indirectSpecularLobeWeightSec, skylightingSH); color.xyz += indirectSpecular; } # else { - float3 indirectSpecular = Hair::GetHairDynamicCubemapSpecularIrradiance(uv, screenUV, hairT, worldSpaceNormal, worldSpaceVertexNormal, worldSpaceViewDirection, SharedData::hairSpecularSettings.Glossiness, indirectSpecularLobeWeightPrim, indirectSpecularLobeWeightSec); - indirectSpecular = Color::LinearToGamma(indirectSpecular); + float3 indirectSpecular = Hair::GetHairDynamicCubemapSpecularIrradiance(uv, screenUV, hairT, worldSpaceNormal, worldSpaceVertexNormal, worldSpaceViewDirection, SharedData::hairSpecularSettings.HairGlossiness, indirectSpecularLobeWeightPrim, indirectSpecularLobeWeightSec); color.xyz += indirectSpecular; } # endif @@ -3234,7 +3237,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) # if defined(HAIR) && defined(CS_HAIR) if (SharedData::hairSpecularSettings.Enabled) { - outGlossiness = saturate(SharedData::hairSpecularSettings.Glossiness * 0.0075f * SSRParams.w); + outGlossiness = 1.0 - pow(abs(2.0 / (glossiness * 0.5 + 2.0)), 0.25); } # endif diff --git a/src/Features/HairSpecular.cpp b/src/Features/HairSpecular.cpp index d6a665b924..43b2545c72 100644 --- a/src/Features/HairSpecular.cpp +++ b/src/Features/HairSpecular.cpp @@ -14,18 +14,20 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT( HairSaturation, SpecularIndirectMult, DiffuseIndirectMult, - BaseColorMult) + BaseColorMult, + Transmission) void HairSpecular::DrawSettings() { ImGui::Checkbox("Enabled", (bool*)&settings.Enabled); - ImGui::SliderFloat("Glossiness", &settings.HairGlossiness, 0.0f, 100.0f, "%.0f"); + ImGui::SliderFloat("Glossiness", &settings.HairGlossiness, 0.0f, 256.0f, "%.0f"); ImGui::SliderFloat("Specular Multiplier", &settings.SpecularMult, 0.0f, 10.0f, "%.2f"); ImGui::SliderFloat("Diffuse Multiplier", &settings.DiffuseMult, 0.0f, 10.0f, "%.2f"); ImGui::SliderFloat("Indirect Specular Multiplier", &settings.SpecularIndirectMult, 0.0f, 10.0f, "%.2f"); ImGui::SliderFloat("Indirect Diffuse Multiplier", &settings.DiffuseIndirectMult, 0.0f, 10.0f, "%.2f"); ImGui::SliderFloat("Hair Base Color Multiplier", &settings.BaseColorMult, 0.0f, 10.0f, "%.2f"); ImGui::SliderFloat("Hair Saturation", &settings.HairSaturation, 0.0f, 5.0f, "%.2f"); + ImGui::SliderFloat("Transmission", &settings.Transmission, 0.0f, 1.0f, "%.2f"); ImGui::Spacing(); ImGui::Checkbox("Enable Tangent Shift", (bool*)&settings.EnableTangentShift); ImGui::SliderFloat("Primary Specular Tangent Shift", &settings.PrimaryTangentShift, -1.0f, 1.0f, "%.2f"); diff --git a/src/Features/HairSpecular.h b/src/Features/HairSpecular.h index 66791c2741..07471b61c5 100644 --- a/src/Features/HairSpecular.h +++ b/src/Features/HairSpecular.h @@ -19,9 +19,8 @@ struct HairSpecular : Feature virtual std::pair> GetFeatureSummary() override { return { - "Provides physically-based hair shading with realistic specular highlights and tangent-based light interaction for more lifelike hair appearance.", - { "Physically-based hair specular calculation", - "Configurable primary and secondary tangent shifts", + "Provides better hair shading with realistic specular highlights and tangent-based light interaction for more lifelike hair appearance.", + { "Realistic hair specular highlights", "Enhanced hair glossiness and saturation controls", "Separate specular and diffuse lighting multipliers", "Tangent shift texture support for varied hair highlights" } @@ -38,17 +37,17 @@ struct HairSpecular : Feature struct alignas(16) Settings { uint Enabled = true; - float HairGlossiness = 60.0f; + float HairGlossiness = 80.0f; float SpecularMult = 1.0f; float DiffuseMult = 1.0f; uint EnableTangentShift = true; float PrimaryTangentShift = 0.5f; float SecondaryTangentShift = -0.25f; - float HairSaturation = 1.25f; + float HairSaturation = 1.0f; float SpecularIndirectMult = 1.0f; float DiffuseIndirectMult = 1.0f; - float BaseColorMult = 1.5f; - float pad; + float BaseColorMult = 1.0f; + float Transmission = 0.5f; } settings; eastl::unique_ptr texTangentShift = nullptr;