Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 18 additions & 19 deletions features/Hair Specular/Shaders/Hair/Hair.hlsli
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ 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 VN, float3 lightColor, float shininess, float selfShadow, float2 uv, float3 baseColor)
void GetHairDirectLightScheuermann(out float3 dirDiffuse, out float3 dirSpecular, out float3 dirTransmission, float3 T, float3 L, float3 V, float3 N, float3 VN, float3 lightColor, float shininess, float selfShadow, float2 uv, float3 baseColor)
{
lightColor *= selfShadow;
const float3 H = normalize(L + V);
Expand All @@ -75,7 +75,7 @@ namespace Hair
// 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;
dirDiffuse = saturate(scatterColor + NdotL) * dirDiffuse * lightColor * SharedData::hairSpecularSettings.DiffuseMult;

float3 TshiftPrimary;
float3 TshiftSecondary;
Expand All @@ -96,8 +96,8 @@ namespace Hair
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;
dirSpecular = specTerm * lightColor;
dirSpecular = specR * lightColor * SharedData::hairSpecularSettings.SpecularMult;
dirTransmission = specT * lightColor * SharedData::hairSpecularSettings.SpecularMult;
}

float Hair_g(float B, float Theta)
Expand Down Expand Up @@ -185,18 +185,19 @@ namespace Hair
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);
float luma = Color::RGBToLuminance2(baseColor);
float3 scatterTint = shadow < 1 ? pow(abs(baseColor / luma), 1 - shadow) : 1;
S += sqrt(baseColor) * diffuseScatter * scatterTint;

return max(S, 0);
}

void GetHairDirectLightMarschner(out float3 dirDiffuse, out float3 dirSpecular, float3 T, float3 L, float3 V, float3 N, float3 VN, float3 lightColor, float shininess, float selfShadow, float2 uv, float3 baseColor)
void GetHairDirectLightMarschner(out float3 dirDiffuse, out float3 dirSpecular, out float3 dirTransmission, float3 T, float3 L, float3 V, float3 N, float3 VN, float3 lightColor, float shininess, float selfShadow, float2 uv, float3 baseColor)
{
lightColor *= HAIR_LIGHTING_MULTIPLIER * selfShadow;
dirDiffuse = 0;
dirSpecular = 0;
dirTransmission = 0;
const float roughness = 1 - saturate(shininess * 0.01);

if (SharedData::hairSpecularSettings.EnableTangentShift) {
Expand All @@ -208,17 +209,16 @@ namespace Hair

float backlit = SharedData::hairSpecularSettings.Transmission;

dirSpecular += D_Marschner(L, V, T, roughness, baseColor, 0, backlit) * lightColor;
dirDiffuse += GetHairDiffuseAttenuationKajiyaKay(T, V, L, 0, baseColor) * lightColor;
dirSpecular += D_Marschner(L, V, T, roughness, baseColor, 0, backlit) * lightColor * SharedData::hairSpecularSettings.SpecularMult;
dirTransmission += GetHairDiffuseAttenuationKajiyaKay(T, V, L, selfShadow, baseColor) * lightColor * SharedData::hairSpecularSettings.DiffuseMult;
}

void GetHairDirectLight(out float3 dirDiffuse, out float3 dirSpecular, float3 T, float3 L, float3 V, float3 N, float3 VN, float3 lightColor, float shininess, float selfShadow, float2 uv, float3 baseColor)
void GetHairDirectLight(out float3 dirDiffuse, out float3 dirSpecular, out float3 dirTransmission, float3 T, float3 L, float3 V, float3 N, float3 VN, float3 lightColor, float shininess, float selfShadow, float2 uv, float3 baseColor)
{
if (SharedData::hairSpecularSettings.HairMode == 0) {
GetHairDirectLightScheuermann(dirDiffuse, dirSpecular, T, L, V, N, VN, lightColor, shininess, selfShadow, uv, baseColor);
GetHairDirectLightScheuermann(dirDiffuse, dirSpecular, dirTransmission, T, L, V, N, VN, lightColor, shininess, selfShadow, uv, baseColor);
} else {
GetHairDirectLightMarschner(dirDiffuse, dirSpecular, T, L, V, N, VN, lightColor, shininess, selfShadow, uv, baseColor);
dirDiffuse = Color::LinearToGamma(dirDiffuse);
GetHairDirectLightMarschner(dirDiffuse, dirSpecular, dirTransmission, T, L, V, N, VN, lightColor, shininess, selfShadow, uv, baseColor);
dirSpecular = Color::LinearToGamma(dirSpecular);
}
}
Expand All @@ -233,17 +233,16 @@ namespace Hair
specularLobeWeightPrimary = 0;
specularLobeWeightSecondary = 0;
float3 L = normalize(V - N * dot(V, N));
// float NdotL = dot(N, L);
// float VdotL = dot(V, L);

if (SharedData::hairSpecularSettings.EnableTangentShift) {
const float shift = TexTangentShift.SampleLevel(SampColorSampler, uv, 0).x - 0.5;
T = ShiftTangent(T, N, shift);
}

diffuseLobeWeight = D_Marschner(L, V, T, roughnessPrimary, baseColor, 0.2, 0);
diffuseLobeWeight += GetHairDiffuseAttenuationKajiyaKay(T, V, L, 0, baseColor);
specularLobeWeightPrimary = D_Marschner(L, V, T, roughnessPrimary, baseColor, 0.2, 0) * Math::PI;
diffuseLobeWeight = GetHairDiffuseAttenuationKajiyaKay(T, V, L, 1, baseColor) * Math::PI;
diffuseLobeWeight = Color::LinearToGamma(diffuseLobeWeight);
specularLobeWeightPrimary = Color::LinearToGamma(specularLobeWeightPrimary);
return;
} else {
float NdotVshifted = NdotV;
Expand Down Expand Up @@ -334,12 +333,12 @@ namespace Hair
float3 N1 = N;
float3 N2 = N;

const float roughnessPrimary = pow(abs(2.0 / (glossiness + 2.0)), 0.25);
const float roughnessPrimary = SharedData::hairSpecularSettings.HairMode == 1 ? 1.0 : 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, 0).x - 0.5;
N1 = ShiftNormal(T, N, shift + SharedData::hairSpecularSettings.PrimaryTangentShift);
N1 = ShiftNormal(T, N, shift + (SharedData::hairSpecularSettings.HairMode == 1 ? 0.0 : SharedData::hairSpecularSettings.PrimaryTangentShift));
N2 = ShiftNormal(T, N, shift + SharedData::hairSpecularSettings.SecondaryTangentShift);
}

