#ifndef UTS_PBR #define UTS_PBR #define ColorSpaceDielectricSpec half4(0.22, 0.22, 0.22, 0.779) float3 GetSpecularColor(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(UtsBSDFData bsdfData, PreLightData preLightData, float3 V, float3 L) { #ifdef _PBR_MODE_OFF return 0; #else float3 specTerm; float3 N = bsdfData.normalWS; float3 H = normalize(L + V); float NdotL = dot(N, L); float NdotH = saturate(dot(N, H)); float clampedNdotV = ClampNdotV(preLightData.NdotV); float clampedNdotL = saturate(NdotL); float partLambdaV; float3 DV = 0; #ifdef _PBR_MODE_STANDARD 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_ANISOTROPY 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_HAIR 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)); DV = pow(NdotH, 5.0 * specularExponent); DV = StepFeatherToon(DV, _ToonSpecularStep, _ToonSpecularFeather); //specTerm = pow(NdotH, 5.0 * specularExponent); //specTerm = StepFeatherToon(specTerm, _ToonSpecularStep, _ToonSpecularFeather); //return specTerm * ColorSpaceDielectricSpec.rgb * clampedNdotL; #endif specTerm = DV * preLightData.specularFGD; 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 #endif