Added new fabirc pbr mode;

Added new stocking surface feature;

Fixed the issue that diffuse bsdf is not energy conserving.
Fixed the bug that shader can not render alpha clip properly;
This commit is contained in:
2025-05-15 16:07:54 +09:00
parent d19322b768
commit 35dc7b15a6
21 changed files with 234 additions and 103 deletions

View File

@@ -50,6 +50,57 @@ float3 UtsComputeDiffuseColor(float3 baseColor, float metallic)
return UtsComputeDiffuseColor(baseColor, metallic, 0.0);
}
float Random(float2 uv)
{
return frac(sin(dot(uv, float2(12.9898, 78.233))) * 43758.5453);
}
float unity_noise_interpolate (float a, float b, float t)
{
return (1.0-t)*a + (t*b);
}
float ValueNoise (float2 uv)
{
float2 i = floor(uv);
float2 f = frac(uv);
f = f * f * (3.0 - 2.0 * f);
uv = abs(frac(uv) - 0.5);
float2 c0 = i + float2(0.0, 0.0);
float2 c1 = i + float2(1.0, 0.0);
float2 c2 = i + float2(0.0, 1.0);
float2 c3 = i + float2(1.0, 1.0);
float r0 = Random(c0);
float r1 = Random(c1);
float r2 = Random(c2);
float r3 = Random(c3);
float bottomOfGrid = unity_noise_interpolate(r0, r1, f.x);
float topOfGrid = unity_noise_interpolate(r2, r3, f.x);
float t = unity_noise_interpolate(bottomOfGrid, topOfGrid, f.y);
return t;
}
float SimpleNoise(float2 UV, float Scale)
{
float t = 0.0;
float freq = pow(2.0, float(0));
float amp = pow(0.5, float(3-0));
t += ValueNoise(float2(UV.x*Scale/freq, UV.y*Scale/freq))*amp;
freq = pow(2.0, float(1));
amp = pow(0.5, float(3-1));
t += ValueNoise(float2(UV.x*Scale/freq, UV.y*Scale/freq))*amp;
freq = pow(2.0, float(2));
amp = pow(0.5, float(3-2));
t += ValueNoise(float2(UV.x*Scale/freq, UV.y*Scale/freq))*amp;
return t;
}
#define SampleRampSignalLine(texture, u) (SAMPLE_TEXTURE2D_LOD(texture, s_linear_clamp_sampler, float2(u, 0.5), 0))
// Exposure
@@ -243,4 +294,4 @@ float3 GetWorldPosFromDepthBuffer(float2 clipPos01, float cameraDepth)
return mul(unity_CameraToWorld, float4(localInvertDepthDirHD, 1.0)).xyz;
}
#endif
#endif

View File

