258 lines
12 KiB
HLSL
258 lines
12 KiB
HLSL
#ifndef UTS_MATERIAL_EVALUATION
|
|
#define UTS_MATERIAL_EVALUATION
|
|
|
|
#define ColorSpaceDielectricSpec half4(0.22, 0.22, 0.22, 0.779)
|
|
|
|
struct UtsShadeMask
|
|
{
|
|
float baseShadeMask;
|
|
float firstShadeMask;
|
|
};
|
|
|
|
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));
|
|
}
|
|
|
|
float3 SampleSDFTexture(float3 L, float2 uv, out float angle)
|
|
{
|
|
float2 right_uv = float2(1 - uv.x, uv.y);
|
|
float3 left_SDFTex = SAMPLE_TEXTURE2D(_SDFShadingMap, sampler_SDFShadingMap, uv).rgb;
|
|
float3 right_SDFTex = SAMPLE_TEXTURE2D(_SDFShadingMap, sampler_SDFShadingMap, right_uv).rgb;
|
|
|
|
float2 leftVector = normalize(mul(UNITY_MATRIX_M, float4(1.0, 0.0, 0.0, 0.0)).xz);
|
|
float2 forwardVector = normalize(mul(UNITY_MATRIX_M, float4(0.0, 0.0, 1.0, 0.0)).xz);
|
|
|
|
float2 lightDirection = normalize(L.xz);
|
|
angle = saturate(dot(forwardVector, lightDirection) * -1.0 + _SDFShadowLevel);
|
|
bool isRightSide = dot(lightDirection, leftVector) > 0;
|
|
|
|
return isRightSide ? right_SDFTex : left_SDFTex;
|
|
}
|
|
|
|
float GetHairShadow(PositionInputs posInput, float3 L)
|
|
{
|
|
float shadow = 1.0;
|
|
|
|
// Push the face fragment view space position towards the light for a little bit
|
|
float hairShadowOpacity = saturate(Remap(length(posInput.positionWS), float2(_HairShadowFadeOutDistance, _HairShadowFadeInDistance), float2(0, 1)));
|
|
|
|
if (hairShadowOpacity > 0.0)
|
|
{
|
|
float3 viewLightDir = TransformWorldToViewDir(L); // / posInput.deviceDepth; when linearDepth grows large, the movement amount should be lower since we are getting further from the face.
|
|
float3 cameraDirOS = normalize(TransformWorldToObject(GetCameraPositionWS()));
|
|
float shadowLengthY = _HairShadowDistance * 5.0 * max(0.5, posInput.linearDepth * _HairShadowDistanceScaleFactor) / posInput.linearDepth;
|
|
float2 shadowLength = float2(shadowLengthY * 2.0f, shadowLengthY);
|
|
|
|
float3 camDirOS = normalize(TransformWorldToObject(GetCameraPositionWS()));
|
|
float camDirFactor = 1 - smoothstep(0.1, 0.9, camDirOS.y);
|
|
shadowLength.y *= camDirFactor;
|
|
|
|
float2 samplingPoint = (posInput.positionSS + shadowLength * viewLightDir.xy * (_ScreenSize.xy / float2(1920.0f, 1080.0f))) * _ScreenSize.zw; // Use 1080p as the reference resolution to achieve consistent shadow lengths across various screen resolutions.
|
|
|
|
// Then sample the hair buffer, to see if the fragment lands in shadow.
|
|
float2 scaledUVs = samplingPoint * _HairShadowRTHandleScale.xy; // We have to including the scaling factor for our shadow map since we are not going to allocate new texture if the rendering resolution changed.
|
|
float hairDepth = SAMPLE_TEXTURE2D(_HairShadowTex, s_trilinear_clamp_sampler, scaledUVs).r;
|
|
float shadowMask = posInput.deviceDepth <= hairDepth + _HairShadowDepthBias ? 1 : 0; // Hair < Face means Hair are closer to camera
|
|
// Note that we have LinearEyeDepth in the buffer. A comparison of depth is needed so that we don't project the shadow of hair behind the face.
|
|
shadow = lerp(1, 1.0 - hairShadowOpacity, shadowMask);
|
|
}
|
|
|
|
return shadow;
|
|
}
|
|
|
|
DirectLighting UtsShadeSurface(PositionInputs posInput, UtsBSDFData bsdfData, PreLightData preLightData, SHADOW_TYPE shadow,
|
|
float3 lightColor, float3 V, float3 L, float2 uv,
|
|
float diffuseDimmer, float specularDimmer)
|
|
{
|
|
DirectLighting lighting;
|
|
ZERO_INITIALIZE(DirectLighting, lighting);
|
|
|
|
if (Max3(lightColor.r, lightColor.g, lightColor.b) > 0.0)
|
|
{
|
|
shadow = smoothstep(0.4, 0.6, shadow);
|
|
#if _RECEIVE_HAIR_SHADOW_ON && ENABLE_UTS_HAIR_SHAOW
|
|
shadow *= GetHairShadow(posInput, L);
|
|
#endif
|
|
|
|
#if _SHADING_MODE_SDF
|
|
float angle;
|
|
float3 sdfTexture = SampleSDFTexture(L, uv, angle); // r: sdf shadow, g: sdf highlight, b: fixed shadow
|
|
float shadowSmoothLevel = _SDFShadowSmoothLevel / 10.0;
|
|
float sdfShadowMask = smoothstep(angle - shadowSmoothLevel, angle + shadowSmoothLevel, sdfTexture.r);
|
|
float sdfHighlight = sdfTexture.g * _SDFHighlightStrength;
|
|
#endif
|
|
|
|
float3 diffuseTerm = 0.0;
|
|
float3 specularTerm = ComputeSpecularTerm(bsdfData, preLightData, V, L);
|
|
#if _USE_SHADING_RAMP_MAP_ON
|
|
#if _SHADING_MODE_STANDARD
|
|
float NdotL = dot(bsdfData.normalWS, L);
|
|
float halfLambert = 0.5 * NdotL + 0.5;
|
|
|
|
float3 rampColor = SAMPLE_TEXTURE2D_ARRAY_LOD(_ShadingRampMap, s_linear_clamp_sampler, float2(halfLambert * shadow.x, 0.0), _ShadingIndex, 0.0).rgb;
|
|
|
|
diffuseTerm = bsdfData.diffuseColor * rampColor;
|
|
specularTerm *= saturate(NdotL) * shadow;
|
|
#elif _SHADING_MODE_SDF
|
|
float3 rampColor = SAMPLE_TEXTURE2D_ARRAY_LOD(_ShadingRampMap, s_linear_clamp_sampler, float2(sdfShadowMask * shadow.x, 0.0), _ShadingIndex, 0.0).rgb;
|
|
|
|
diffuseTerm = bsdfData.diffuseColor * rampColor;
|
|
specularTerm = (specularTerm + sdfHighlight) * sdfShadowMask * shadow;
|
|
#endif
|
|
#else
|
|
#if _SHADING_MODE_STANDARD
|
|
float NdotL = dot(bsdfData.normalWS, L);
|
|
float halfLambert = 0.5 * NdotL + 0.5;
|
|
|
|
float firstColorFeatherForMask = lerp(_1stShadeColorFeather, 0.0, max(_ComposerMaskMode, _FirstShadeOverridden));
|
|
float baseShadeMask = saturate((halfLambert - (_1stShadeColorStep - firstColorFeatherForMask)) / (_1stShadeColorStep - (_1stShadeColorStep - firstColorFeatherForMask)));
|
|
baseShadeMask *= shadow;
|
|
|
|
float secondColorFeatherForMask = lerp(_2ndShadeColorFeather, 0.0, max(_SecondShadeOverridden, _ComposerMaskMode));
|
|
float firstShadeMask = saturate((halfLambert - (_2ndShadeColorStep - secondColorFeatherForMask)) / (_2ndShadeColorStep - (_2ndShadeColorStep - secondColorFeatherForMask)));
|
|
|
|
diffuseTerm = lerp(lerp(bsdfData.secondShadingDiffuseColor, bsdfData.firstShadingDiffuseColor, firstShadeMask), bsdfData.diffuseColor, baseShadeMask);
|
|
specularTerm *= baseShadeMask;
|
|
#elif _SHADING_MODE_SDF
|
|
float shadeMask = sdfShadowMask * sdfTexture.b * shadow;
|
|
|
|
diffuseTerm = lerp(bsdfData.firstShadingDiffuseColor, bsdfData.diffuseColor, shadeMask);
|
|
specularTerm = (specularTerm + sdfHighlight) * shadeMask;
|
|
#endif
|
|
#endif
|
|
|
|
lighting.diffuse += diffuseTerm * lightColor * diffuseDimmer;
|
|
lighting.specular += specularTerm * lightColor * specularDimmer;
|
|
}
|
|
|
|
return lighting;
|
|
}
|
|
|
|
DirectLighting UtsEvaluateAngelRing(FragInputs input, float3 normalWS, float3 V)
|
|
{
|
|
DirectLighting lighting;
|
|
ZERO_INITIALIZE(DirectLighting, lighting);
|
|
|
|
// Should we scroll the angel ring texture on x?
|
|
float3 cameraRight = UNITY_MATRIX_V[0].xyz;
|
|
float3 cameraFront = UNITY_MATRIX_V[2].xyz;
|
|
float3 upVector = float3(0, 1, 0);
|
|
float3 rightAxis = cross(cameraFront, upVector);
|
|
float cameraRightMagnitude = sqrt(cameraRight.x * cameraRight.x + cameraRight.y * cameraRight.y + cameraRight.z * cameraRight.z);
|
|
float rightAxisMagnitude = sqrt(rightAxis.x * rightAxis.x + rightAxis.y * rightAxis.y + rightAxis.z * rightAxis.z);
|
|
float cameraRollCos = dot(rightAxis, cameraRight) / (rightAxisMagnitude * cameraRightMagnitude);
|
|
float3 cameraRoll = acos(clamp(cameraRollCos, -1.0, 1.0));
|
|
float cameraDir = cameraRight.y < 0 ? -1.0 : 1.0;
|
|
|
|
float2 arOffsetU = lerp(mul(UNITY_MATRIX_V, float4(normalWS, 0)).xyz, float3(0, 0, 1), _AngelRingOffsetU).xy;
|
|
arOffsetU = arOffsetU * 0.5 + 0.5;
|
|
float2 arvnRotate = RotateUV(arOffsetU, -(cameraDir * cameraRoll), 0.5, 1.0);
|
|
float2 arOffsetUV = float2(arvnRotate.x, lerp(input.texCoord0.y, arvnRotate.y, _AngelRingOffsetV));
|
|
float4 angelRingColor = SAMPLE_TEXTURE2D(_AngelRingColorMap, sampler_AngelRingColorMap, TRANSFORM_TEX(arOffsetUV, _AngelRingColorMap)) * _AngelRingColor * _AngelRingIntensity;
|
|
|
|
float weight = saturate(dot(normalize(V), normalWS));
|
|
|
|
lighting.specular += angelRingColor.r * angelRingColor.a * weight;
|
|
|
|
return lighting;
|
|
}
|
|
|
|
#endif |