Added UTSPolygonFormFactor and UTSComputeEdgeFactor

This commit is contained in:
Misaki
2025-01-17 18:47:37 +09:00
parent b838223551
commit 12a03e9c3c
3 changed files with 107 additions and 173 deletions

View File

@@ -187,3 +187,80 @@ half3 FitWithCurveApprox(half NdotL, half Curvature)
}
#endif
// The output is *not* normalized by the factor of 1/TWO_PI (this is done by the PolygonFormFactor function).
real3 UTSComputeEdgeFactor(real3 V1, real3 V2)
{
real subtendedAngle;
real V1oV2 = dot(V1, V2);
real3 V1xV2 = cross(V1, V2); // Plane normal (tangent to the unit sphere)
real sqLen = saturate(1 - V1oV2 * V1oV2); // length(V1xV2) = abs(sin(angle))
real rcpLen = rsqrt(max(FLT_EPS, sqLen)); // Make sure it is finite
#if 0
real y = rcpLen * acos(V1oV2);
#else
// Let y[x_] = ArcCos[x] / Sqrt[1 - x^2].
// Range reduction: since ArcCos[-x] == Pi - ArcCos[x], we only need to consider x on [0, 1].
real x = abs(V1oV2);
// Limit[y[x], x -> 1] == 1,
// Limit[y[x], x -> 0] == Pi/2.
// The approximation is exact at the endpoints of [0, 1].
// Max. abs. error on [0, 1] is 1.33e-6 at x = 0.0036.
// Max. rel. error on [0, 1] is 8.66e-7 at x = 0.0037.
real y = HALF_PI + x * (-0.99991 + x * (0.783393 + x * (-0.649178 + x * (0.510589 + x * (-0.326137 + x * (0.137528 + x * -0.0270813))))));
if (V1oV2 < 0)
{
y = rcpLen * PI - y;
}
#endif
return V1xV2 * y;
}
// Input: 3-5 vertices in the coordinate frame centered at the shaded point.
// Output: signed vector irradiance.
// No horizon clipping is performed.
real3 UTSPolygonFormFactor(real4x3 L, real3 L4, uint n)
{
// The length cannot be zero since we have already checked
// that the light has a non-zero effective area,
// and thus its plane cannot pass through the origin.
L[0] = normalize(L[0]);
L[1] = normalize(L[1]);
L[2] = normalize(L[2]);
switch (n)
{
case 3:
L[3] = L[0];
break;
case 4:
L[3] = normalize(L[3]);
L4 = L[0];
break;
case 5:
L[3] = normalize(L[3]);
L4 = normalize(L4);
break;
}
// If the magnitudes of a pair of edge factors are
// nearly the same, catastrophic cancellation may occur:
// https://en.wikipedia.org/wiki/Catastrophic_cancellation
// For the same reason, the value of the cross product of two
// nearly collinear vectors is prone to large errors.
// Therefore, the algorithm is inherently numerically unstable
// for area lights that shrink to a line (or a point) after
// projection onto the unit sphere.
real3 F = UTSComputeEdgeFactor(L[0], L[1]);
F += UTSComputeEdgeFactor(L[1], L[2]);
F += UTSComputeEdgeFactor(L[2], L[3]);
if (n >= 4)
F += UTSComputeEdgeFactor(L[3], L4);
if (n == 5)
F += UTSComputeEdgeFactor(L4, L[0]);
return F; // The output may be projected onto the tangent plane (F.z) to yield signed irradiance.
}

View File