@@ -295,7 +295,7 @@ void UtsGetSurfaceAndBuiltinData(FragInputs input, float3 V, inout PositionInput
#else
float alphaCutoff = _AlphaCutoff;
#endif
// clip(-0.1);
GENERIC_ALPHA_TEST(alphaValue, alphaCutoff);
#endif

View File

@@ -1,6 +1,22 @@
#ifndef UTS_SURFACE_FEATURE_EVALUATION
#define UTS_SURFACE_FEATURE_EVALUATION
void UtsEvaluateLighting_Stocking(FragInputs input, PositionInputs posInput, float3 normalWS, float3 V, inout AggregateLighting aggregateLighting)
{
float NdotV = saturate(dot(normalize(V), normalWS));
NdotV = pow(NdotV, 2.0);
// TODO: Move sparkle to bsdf evaluation?
// float sparkle = Random(posInput.positionNDC.xy);
// sparkle = step(0.995, sparkle);
// float noise = SimpleNoise(posInput.positionNDC.xy, 500.0);
// sparkle = noise < sparkle ? 1.0 : 0.0;
aggregateLighting.direct.diffuse *= NdotV;
// aggregateLighting.direct.specular = saturate(aggregateLighting.direct.specular + sparkle * (1.0 - NdotV) * 0.5);
}
DirectLighting UtsEvaluateLighting_RimLight(PositionInputs posInput, UtsBSDFData bsdfData, PreLightData preLightData
#if _LIGHT_BASE_RIM_LIGHT_ON
, float3 L, float3 lightColor
@@ -9,9 +25,10 @@ DirectLighting UtsEvaluateLighting_RimLight(PositionInputs posInput, UtsBSDFData
{
DirectLighting lighting;
ZERO_INITIALIZE(DirectLighting, lighting);
float3 rimLightColor = _RimLightColor.rgb * _RimLightIntensity;
rimLightColor = lerp(rimLightColor, rimLightColor * bsdfData.diffuseColor.rgb, _AlbedoAffectRimLight * _RimLightColor.a);
#if _SCREEN_SPACE_RIM_LIGHT_ON
float3 normalVS = normalize(mul((float3x3)UNITY_MATRIX_V, bsdfData.geomNormalWS));
float2 depthUV = posInput.positionNDC.xy + normalVS.xy * (_RimLightLevel * 0.05 / posInput.linearDepth);
@@ -24,17 +41,17 @@ DirectLighting UtsEvaluateLighting_RimLight(PositionInputs posInput, UtsBSDFData
float rimLightMask = pow(1.0 - clampNdotV, exp2(lerp(3.0, 0.0, _RimLightLevel)));
rimLightMask = lerp(rimLightMask, step(_RimLightClippingLevel, rimLightMask), _RimLightClipping);
#endif
#if _LIGHT_BASE_RIM_LIGHT_ON
float halfLambert = 0.5 * dot(bsdfData.normalWS, L) + 0.5;
float lightBaseMask = saturate(smoothstep(_LightDirectionRimLightLevel, 1.0, halfLambert));
rimLightMask *= lightBaseMask;
rimLightColor *= lightColor;
#endif
lighting.diffuse = rimLightMask * rimLightColor;
return lighting;
}
@@ -42,7 +59,7 @@ DirectLighting UtsEvaluateLighting_AngelRing(FragInputs input, float3 normalWS,
{
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;
@@ -53,18 +70,18 @@ DirectLighting UtsEvaluateLighting_AngelRing(FragInputs input, float3 normalWS,
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).x, 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
#endif

View File

@@ -53,6 +53,17 @@ bool UtsUseScreenSpaceShadow(DirectionalLightData light, float3 normalWS)
#endif
}
bool IsNonZeroBSDF(float3 L, UtsBSDFData bsdfData)
{
#if _MATERIAL_TYPE_FACE
return true;
#else
float NdotL = dot(bsdfData.normalWS, L);
return NdotL > 0.0;
#endif
}
void UtsLightLoop(FragInputs fragInputs, PositionInputs posInput, UtsBSDFData bsdfData, BuiltinData builtinData,
float3 V, uint featureFlags, out LightLoopOutput lightLoopOutput)
{
@@ -87,6 +98,7 @@ void UtsLightLoop(FragInputs fragInputs, PositionInputs posInput, UtsBSDFData bs
float3 L = -light.forward;
// Is it worth sampling the shadow map?
// Should we skip it if NdotL is negative? (i.e. transmission)
if ((light.lightDimmer > 0) && (light.shadowDimmer > 0))
{
context.shadowValue = GetDirectionalShadowAttenuation(context.shadowContext,
@@ -345,6 +357,11 @@ void UtsLightLoop(FragInputs fragInputs, PositionInputs posInput, UtsBSDFData bs
#endif
}
if (HasFlag(bsdfData.surfaceFeatures, SURFACEFEATURE_STOCKING))
{
UtsEvaluateLighting_Stocking(fragInputs, posInput, bsdfData.normalWS, V, aggregateLighting);
}
#ifndef _LIGHT_BASE_RIM_LIGHT_ON
if (HasFlag(bsdfData.surfaceFeatures, SURFACEFEATURE_RIM_LIGHT))
{

View File

@@ -57,8 +57,14 @@ float3 ComputeSpecularTerm(UtsBSDFData bsdfData, PreLightData preLightData, floa
float specularExponent = RoughnessToBlinnPhongSpecularExponent(PerceptualRoughnessToRoughness(bsdfData.perceptualRoughness));
DV = D_KajiyaKay(t, H, specularExponent);
float normalizeSpec = DV * rcp(specularExponent + 2) * 2 * PI;
float normalizeSpec = DV * rcp(specularExponent + 2.0) * TWO_PI;
DV = DV * normalizeSpec * _KKColor.rgb;
#elif _PBR_MODE_FABRIC
float D = D_Charlie(NdotH, bsdfData.roughnessT);
// V_Charlie is expensive, use approx with V_Ashikhmin instead
// float V = V_Charlie(NdotL, clampedNdotV, bsdfData.roughness);
float Vis = V_Ashikhmin(NdotL, clampedNdotV);
DV = D * Vis;
#elif _PBR_MODE_TOON
float specularExponent = RoughnessToBlinnPhongSpecularExponent(PerceptualRoughnessToRoughness(bsdfData.perceptualRoughness));
DV = pow(NdotH, 5.0 * specularExponent);
@@ -108,14 +114,14 @@ DirectLighting UtsShadeSurface(PositionInputs posInput, UtsBSDFData bsdfData, Pr
{
DirectLighting lighting;
ZERO_INITIALIZE(DirectLighting, lighting);
if (Max3(lightColor.r, lightColor.g, lightColor.b) > 0.0)
{
SHADOW_TYPE sharpShadow = smoothstep(0.4, 0.6, shadow);
#if _RECEIVE_HAIR_SHADOW_ON && ENABLE_UTS_HAIR_SHAOW
sharpShadow *= GetHairShadow(posInput, L);
#endif
#if _SHADING_MODE_SDF
float angle;
float3 sdfTexture = SampleSDFTexture(L, uv, angle); // r: sdf shadow, g: sdf highlight, b: halfshadow
@@ -123,7 +129,7 @@ DirectLighting UtsShadeSurface(PositionInputs posInput, UtsBSDFData bsdfData, Pr
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);
@@ -136,22 +142,22 @@ DirectLighting UtsShadeSurface(PositionInputs posInput, UtsBSDFData bsdfData, Pr
#if _SHADING_MODE_STANDARD
float NdotL = dot(bsdfData.normalWS, L);
float halfLambert = 0.5 * NdotL + 0.5;
float3 rampColor = SAMPLE_TEXTURE2D_ARRAY(_ShadingRampMap, s_linear_clamp_sampler, float2(halfLambert * shadow.x, rampMask), _ShadingIndex).rgb;
diffuseTerm = bsdfData.diffuseColor * rampColor;
diffuseTerm = bsdfData.diffuseColor * rampColor * INV_PI;
specularTerm *= saturate(NdotL) * sharpShadow;
#elif _SHADING_MODE_SDF
float3 rampColor = SAMPLE_TEXTURE2D_ARRAY(_ShadingRampMap, s_linear_clamp_sampler, float2(sdfShadowMask * sharpShadow.x, rampMask), _ShadingIndex).rgb;
diffuseTerm = bsdfData.diffuseColor * rampColor;
diffuseTerm = bsdfData.diffuseColor * rampColor * INV_PI;
specularTerm = (specularTerm + sdfHighlight) * sdfShadowMask * sharpShadow;
#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 - _1stShadeColorFeather)) / (_1stShadeColorStep - (_1stShadeColorStep - _1stShadeColorFeather)));
baseShadeMask *= sharpShadow.x;
@@ -159,30 +165,30 @@ DirectLighting UtsShadeSurface(PositionInputs posInput, UtsBSDFData bsdfData, Pr
// float secondColorFeatherForMask = lerp(_2ndShadeColorFeather, 0.0, max(_SecondShadeOverridden, _ComposerMaskMode));
float firstShadeMask = saturate((halfLambert - (_2ndShadeColorStep - _2ndShadeColorFeather)) / (_2ndShadeColorStep - (_2ndShadeColorStep - _2ndShadeColorFeather)));
diffuseTerm = lerp(lerp(bsdfData.secondShadingDiffuseColor, bsdfData.firstShadingDiffuseColor, firstShadeMask), bsdfData.diffuseColor, baseShadeMask);
diffuseTerm = lerp(lerp(bsdfData.secondShadingDiffuseColor, bsdfData.firstShadingDiffuseColor, firstShadeMask), bsdfData.diffuseColor, baseShadeMask) * INV_PI;
specularTerm *= baseShadeMask;
#elif _SHADING_MODE_SDF
float shadeMask = sdfShadowMask * sdfTexture.b * sharpShadow.x;
diffuseTerm = lerp(bsdfData.firstShadingDiffuseColor, bsdfData.diffuseColor, shadeMask);
diffuseTerm = lerp(bsdfData.firstShadingDiffuseColor, bsdfData.diffuseColor, shadeMask) * INV_PI;
specularTerm = (specularTerm + sdfHighlight) * shadeMask;
#endif
#endif
lighting.diffuse += diffuseTerm * lightColor * diffuseDimmer;
lighting.specular += specularTerm * lightColor * specularDimmer;
#if _LIGHT_BASE_RIM_LIGHT_ON
if (HasFlag(bsdfData.surfaceFeatures, SURFACEFEATURE_RIM_LIGHT))
{
DirectLighting rimLightLighting = UtsEvaluateLighting_RimLight(posInput, bsdfData, preLightData, L, lightColor);
lighting.diffuse += rimLightLighting.diffuse;
lighting.specular += rimLightLighting.specular;
}
#endif
}
return lighting;
}

View File

@@ -23,7 +23,7 @@ float3 SampleSDFTexture(float3 L, float2 uv, out float angle)
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;
}
@@ -41,11 +41,11 @@ float GetHairShadow(PositionInputs posInput, float3 L)
float2 shadowLength = float2(shadowLengthY * 2.0f, shadowLengthY);
float3 cameraDirOS = normalize(TransformWorldToObject(GetCameraPositionWS()));
float cameraDirFactor = 1 - smoothstep(0.1, 0.9, cameraDirOS.y);
shadowLength.y *= cameraDirFactor;
float cameraDirFactor = 1.0 - smoothstep(0.1, 0.9, cameraDirOS.y);
// shadowLength.y *= cameraDirFactor;
// TODO: sample point is still shifting when fov change.
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.
float2 samplingPoint = (posInput.positionSS + shadowLength * viewLightDir.xy) * _ScreenSize.zw; // Use 1080p as the reference resolution to achieve consistent shadow lengths across various screen resolutions.
float2 scaledUVs = samplingPoint * _RTHandleScale.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 hairShadow = SAMPLE_TEXTURE2D_SHADOW(_HairShadowTex, s_linear_clamp_compare_sampler, float3(scaledUVs, posInput.deviceDepth + _HairShadowDepthBias)).r;

View File

@@ -87,6 +87,7 @@ float _SSRWeight;
//Rim Light
float4 _RimLightColor;
float _RimLightIntensity;
half _AlbedoAffectRimLight;
float _RimLightLevel;
half _RimLightClipping;
float _RimLightClippingLevel;

View File

@@ -1,6 +1,4 @@
//Unity Toon Shader/HDRP
//nobuyuki@unity3d.com
//toshiyuki@unity3d.com (Universal RP/HDRP)
#include "Packages/com.misaki.hdrp-toon/Runtime/Shaders/Includes/Common/UtsLitData.hlsl"
#define APPROXIMATE_POLY_LIGHT_AS_SPHERE_LIGHT
@@ -80,7 +78,7 @@ void Frag(PackedVaryingsToPS packedInput,
#endif
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(packedInput);
FragInputs input = UnpackVaryingsMeshToFragInputs(packedInput.vmesh);
#if defined(PLATFORM_SUPPORTS_PRIMITIVE_ID_IN_PIXEL_SHADER) && SHADER_STAGE_FRAGMENT
#if (defined(VARYINGS_NEED_PRIMITIVEID) || (SHADERPASS == SHADERPASS_FULL_SCREEN_DEBUG))
@@ -91,24 +89,23 @@ void Frag(PackedVaryingsToPS packedInput,
#if defined(VARYINGS_NEED_CULLFACE) && SHADER_STAGE_FRAGMENT
input.isFrontFace = IS_FRONT_VFACE(packedInput.cullFace, true, false);
#endif
// We need to readapt the SS position as our screen space positions are for a low res buffer, but we try to access a full res buffer.
input.positionSS.xy = _OffScreenRendering > 0 ? (input.positionSS.xy * _OffScreenDownsampleFactor) : input.positionSS.xy;
uint2 tileIndex = uint2(input.positionSS.xy) / GetTileSize();
PositionInputs posInput = GetPositionInput(input.positionSS.xy, _ScreenSize.zw, input.positionSS.z, input.positionSS.w, input.positionRWS.xyz, tileIndex);
#ifdef VARYINGS_NEED_POSITION_WS
float3 V = GetWorldSpaceNormalizeViewDir(input.positionRWS);
#ifdef MATERIAL_TYPE_EYE
// Must have view Dir to work
float2 viewT = TransformObjectToTangent(V, input.tangentToWorld);
float2 parallaxOffset = viewT;
parallaxOffset.y = -parallaxOffset.y;
input.texCoord0.xy = clamp(input.texCoord0.xy -_EyeParallaxAmount * parallaxOffset, 0, 1);
// Must have view Dir to work
float2 viewT = TransformObjectToTangent(V, input.tangentToWorld);
float2 parallaxOffset = viewT;
parallaxOffset.y = -parallaxOffset.y;
input.texCoord0.xy = clamp(input.texCoord0.xy -_EyeParallaxAmount * parallaxOffset, 0, 1);
#endif
#else
// Unused
float3 V = float3(1.0, 1.0, 1.0); // Avoid the division by 0
@@ -118,10 +115,10 @@ void Frag(PackedVaryingsToPS packedInput,
#else
uint featureFlags = LIGHT_FEATURE_MASK_FLAGS_OPAQUE;
#endif
SurfaceData tempSurfaceData;
BuiltinData builtinData;
GetSurfaceAndBuiltinData(input, V, posInput, tempSurfaceData, builtinData);
UtsGetSurfaceAndBuiltinData(input, V, posInput, tempSurfaceData, builtinData);
UTSSurfaceData surfaceData = GetUTSSurfaceData(input, V);
UtsBSDFData bsdfData = ConvertUTSSurfaceDataToUTSBSDFData(surfaceData);
@@ -132,11 +129,9 @@ void Frag(PackedVaryingsToPS packedInput,
context.shadowContext = InitShadowContext();
context.shadowValue = 1;
context.sampleReflection = 0.0;
#if UNITY_VERSION >= 202120 && UNITY_VERSION < 202320
context.splineVisibility = -1;
#endif
#ifdef APPLY_FOG_ON_SKY_REFLECTIONS
context.positionWS = posInput.positionWS;
context.positionWS = posInput.positionWS;
#endif
// With XR single-pass and camera-relative: offset position to do lighting computations from the combined center view (original camera matrix).
@@ -145,24 +140,13 @@ void Frag(PackedVaryingsToPS packedInput,
// Initialize the contactShadow and contactShadowFade fields
InitContactShadow(posInput, context);
LightLoopOutput lightLoopOutput;
ZERO_INITIALIZE(LightLoopOutput, lightLoopOutput);
UtsLightLoop(input, posInput, bsdfData, builtinData, V, featureFlags, lightLoopOutput);
float3 finalColor = lightLoopOutput.diffuseLighting + lightLoopOutput.specularLighting;
// #if _SURFACE_TYPE_TRANSPARENT
// float alpha = channelAlpha;
// outColor = EvaluateAtmosphericScattering(posInput, V, float4(finalColor, 1));
// #else
// float alpha = lerp(1.0, channelAlpha, _AlphaCutoffEnable);
// #endif
//
// alpha = lerp(alpha, step(_AlphaCutoff, alpha), _AlphaCutoffEnable);
outColor = float4(finalColor, 1.0);
UtsLightLoop(input, posInput, bsdfData, builtinData, V, featureFlags, lightLoopOutput);
outColor = ApplyBlendMode(lightLoopOutput.diffuseLighting, lightLoopOutput.specularLighting, builtinData.opacity);
#if _MATERIAL_TYPE_FRONTHAIR && ENABLE_UTS_HAIR_BLENDING
float2 screenUV = posInput.positionSS; // * _HairBlendingRTHandleScale.xy; // Why we don't need to scale? Does unity handle it internally?