由於景深碰撞不能應用於透明物體,因此試了一下UE4的距離場碰撞,效果還可以接受,但是發現發射器的Collision中Random Spread和Random Distribution參數都失效了,粒子只能按照法線做反彈。
檢查源碼發現CollideWithDepthBuffer函數中的一行ComputeCollidingVelocity函數在CollideWithDistanceField中被改寫成過程了,而且裏面沒有隨機分佈的相關代碼,把部分過程刪掉換成NewVelocity = ComputeCollidingVelocity(MidVelocity, CollisionPlane.xyz, RelativeTime, Resilience);就可以解決問題了。
//C:\Program Files\Epic Games\UE_4.17\Engine\Shaders\Private\ParticleSimulationShader.usf
/**
* Compute collision with the global signed distance field
*/
void CollideWithDistanceField(
out float3 NewPosition,
out float3 NewVelocity,
inout float RelativeTime,
in float3 InPosition,
in float3 InVelocity,
in float3 Acceleration,
in float CollisionRadius,
in float Resilience
)
{
// Integration assuming no collision.
float3 MidVelocity = InVelocity.xyz + 0.5f * Acceleration;
float3 DeltaPosition = DeltaSeconds * MidVelocity;
NewPosition = InPosition.xyz + DeltaPosition;
NewVelocity = InVelocity.xyz + Acceleration;
float DistanceToNearestSurface = GetDistanceToNearestSurfaceGlobal(InPosition);
float MaxCollisionDistance = CollisionRadius + length(DeltaPosition.xyz);
if (DistanceToNearestSurface < MaxCollisionDistance)
{
float3 CollisionWorldNormal = normalize(GetDistanceFieldGradientGlobal(InPosition));
float3 CollisionWorldPosition = InPosition - CollisionWorldNormal * DistanceToNearestSurface;
float4 CollisionPlane = float4(CollisionWorldNormal.xyz, dot(CollisionWorldPosition.xyz, CollisionWorldNormal.xyz));
// Compute the portion of velocity normal to the collision plane.
float VelocityDot = dot(CollisionPlane.xyz, DeltaPosition.xyz);
float InvVelocityDot = rcp(VelocityDot + 0.0001f); // Add a small amount to avoid division by zero.
// Distance to the plane from the center of the particle.
float DistanceToPlane = dot(CollisionPlane.xyz, InPosition.xyz) - CollisionPlane.w;
// Find out the time of intersection for both the front and back of the sphere.
float t_back = -(DistanceToPlane + CollisionRadius) * InvVelocityDot;
float t_front = -(DistanceToPlane - CollisionRadius) * InvVelocityDot;
//if (t_back >= 0 && t_front <= 1 && DistanceToPlane >= 0)
if (step(0, t_back) * step(t_front, 1) * step(0, DistanceToPlane))
{
NewVelocity = ComputeCollidingVelocity(MidVelocity, CollisionPlane.xyz, RelativeTime, Resilience);
// If the particle lies approximately on the collision plane, don't jump to the point of collision.
t_front *= step(VelocityDot,-1);
// Integrate position taking the collision in to account.
NewPosition = InPosition + DeltaPosition * t_front + NewVelocity * (1.0f - t_front) * DeltaSeconds;
// Update the relative time. Usually this does nothing, but if the
// user has elected to kill the particle upon collision this will do
// so.
RelativeTime += Simulation.CollisionTimeBias;
}
//else if (t_front > 0 && t_back < 1 && DistanceToPlane < 0)
else if (step(0, t_front) * step(t_back, 1) * step(DistanceToPlane,0))
{
// The particle has collided against a backface, kill it by setting
// relative time to a value > 1.0.
RelativeTime = 1.1f;
}
}
}