Expand Down
25 changes: 14 additions & 11 deletions package/Shaders/Lighting.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -2076,6 +2076,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace)
baseColor.xyz *= hairTint;
baseColor.xyz = Hair::Saturation(baseColor.xyz, SharedData::hairSpecularSettings.HairSaturation);
baseColor.xyz *= SharedData::hairSpecularSettings.BaseColorMult;
baseColor.xyz = SharedData::hairSpecularSettings.HairMode == 1 ? baseColor.xyz * baseColor.xyz : baseColor.xyz; // To match color for Marschner
}

float3 sampledHairFlow = 0;
Expand Down Expand Up @@ -2274,6 +2275,8 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace)
float3 shiftedNormal = Hair::ShiftWorldNormal(hairT, worldSpaceNormal, 0, uv);
screenSpaceNormal = normalize(FrameBuffer::WorldToView(shiftedNormal, false, eyeIndex));
}

float3 transmissionColor = 0;
# endif

# if defined(TRUE_PBR)
Expand Down Expand Up @@ -2632,8 +2635,10 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace)
} else {
# if defined(HAIR) && defined(CS_HAIR)
if (SharedData::hairSpecularSettings.Enabled) {
float3 dirTransmissionColor = 0.0;
float hairShadow = Hair::HairSelfShadow(input.WorldPosition.xyz, DirLightDirection, screenNoise, eyeIndex);
Hair::GetHairDirectLight(dirDiffuseColor, lightsSpecularColor, hairT, DirLightDirection, viewDirection, modelNormal.xyz, worldSpaceVertexNormal.xyz, dirLightColor.xyz * dirDetailShadow, SharedData::hairSpecularSettings.HairGlossiness, hairShadow, uv, baseColor.xyz);
Hair::GetHairDirectLight(dirDiffuseColor, lightsSpecularColor, dirTransmissionColor, hairT, DirLightDirection, viewDirection, modelNormal.xyz, worldSpaceVertexNormal.xyz, dirLightColor.xyz * dirDetailShadow, SharedData::hairSpecularSettings.HairGlossiness, hairShadow, uv, baseColor.xyz);
transmissionColor += dirTransmissionColor;
}
else {
# if defined(SPECULAR)
Expand Down Expand Up @@ -2712,9 +2717,11 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace)
# if defined(HAIR) && defined(CS_HAIR)
if (SharedData::hairSpecularSettings.Enabled) {
float3 lightSpecularColor = 0;
float3 lightTransmissionColor = 0;
float hairShadow = Hair::HairSelfShadow(input.WorldPosition.xyz, normalizedLightDirection, screenNoise, eyeIndex);
Hair::GetHairDirectLight(lightDiffuseColor, lightSpecularColor, hairT, normalizedLightDirection, viewDirection, modelNormal.xyz, worldSpaceVertexNormal.xyz, lightColor, SharedData::hairSpecularSettings.HairGlossiness, hairShadow, uv, baseColor.xyz);
Hair::GetHairDirectLight(lightDiffuseColor, lightSpecularColor, lightTransmissionColor, hairT, normalizedLightDirection, viewDirection, modelNormal.xyz, worldSpaceVertexNormal.xyz, lightColor, SharedData::hairSpecularSettings.HairGlossiness, hairShadow, uv, baseColor.xyz);
lightsSpecularColor += lightSpecularColor;
transmissionColor += lightTransmissionColor;
} else {
# if defined(SPECULAR)
lightsSpecularColor += GetLightSpecularInput(input, normalizedLightDirection, viewDirection, modelNormal.xyz, lightColor, shininess, uv);
Expand Down Expand Up @@ -2876,8 +2883,10 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace)
if (SharedData::hairSpecularSettings.Enabled) {
float hairShadow = Hair::HairSelfShadow(input.WorldPosition.xyz, normalizedLightDirection, screenNoise, eyeIndex);
float3 lightSpecularColor = 0;
Hair::GetHairDirectLight(lightDiffuseColor, lightSpecularColor, hairT, normalizedLightDirection, viewDirection, modelNormal.xyz, worldSpaceVertexNormal.xyz, lightColor * contactShadow, SharedData::hairSpecularSettings.HairGlossiness, hairShadow, uv, baseColor.xyz);
float3 lightTransmissionColor = 0;
Hair::GetHairDirectLight(lightDiffuseColor, lightSpecularColor, lightTransmissionColor, hairT, normalizedLightDirection, viewDirection, modelNormal.xyz, worldSpaceVertexNormal.xyz, lightColor * contactShadow, SharedData::hairSpecularSettings.HairGlossiness, hairShadow, uv, baseColor.xyz);
lightsSpecularColor += lightSpecularColor;
transmissionColor += lightTransmissionColor;
} else {
# if defined(SPECULAR)
lightsSpecularColor += GetLightSpecularInput(input, normalizedLightDirection, worldSpaceViewDirection, worldSpaceNormal.xyz, lightColor, shininess, uv);
Expand All @@ -2901,13 +2910,6 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace)
diffuseColor += lightsDiffuseColor;
specularColor += lightsSpecularColor;

# if defined(HAIR) && defined(CS_HAIR)
if (SharedData::hairSpecularSettings.Enabled) {
diffuseColor *= SharedData::hairSpecularSettings.DiffuseMult;
specularColor *= baseColor.w * SharedData::hairSpecularSettings.SpecularMult;
}
# endif

# if !defined(LANDSCAPE)
if (Permutation::PixelShaderDescriptor & Permutation::LightingFlags::CharacterLight) {
float charLightMul = saturate(dot(worldSpaceViewDirection, worldSpaceNormal.xyz)) * CharacterLightParams.x + CharacterLightParams.y * saturate(dot(float2(0.164398998, -0.986393988), worldSpaceNormal.yz));
Expand Down Expand Up @@ -3205,6 +3207,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace)
# else
color.xyz += indirectDiffuseLobeWeight * directionalAmbientColor;
# endif
color.xyz += transmissionColor;
}
# else
color.xyz += diffuseColor * baseColor.xyz;
Expand Down Expand Up @@ -3513,7 +3516,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace)

