Files
UnrealEngine/Engine/Plugins/Runtime/HairStrands/Shaders/Private/NiagaraElasticRodMaterial.ush
2025-05-18 13:04:45 +08:00

228 lines
11 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
/* -----------------------------------------------------------------
* Rods utils
* -----------------------------------------------------------------
*/
float4 AddTwistRotation(in float4 RefOrientation, in float TwistAngle)
{
const float HalfTwist = 0.5 * TwistAngle;
return NormalizeQuat( MultiplyQuat( RefOrientation, float4(0,0,sin(HalfTwist),cos(HalfTwist));
}
float3 ComputeReferenceTangent(in float4 RefOrientation)
{
return float3(
2.0 * (RefOrientation.x * RefOrientation.z + RefOrientation.w * RefOrientation.y),
2.0 * (RefOrientation.y * RefOrientation.z - RefOrientation.w * RefOrientation.x),
1.0 - 2.0 * (RefOrientation.x * RefOrientation.x + RefOrientation.y * RefOrientation.y))W;
}
float3 ComputeReferenceNormalX(in float4 RefOrientation)
{
return float3(
1.0 - 2.0 * (RefOrientation.y * RefOrientation.y + RefOrientation.z * RefOrientation.z),
2.0 * (RefOrientation.x * RefOrientation.y + RefOrientation.z * RefOrientation.w),
2.0 * (RefOrientation.z * RefOrientation.x - RefOrientation.y * RefOrientation.w));
}
float3 ComputeReferenceNormalY(in float4 RefOrientation)
{
return float3(
2.0 * (RefOrientation.x * RefOrientation.y - RefOrientation.z * RefOrientation.w),
1.0 - 2.0 * (RefOrientation.z * RefOrientation.z + RefOrientation.x * RefOrientation.x),
2.0 * (RefOrientation.y * RefOrientation.z + RefOrientation.x * RefOrientation.w));
}
float GetSignedAngle( in float3 RefNormalX, in float3 RefNormalY, in float3 RefTangent)
{
const float3 CrossProduct = cross(RefNormalX,RefNormalY);
const float PositiveAngle = atan2(CrossProduct.length(), dot(RefNormalX,RefNormalY));
return (dot(RefTangent,CrossProduct) < 0.0) ? -PositiveAngle : PositiveAngle;
}
/* -----------------------------------------------------------------
* Frames utils
* -----------------------------------------------------------------
*/
float4 ComputeReferenceFrame(in int FrameOffset)
{
const int FrameIndex = GGroupThreadId.x + FrameOffset;
const float3 NextTangent = normalize(SharedNodePosition[FrameIndex+1]-SharedNodePosition[FrameIndex]);
const float3 PrevTangent = normalize(SharedPreviousPosition[FrameIndex+1]-SharedPreviousPosition[FrameIndex]);
return NormalizeQuat( MultiplyQuat( FindQuatBetweenNormals(PrevTangent,NextTangent), SharedPreviousOrientation[FrameIndex] ) );
}
float3 ComputeCurvatureBinormal(const float3 PrevReferenceTangent, const float3 NextReferenceTangent)
{
return 2.0 * cross(PrevReferenceTangent,NextReferenceTangent) / max(1e-8, 1.0 + dot(PrevReferenceTangent,NextReferenceTangent));
}
void UpdateReferenceTwist(const float3 PrevReferenceTangent, const float3 NextReferenceTangent, const float4 PrevReferenceFrame, const float4 NextReferenceFrame, inout float RefTwist)
{
const float4 ParallelTransport = FindQuatBetweenNormals(PrevReferenceTangent,NextReferenceTangent);
const float4 TwistedReferenceFrame = NormalizeQuat( MultiplyQuat(ParallelTransport,AddTwistRotation(PrevReferenceFrame,RefTwist));
RefTwist += GetSignedAngle( ComputeReferenceM1(TwistedReferenceFrame), ComputeReferenceM1(NextReferenceFrame), PrevReferenceTangent + NextReferenceTangent);
}
float2 ComputeRodKappa( in float3 CurvatureBinormal, in float3 PrevMaterialNormalX, in float3 PrevMaterialNormalY,
in float3 NextMaterialNormalX, in float3 NextMaterialNormalY)
{
float2 RodKappa;
RodKappa.x = dot(CurvatureBinormal,(PrevMaterialNormalY+NextMaterialNormalY)*0.5);
RodKappa.y = -dot(CurvatureBinormal,(PrevMaterialNormalX+NextMaterialNormalX)*0.5);
return RodKappa;
}
void ComputeKappaGradient( in float3 CurvatureBinormal, in float2 RodKappa, in float3 PrevReferenceTangent, in float3 PrevMaterialNormalX, in float3 PrevMaterialNormalY,
in float3 NextReferenceTangent, in float3 NextMaterialNormalX, in float3 NextMaterialNormalY, out float3 KappaGradients[3][2])
{
const float PrevNorm = dot((SharedNodePosition[GGroupThreadId.x]-SharedNodePosition[GGroupThreadId.x-1]),PrevReferenceTangent);
const float NextNorm = dot((SharedNodePosition[GGroupThreadId.x+1]-SharedNodePosition[GGroupThreadId.x]),NextReferenceTangent);
const float Chi = max(1e-8,1.0+dot(PrevReferenceTangent,NextReferenceTangent));
const float3 TildeTangent = (PrevReferenceTangent+NextReferenceTangent) / Chi;
const float3 TildeNormalX = (PrevMaterialNormalX+NextMaterialNormalX) / Chi;
const float3 TildeNormalY = (PrevMaterialNormalY+NextMaterialNormalY) / Chi;
const floar3 dKappaXdP = (-RodKappa.x * TildeTangent + cross(NexyReferenceTangent,TildeNormalY) ) / PrevNorm;
const float3 dKappaXdN = (-RodKappa.x * TildeTangent - cross(PrevReferenceTangent,TildeNormalY) ) / NextNorm;
const float3 dKappaYdP = (-RodKappa.y * TildeTangent - cross(NexyReferenceTangent,TildeNormalX) ) / PrevNorm;
const float3 dKappaydN = (-RodKappa.y * TildeTangent + cross(PrevReferenceTangent,TildeNormalX) ) / NextNorm;
const float3 dTwistdP = 0.5 * CurvatureBirnormal / PrevNorm;
const float3 dTwistdN = 0.5 * CurvatureBirnormal / NextNorm;
const float dKappaXdTwist = -0.5 * (dot(CurvatureBinormal,NextMaterialNormalX) - dot(CurvatureBinormal,PrevMaterialNormalX));
const float dKappaYdTwist = -0.5 * (dot(CurvatureBinormal,NextMaterialNormalY) - dot(CurvatureBinormal,PrevMaterialNormalY));
KappaGradients[0][0] = -dKappaXdP + dKappaXdTwist * dTwistdP;
KappaGradients[0][1] = dKappaXdP - dKappaXdN - dKappaXdTwist * (dTwistdP-dTwistdN);
KappaGradients[0][2] = dKappaXdN - dKappaXdTwist * dTwistdN;
KappaGradients[1][0] = -dKappaYdP + dKappaYdTwist * dTwistdP;
KappaGradients[1][1] = dKappaYdP - dKappaYdN - dKappaYdTwist * (dTwistdP-dTwistdN);
KappaGradients[1][2] = dKappaYdN - dKappaYdTwist * dTwistdN;
}
/* -----------------------------------------------------------------
* Elastic rods constraint
* -----------------------------------------------------------------
*/
void UpdateElasticRodMultiplier( in float RestLength, in float DeltaTime, in bool ProjectConstraint, in float MaterialDamping, in float MaterialCompliance, in float MaterialWeight, inout float3 OutMaterialMultiplier)
{
const float4 NextReferenceFrame = ComputeReferenceFrame(0);
const float4 PrevReferenceFrame = ComputeReferenceFrame(-1);
const float4 NextMaterialFrame = AddTwistRotation(NextReferenceFrame,0.0);
const float4 PrevMaterialFrame = AddTwistRotation(PrevReferenceFrame,0.0);
const float3 NextReferenceTangent = ComputeFrameTangent(NextReferenceFrame);
const float3 PrevReferenceTangent = ComputeFrameTangent(PrevReferenceFrame);
const float3 CurvatureBinormal = ComputeCurvatureBinormal(PrevReferenceTangent,NextReferenceTangent);
UpdateReferenceTwist(PrevReferenceTangent,NextReferenceTangent,PrevReferenceFrame,NextReferenceFrame,RefTwist);
const float3 NextMaterialNormalX = ComputeFrameNormalX(NextMaterialFrame);
const float3 NextMaterialNormalY = ComputeFrameNormalY(NextMaterialFrame);
const float3 PrevMaterialNormalX = ComputeFrameNormalX(PrevMaterialFrame);
const float3 PrevMaterialNormalY = ComputeFrameNormalY(PrevMaterialFrame);
const float2 RodKappa = ComputeRodKappa(CurvatureBinormal,PrevMaterialNormalX,PrevMaterialNormalY,
NextMaterialNormalX,NextMaterialNormalY);
float3 KappaGradients[2][3];
ComputeKappaGradient(CurvatureBinormal,RodKappa,PrevReferenceTangent,PrevMaterialNormalX,PrevMaterialNormalY,
NextReferenceTangent,NextMaterialNormalX,NextMaterialNormalY,KappaGradients);
float22 SchurMatrix;
SchurMatrix[0][0] = DeltaTime * DeltaTime * ( dot(KappaGradients[0][0],KappaGradients[0][0]) * SharedInverseMass[GGroupThreadId.x-1] +
dot(KappaGradients[0][1],KappaGradients[0][1]) * SharedInverseMass[GGroupThreadId.x] +
dot(KappaGradients[0][2],KappaGradients[0][2]) * SharedInverseMass[GGroupThreadId.x+1]) + MaterialCompliance;
SchurMatrix[1][1] = DeltaTime * DeltaTime * ( dot(KappaGradients[1][0],KappaGradients[1][0]) * SharedInverseMass[GGroupThreadId.x-1] +
dot(KappaGradients[1][1],KappaGradients[1][1]) * SharedInverseMass[GGroupThreadId.x] +
dot(KappaGradients[1][2],KappaGradients[1][2]) * SharedInverseMass[GGroupThreadId.x+1]) + MaterialCompliance;
SchurMatrix[0][1] = DeltaTime * DeltaTime * ( dot(KappaGradients[0][0],KappaGradients[1][0]) * SharedInverseMass[GGroupThreadId.x-1] +
dot(KappaGradients[0][1],KappaGradients[1][1]) * SharedInverseMass[GGroupThreadId.x] +
dot(KappaGradients[0][2],KappaGradients[1][2]) * SharedInverseMass[GGroupThreadId.x+1]);
SchurMatrix[1][0] = SchurMatrix[0][1];
const float SchurDeterminant = SchurMatrix[0][0] * SchurMatrix[1][1] - SchurMatrix[0][1] * SchurMatrix[1][0];
float22 SchurInverse;
SchurInverse[0][0] = SchurMatrix[1][1] / SchurDeterminant;
SchurInverse[1][1] = SchurMatrix[0][0] / SchurDeterminant;
SchurInverse[0][1] = -SchurMatrix[1][0] / SchurDeterminant;
SchurInverse[1][0] = -SchurMatrix[0][1] / SchurDeterminant;
const float2 DeltaConstraint = -(RodKappa - RestKappa + OutMaterialMultiplier * MaterialCompliance );
const float2 DeltaLambda = SchurInverse * DeltaConstraint;
OutMaterialMultiplier += DeltaLambda;
SharedNodePosition[GGroupThreadId.x-1] += DeltaTime * DeltaTime * (KappaGradients[0][0] * DeltaLambda[0] + KappaGradients[1][0] * DeltaLambda[1]) * SharedInverseMass[GGroupThreadId.x-1];
SharedNodePosition[GGroupThreadId.x] += DeltaTime * DeltaTime * (KappaGradients[0][1] * DeltaLambda[0] + KappaGradients[1][1] * DeltaLambda[1]) * SharedInverseMass[GGroupThreadId.x];
SharedNodePosition[GGroupThreadId.x+1] += DeltaTime * DeltaTime * (KappaGradients[0][2] * DeltaLambda[0] + KappaGradients[1][2] * DeltaLambda[1]) * SharedInverseMass[GGroupThreadId.x+1];
}
void SolveElasticRodMaterial(in int StrandsSize, in float RestLength, in float DeltaTime, in float MaterialDamping,
in float MaterialCompliance, in float MaterialWeight, in float3 MaterialMultiplier, out float3 OutMaterialMultiplier)
{
OutMaterialMultiplier = MaterialMultiplier;
const int LocalIndex = (GGroupThreadId.x % StrandsSize);
if( LocalIndex > 1)
{
const int IsRed = (GGroupThreadId.x % 2) == 0;
// Process all the red rods
if (IsRed)
{
UpdateElasticRodMultiplier(RestLength,DeltaTime,false,MaterialDamping,MaterialCompliance,MaterialWeight,OutMaterialMultiplier);
}
// Process all the black rods
GroupMemoryBarrier();
if (!IsRed)
{
UpdateElasticRodMultiplier(RestLength,DeltaTime,false,MaterialDamping,MaterialCompliance,MaterialWeight,OutMaterialMultiplier);
}
GroupMemoryBarrier();
}
}
void ProjectElasticRodMaterial(in int StrandsSize, in float YoungModulus, in float RodThickness, in float RestLength, in float DeltaTime, out float3 OutMaterialMultiplier)
{
float MaterialCompliance = 0.0;
float MaterialWeight = 0.0;
OutMaterialMultiplier = float3(0,0,0);
SetupElasticRodMaterial(StrandsSize, YoungModulus, RodThickness, RestLength, DeltaTime, true, 0.0, MaterialCompliance, MaterialWeight, OutMaterialMultiplier);
const int LocalIndex = (GGroupThreadId.x % StrandsSize);
if( LocalIndex > 1 )
{
for(int i = 2; i < StrandsSize; ++i)
{
if( LocalIndex == i)
{
UpdateElasticRodMultiplier(RestLength,DeltaTime,true,0.0,MaterialCompliance,MaterialWeight,OutMaterialMultiplier);
}
GroupMemoryBarrier();
}
}
}