@@ -2,6 +2,8 @@
//nobuyuki@unity3d.com
//toshiyuki@unity3d.com (Universal RP/HDRP)
#define APPROXIMATE_POLY_LIGHT_AS_SPHERE_LIGHT
#if SHADERPASS != SHADERPASS_FORWARD
#error SHADERPASS_is_not_correctly_define
#endif
@@ -530,14 +532,14 @@ void Frag(PackedVaryingsToPS packedInput,
float4 distances; // {d, d^2, 1/d, d_proj}
GetPunctualLightVectors(posInput.positionWS, s_lightData, lightDirection, distances);
float4 lightColor = EvaluateLight_Punctual(context, posInput, s_lightData, lightDirection, distances);
float3 additionalLightColor = ApplyCurrentExposureMultiplier(lightColor.rgb) * lightColor.a;
float3 additionalLightColor = ApplyCurrentExposureMultiplier(lightColor.rgb);
const float notDirectional = 1.0f;
UTSLightData utsLightData;
utsLightData.lightColor = additionalLightColor;
utsLightData.lightDirection = lightDirection;
utsLightData.diffuseDimmer = s_lightData.diffuseDimmer;
utsLightData.specularDimmer = s_lightData.specularDimmer;
utsLightData.diffuseDimmer = s_lightData.diffuseDimmer * lightColor.a;
utsLightData.specularDimmer = s_lightData.specularDimmer * lightColor.a;
utsLightData.shadowTint = s_lightData.shadowTint;
utsLightData.penumbraTint = s_lightData.penumbraTint;
@@ -634,12 +636,25 @@ void Frag(PackedVaryingsToPS packedInput,
float4 ltcValue;
float4x3 lightVerts;
float3x3 invM = transpose(preLightData.ltcTransformDiffuse);
float3 A = mul(invM, right);
float3 B = mul(invM, up);
float3 C = mul(invM, center);
lightVerts[0] = C - halfWidth * A - halfHeight * B; // LL
lightVerts[1] = lightVerts[0] + (2 * halfHeight) * B; // UL
lightVerts[2] = lightVerts[1] + (2 * halfWidth) * A; // UR
lightVerts[3] = lightVerts[2] - (2 * halfHeight) * B; // LR
float3 F = UTSPolygonFormFactor(lightVerts, float3(0,0,1), 4);
float l = length(F * PI);
float alpha = saturate(max(0, (l * l) / (l + 1)));
// Diffuse
ltcValue = EvaluateLTC_Area(isRectLight, center, right, up, halfWidth, halfHeight, transpose(preLightData.ltcTransformDiffuse), /*bsdfData.perceptualRoughness*/ 1.0f, s_lightData.cookieMode, s_lightData.cookieScaleOffset);
utsLightData.diffuseDimmer *= ltcValue.a * intensity;
//ltcValue = smoothstep(0.0, 0.05, ltcValue);
//utsLightData.lightColor *= ltcValue.rgb * ltcValue.a * intensity;
utsLightData.lightColor *= ltcValue.rgb * intensity;
utsLightData.diffuseDimmer *= alpha * intensity;
utsLightData.lightColor *= ltcValue.rgb;
// Specular
ltcValue = EvaluateLTC_Area(isRectLight, center, right, up, halfWidth, halfHeight, transpose(preLightData.ltcTransformSpecular[0]), bsdfData.perceptualRoughness, s_lightData.cookieMode, s_lightData.cookieScaleOffset);
@@ -672,162 +687,6 @@ void Frag(PackedVaryingsToPS packedInput,
//utsAggregateLighting.directDiffuse += intensity;
#endif
/*
if(s_lightData.lightType == GPULIGHTTYPE_RECTANGLE)
{
#if SHADEROPTIONS_BARN_DOOR
// Apply the barn door modification to the light data
RectangularLightApplyBarnDoor(s_lightData, posInput.positionWS);
#endif
if (dot(s_lightData.forward, unL) < FLT_EPS)
{
float3x3 lightToWorld = float3x3(s_lightData.right, s_lightData.up, -s_lightData.forward);
unL = mul(unL, transpose(lightToWorld));
float halfWidth = s_lightData.size.x * 0.5;
float halfHeight = s_lightData.size.y * 0.5;
float range = s_lightData.range;
float3 invHalfDim = rcp(float3(range + halfWidth, range + halfHeight, range));
float intensity;
// Compute the light attenuation.
#ifdef ELLIPSOIDAL_ATTENUATION
// The attenuation volume is an axis-aligned ellipsoid s.t.
// r1 = (r + w / 2), r2 = (r + h / 2), r3 = r.
intensity = EllipsoidalDistanceAttenuation(unL, invHalfDim, s_lightData.rangeAttenuationScale, s_lightData.rangeAttenuationBias);
#else
// The attenuation volume is an axis-aligned box s.t.
// hX = (r + w / 2), hY = (r + h / 2), hZ = r.
intensity = BoxDistanceAttenuation(unL, invHalfDim, s_lightData.rangeAttenuationScale, s_lightData.rangeAttenuationBias);
#endif
if(intensity != 0.0f)
{
utsLightData.diffuseDimmer *= intensity;
utsLightData.specularDimmer *= intensity;
// Translate the light s.t. the shaded point is at the origin of the coordinate system.
s_lightData.positionRWS -= posInput.positionWS;
float4x3 lightVerts;
// TODO: some of this could be precomputed.
lightVerts[0] = s_lightData.positionRWS + s_lightData.right * -halfWidth + s_lightData.up * -halfHeight; // LL
lightVerts[1] = s_lightData.positionRWS + s_lightData.right * -halfWidth + s_lightData.up * halfHeight; // UL
lightVerts[2] = s_lightData.positionRWS + s_lightData.right * halfWidth + s_lightData.up * halfHeight; // UR
lightVerts[3] = s_lightData.positionRWS + s_lightData.right * halfWidth + s_lightData.up * -halfHeight; // LR
// Rotate the endpoints into the local coordinate system.
lightVerts = mul(lightVerts, transpose(preLightData.orthoBasisViewNormal));
float3 ltcValue;
// Evaluate the diffuse part
// Polygon irradiance in the transformed configuration.
float4x3 LD = mul(lightVerts, preLightData.ltcTransformDiffuse);
float3 formFactorD;
#ifdef APPROXIMATE_POLY_LIGHT_AS_SPHERE_LIGHT
formFactorD = PolygonFormFactor(LD, real3(0,0,1), 4);
ltcValue = PolygonIrradianceFromVectorFormFactor(formFactorD);
#else
ltcValue = PolygonIrradiance(LD);
#endif
utsLightData.diffuseDimmer *= ltcValue;
// Evaluate the specular part
// Polygon irradiance in the transformed configuration.
float4x3 LS = mul(lightVerts, preLightData.ltcTransformSpecular);
float3 formFactorS;
#ifdef APPROXIMATE_POLY_LIGHT_AS_SPHERE_LIGHT
formFactorS = PolygonFormFactor(LS, real3(0,0,1), 4);
ltcValue = PolygonIrradianceFromVectorFormFactor(formFactorS);
#else
ltcValue = PolygonIrradiance(LS);
#endif
utsLightData.specularDimmer *= ltcValue;
//Evaluate the shadow part
float shadow;
posInput.positionWS = posInput.positionWS + lightDirection * _ShadowBias;
#if defined(SCREEN_SPACE_SHADOWS_ON) && !defined(_SURFACE_TYPE_TRANSPARENT)
if ((s_lightData.screenSpaceShadowIndex & SCREEN_SPACE_SHADOW_INDEX_MASK) != INVALID_SCREEN_SPACE_SHADOW)
{
shadow = GetScreenSpaceShadow(posInput, s_lightData.screenSpaceShadowIndex);
}
else
#endif
{
shadow = EvaluateShadow_RectArea(context, posInput, s_lightData, builtinData, GetNormalForShadowBias(bsdfData), normalize(s_lightData.positionRWS), length(s_lightData.positionRWS));
}
context.shadowValue = shadow;
posInput.positionWS = posInput.positionWS - lightDirection * _ShadowBias;
#if defined(UTS_DEBUG_SELFSHADOW)
#else
UTS_OtherLights(context, input, utsLightData, surfaceData, bsdfData, s_lightData.lightType, i_normalDir, notDirectional, channelAlpha, utsAggregateLighting);
#endif
}
}
}
else if(s_lightData.lightType == GPULIGHTTYPE_TUBE)
{
float len = s_lightData.size.x;
float3 T = s_lightData.right;
// Pick the major axis of the ellipsoid.
float3 axis = s_lightData.right;
// We define the ellipsoid s.t. r1 = (r + len / 2), r2 = r3 = r.
// TODO: This could be precomputed.
float range = s_lightData.range;
float invAspectRatio = saturate(range / (range + (0.5 * len)));
// Compute the light attenuation.
float intensity = EllipsoidalDistanceAttenuation(unL, axis, invAspectRatio, s_lightData.rangeAttenuationScale, s_lightData.rangeAttenuationBias);
if(intensity != 0.0f)
{
utsLightData.diffuseDimmer *= intensity;
utsLightData.specularDimmer *= intensity;
// Translate the light s.t. the shaded point is at the origin of the coordinate system.
s_lightData.positionRWS -= posInput.positionWS;
// TODO: some of this could be precomputed.
float3 P1 = s_lightData.positionRWS - T * (0.5 * len);
float3 P2 = s_lightData.positionRWS + T * (0.5 * len);
// Rotate the endpoints into the local coordinate system.
P1 = mul(P1, transpose(preLightData.orthoBasisViewNormal));
P2 = mul(P2, transpose(preLightData.orthoBasisViewNormal));
// Compute the binormal in the local coordinate system.
float3 B = normalize(cross(P1, P2));
float ltcValue;
// Evaluate the diffuse part
ltcValue = LTCEvaluate(P1, P2, B, preLightData.ltcTransformDiffuse);
utsLightData.diffuseDimmer *= ltcValue;
// Evaluate the specular part
ltcValue = LTCEvaluate(P1, P2, B, preLightData.ltcTransformSpecular);
utsLightData.specularDimmer *= ltcValue;
#if defined(UTS_DEBUG_SELFSHADOW)
#else
UTS_OtherLights(context, input, utsLightData, surfaceData, bsdfData, s_lightData.lightType, i_normalDir, notDirectional, channelAlpha, utsAggregateLighting);
#endif
}
}
*/
}
s_lightData = FetchLight(lightStart, min(++i, last));

View File

@@ -54,23 +54,21 @@ void UTS_OtherLights(LightLoopContext lightLoopContext, FragInputs input, UTSLig
float3 addPassLightColor;
if (lightType == GPULIGHTTYPE_TUBE)
{
addPassLightColor = (0.5f * preLightData.diffuseFGD + 0.5f) / PI * additionalLightColor.rgb * utsLightData.diffuseDimmer;
addPassLightColor = (0.5f * preLightData.diffuseFGD + 0.5f) / PI * additionalLightColor.rgb;
}
else if (lightType == GPULIGHTTYPE_RECTANGLE)
{
addPassLightColor = preLightData.diffuseFGD * additionalLightColor.rgb * utsLightData.diffuseDimmer;
//addPassLightColor = (0.5f * preLightData.diffuseFGD + 0.5f) * additionalLightColor.rgb * utsLightData.diffuseDimmer;
addPassLightColor = preLightData.diffuseFGD * additionalLightColor.rgb;
}
else
{
addPassLightColor = _HalfLambert_var * additionalLightColor.rgb * utsLightData.diffuseDimmer;
addPassLightColor = _HalfLambert_var * additionalLightColor.rgb;
}
float pureIntencity = max(0.001, (0.299 * additionalLightColor.r + 0.587 * additionalLightColor.g + 0.114 * additionalLightColor.b));
float pureIntencity = max(0.001, (0.299 * additionalLightColor.r + 0.587 * additionalLightColor.g + 0.114 * additionalLightColor.b));
float3 lightColor = max(float3(0.0, 0.0, 0.0), lerp(addPassLightColor, lerp(float3(0.0, 0.0, 0.0), min(addPassLightColor, addPassLightColor / pureIntencity), notDirectional), _Is_Filter_LightColor));
float3 halfDirection = normalize(viewDirection + lightDirection); // has to be recalced here.
//v.2.0.5:
//v.2.0.5:
_1st_ShadeColor_Step = saturate(_1st_ShadeColor_Step + _StepOffset);
_2nd_ShadeColor_Step = saturate(_2nd_ShadeColor_Step + _StepOffset);
//
@@ -156,8 +154,8 @@ void UTS_OtherLights(LightLoopContext lightLoopContext, FragInputs input, UTSLig
channelOutAlpha =
lerp(Set_BaseColorAlpha, lerp(Set_1st_ShadeAlpha, Set_2nd_ShadeAlpha, Set_ShadeShadowMask), Set_FinalShadowMask);
#endif
utsAggregateLighting.directDiffuse = diffuseTerm;
return;
//utsAggregateLighting.directDiffuse = utsLightData.diffuseDimmer;
//return;
//v.2.0.6: Add HighColor if _Is_Filter_HiCutPointLightColor is False
@@ -246,7 +244,7 @@ void UTS_OtherLights(LightLoopContext lightLoopContext, FragInputs input, UTSLig
specularTerm = specularTerm * (1.0 - Set_FinalShadowMask) * PI * surfaceData.specularColor;
diffuseTerm = diffuseTerm * albedoIntensity;
utsAggregateLighting.directDiffuse += diffuseTerm;
utsAggregateLighting.directSpecular += specularTerm;
utsAggregateLighting.directDiffuse += diffuseTerm * utsLightData.diffuseDimmer;
utsAggregateLighting.directSpecular += specularTerm * utsLightData.specularDimmer;
#endif // _SDFShadow
}