# if defined(HAIR) && defined(CS_HAIR)
if (SharedData::hairSpecularSettings.Enabled) {
outGlossiness = 1.0 - pow(abs(2.0 / (glossiness * 0.5 + 2.0)), 0.25);
outGlossiness = 1.0 - (SharedData::hairSpecularSettings.HairMode == 1 ? 1.0 : pow(abs(2.0 / (glossiness * 0.5 + 2.0)), 0.25));
}
# endif

Expand Down
3 changes: 1 addition & 2 deletions src/Features/HairSpecular.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ void HairSpecular::DrawSettings()
"Kajiya-Kay is an empirical model that simulates hair specular highlights.\n"
"Marschner is a more physically-based model that simulates hair light interaction.\n"
"Both models are anisotropic and support tangent-based shading.\n"
"Note that colors in Marschner mode may appear darker and more saturated.\n"
"Also, without self-shadowing enabled, Marschner mode may look overly bright because of transmission.\n");
"Without self-shadowing, Marschner may look overly bright because of transmission.\n");
}
ImGui::Spacing();
ImGui::SliderFloat("Glossiness", &settings.HairGlossiness, 0.0f, settings.HairMode == 0 ? 256.0f : 100.0f, "%.0f");
Expand Down
2 changes: 1 addition & 1 deletion src/Features/HairSpecular.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ struct HairSpecular : Feature
uint EnableSelfShadow = true;
float SelfShadowStrength = 1.0f;
float SelfShadowExponent = 0.1f;
float SelfShadowScale = 5.0f;
float SelfShadowScale = 2.5f;
uint HairMode = 0; // 0: Kajiya-Kay, 1: Marschner
uint pad[3];
} settings;
Expand Down