Files
com.misaki.hdrp-toon/Runtime/HDRP/Shaders/PBR.hlsl
2024-10-23 20:15:07 +09:00

190 lines
7.3 KiB
HLSL

#define ColorSpaceDielectricSpec half4(0.22, 0.22, 0.22, 0.779)
float3 schlick(float f0, float hl) {
real x = 1.0 - hl;
real x2 = x * x;
real x5 = x * x2 * x2;
return (1.0 - f0) * x5 + f0;
}
float3 SpecularColor(float3 albedo, float metalic)
{
float3 specColor = lerp(ColorSpaceDielectricSpec.rgb, albedo, metalic);
return specColor;
}
float RoughnessToBlinnPhongSpecularExponent(float roughness)
{
return clamp(2 * rcp(roughness * roughness) - 2, FLT_EPS, rcp(FLT_EPS));
}
float StepFeatherToon(float value,float step,float feather)
{
return saturate((value - step + feather) / feather);
}
float3 ComputeSpecularTerm(float3 V, float3 L, BSDFData bsdfData)
{
float3 specTerm;
#ifdef _PBR_Mode_OFF
return 0;
#else
float3 N = bsdfData.normalWS;
float3 H = normalize(L + V);
float NdotL = dot(N, L);
float NdotV = dot(N, V);
float clampedNdotV = ClampNdotV(NdotV);
float clampedNdotL = saturate(NdotL);
float flippedNdotL = ComputeWrappedDiffuseLighting(-NdotL, TRANSMISSION_WRAP_LIGHT);
float diffuseNdotL = clampedNdotL;
float LdotV = dot(L, V);
float NdotH = saturate(dot(N, H));
float HdotL = saturate(dot(H, L));
float3 F = schlick(bsdfData.fresnel0.x, HdotL);
float partLambdaV;
float3 DV = 0;
#ifdef _PBR_Mode_ST
ConvertAnisotropyToRoughness(bsdfData.perceptualRoughness, 0, bsdfData.roughnessT, bsdfData.roughnessB);
partLambdaV = GetSmithJointGGXPartLambdaV(clampedNdotV, bsdfData.roughnessT);
// We use abs(NdotL) to handle the none case of double sided
DV = DV_SmithJointGGX(NdotH, abs(NdotL), clampedNdotV, bsdfData.roughnessT, partLambdaV);
#elif _PBR_Mode_ANISO
float TdotV = dot(bsdfData.tangentWS, V);
float BdotV = dot(bsdfData.bitangentWS, V);
ConvertAnisotropyToRoughness(bsdfData.perceptualRoughness, bsdfData.anisotropy, bsdfData.roughnessT, bsdfData.roughnessB);
partLambdaV = GetSmithJointGGXAnisoPartLambdaV(TdotV, BdotV, clampedNdotV, bsdfData.roughnessT, bsdfData.roughnessB);
// For anisotropy we must not saturate these values
float TdotH = dot(bsdfData.tangentWS, H);
float TdotL = dot(bsdfData.tangentWS, L);
float BdotH = dot(bsdfData.bitangentWS, H);
float BdotL = dot(bsdfData.bitangentWS, L);
// We use abs(NdotL) to handle the none case of double sided
DV = DV_SmithJointGGXAniso(TdotH, BdotH, NdotH, clampedNdotV, TdotL, BdotL, abs(NdotL), bsdfData.roughnessT, bsdfData.roughnessB, partLambdaV);
#elif _PBR_Mode_KK
float3 t = ShiftTangent(bsdfData.bitangentWS, N, bsdfData.anisotropy);
float specularExponent = RoughnessToBlinnPhongSpecularExponent(PerceptualRoughnessToRoughness(bsdfData.coatRoughness));
DV = D_KajiyaKay(t, H, specularExponent);
float normalizeSpec = DV * rcp(specularExponent + 2) * 2 * PI;
//DV *= StepFeatherToon(normalizeSpec,specularStep,specularFeather);
DV = DV * normalizeSpec * _KKColor.rgb;
#elif _PBR_Mode_TOON
float specularExponent = RoughnessToBlinnPhongSpecularExponent(PerceptualRoughnessToRoughness(bsdfData.perceptualRoughness));
specTerm = pow(NdotH, 5.0 * specularExponent);
specTerm = StepFeatherToon(specTerm, _ToonSpecularStep, _ToonSpecularFeather);
return specTerm * ColorSpaceDielectricSpec.rgb * clampedNdotL;
#endif
specTerm = DV * F;
specTerm = specTerm * clampedNdotL;
return specTerm;
#endif
}
half3 FitWithCurveApprox(half NdotL, half Curvature)
{
half curva = (1.0 / mad(Curvature, 0.5 - 0.0625, 0.0625) - 2.0) / (16.0 - 2.0);
half oneMinusCurva = 1.0 - curva;
half3 curve0;
{
half3 rangeMin = half3(0.0, 0.3, 0.3);
half3 rangeMax = half3(1.0, 0.7, 0.7);
half3 offset = half3(0.0, 0.06, 0.06);
half3 t = saturate(mad(NdotL, 1.0 / (rangeMax - rangeMin), (offset + rangeMin) / (rangeMin - rangeMax)));
half3 lowerLine = (t * t) * half3(0.65, 0.5, 0.9);
lowerLine.r += 0.045;
lowerLine.b *= t.b;
half3 m = half3(1.75, 2.0, 1.97);
half3 upperLine = mad(NdotL, m, half3(0.99, 0.99, 0.99) - m);
upperLine = saturate(upperLine);
half3 lerpMin = half3(0.0, 0.35, 0.35);
half3 lerpMax = half3(1.0, 0.7, 0.6);
half3 lerpT = saturate(mad(NdotL, 1.0 / (lerpMax - lerpMin), lerpMin / (lerpMin - lerpMax)));
curve0 = lerp(lowerLine, upperLine, lerpT * lerpT);
}
half3 curve1;
{
half3 m = half3(1.95, 2.0, 2.0);
half3 upperLine = mad(NdotL, m, half3(0.99, 0.99, 1.0) - m);
curve1 = saturate(upperLine);
}
float oneMinusCurva2 = oneMinusCurva * oneMinusCurva;
return lerp(curve0, curve1, mad(oneMinusCurva2, -1.0 * oneMinusCurva2, 1.0));
}
// Todo: SDF nose high light
// #if define(_SDFShadow) || define(_SDFNoiseHelight)
#ifdef _SDFShadow
// Use main light XZ direction & world Left/Forward Vector to sample character face SDF
void SDFSample(out float4 lSDF_Tex, out float4 rSDF_Tex, out float2 leftVector, out float2 forwardVector, float2 UV)
{
lSDF_Tex = SAMPLE_TEXTURE2D(_SDFShadowTex, sampler_SDFShadowTex, UV);
float2 right_uv = float2(1 - UV.x, UV.y);
rSDF_Tex = SAMPLE_TEXTURE2D(_SDFShadowTex, sampler_SDFShadowTex, right_uv);
leftVector = normalize(mul(UNITY_MATRIX_M, float4(1, 0, 0, 0)).xz);
forwardVector = normalize(mul(UNITY_MATRIX_M, float4(0, 0, 1, 0)).xz);
}
// Return 1 -> right side
bool SDFPickSide(out float angle, float2 Left, float2 Front, float2 lightDir)
{
// Remap [-1,1] tp [0,1] | 0 <- Face Toward Light ---- Back Toward Light -> 1
angle = 1- clamp(0 , 1, dot(Front, lightDir) * 0.5 + 0.5);
// Pick side
return dot(lightDir,Left) > 0;
}
// Output: readjusted angle between light and pixel facing direction (Represented by a projection length) [Out Param, angle]
// Output: SDF Texel Color [Return Value]
float4 SDFResult(inout bool rightside, out float angle, float3 L, float2 UV)
{
float4 left_SDFTex;
float4 right_SDFTex;
float2 Left;
float2 Front;
SDFSample(left_SDFTex, right_SDFTex, Left, Front, UV);
float2 light_Dir = normalize(L.xz);
rightside = SDFPickSide(angle, Left, Front, light_Dir);
return rightside ? right_SDFTex : left_SDFTex;
}
float SDFMask(float angle, float tex_direct)
{
float smoothGamma = _SDFSmoothGamma / 10.0f;
float shadowLevel = _SDFShadowLevel / 10.0f;
float SDFMask = smoothstep(tex_direct - smoothGamma, tex_direct + smoothGamma, angle - shadowLevel);
return SDFMask;
}
float SDFNoseHighlight(float angle,float tex_value, bool rightside, float2 UV)
{
// REF: https://zhuanlan.zhihu.com/p/411188212 3.2.1
float highlightValue = 0;
//float cutU = step(0.5, UV.x);
float cutU = UV.x;
float uvMask=lerp(cutU, 1 - cutU, rightside);//discard half of the sdf we sampled (Only one side of highlight wanted)
float lightAtten = pow(max(0, angle - (_SDFShadowLevel / 10.0f)), 0.8);
return smoothstep(lightAtten-_SDFNoseHighlightSmoothRange,lightAtten+_SDFNoseHighlightSmoothRange , uvMask * tex_value) * tex_value; // Safeguard, return 0 when tex_value = 0
}
#endif