Local space velocity-facing mesh particles would disappear in some cases on Android and IOS devices

We found that some local space velocity-facing mesh particles disappear in when they aim at some direction, this issue occurs on on some IOS and Android devices while some other devices work fine. Using renderdoc I found that the cause is RefVector and FacingDir having the same values, which makes YAxis NAN. I think this is a bug. If this particle do not use local space, the RefVector would fall back to world X-asix to avoid two vectors to cross product with having the same value and everything work fine. But if this particle use local space, the RefVector might be rotated by TransformVectorInverse() and the fall-back protection does no good with it. Although we are using unreal 4.27, I see this logic is still unchanged in later version of unreal. Any suggestion to fix this?

[Image Removed]Using renderdoc to debug vertex shader I found that the calculation of facing matrix is also problematic on PC platforms, the facing matrix and the final position output are NAN as we can see on renderdoc’s debug panel. But it just somehow outputs a non-nan position eventually as we can see on the MeshViewer, maybe there is some magic I don’t know that directx driver does to avoid NAN…

[Image Removed] [Image Removed]

Create a niagara mesh particle, enable local space and choose Velocity as facing mode. Add some Y-axis or X-axis rotation on it.

Hi,

Thanks for the report, do you have a repro asset at all? I tried to make one and I must be missing something, I was injecting a nearly equal in the shader to hopefully highlight it.

Thanks,

Stu

Hi,

I managed to get a repro for this, I think the solution is to move the transform inside the camera plane branch, i.e.

		// Determine a reference vector to use for up
		float3 RefVector;
		if (Params.FacingMode == MESH_FACING_CAMERA_PLANE)
		{
			// Use the camera upwards direction as a reference vector
			//-TODO: Add ability to remove HMD roll in VR
			//RefVector = Params.CameraUpDir;
			RefVector = NiagaraWorldToSimVec(Params.CameraUpDir, Params);
		}
		else
		{
			// Prefer to use world up as a reference vector, fall back to world X-axis when facing up or down
			float DotWorldZ = dot(FacingDir, WorldZ);
			RefVector = abs(DotWorldZ) > 0.99f ? (-sign(DotWorldZ) * WorldX) : WorldZ;
		}
 
		// rotate the reference direction to simulation space, if necessary
		//RefVector = NiagaraWorldToSimVec(RefVector, Params);

So far this is working fine, but I need to do more testing.

Thanks,

Stu

After running various tests, this is what I ended up changing the code to locally.

else
{
	// Prefer to use world up as a reference vector, fall back to world X-axis when facing up or down
	const float3 SimFacingDir = NiagaraSimToWorldVec(FacingDir, Params);
	float DotWorldZ = dot(SimFacingDir, WorldZ);
	RefVector = abs(DotWorldZ) > 0.99f ? (-sign(DotWorldZ) * WorldX) : WorldZ;
}

The change you have above will impact the other side of the expression which is incorrect.

Thanks,

Stu

I went through the same process also :slight_smile:

I’ve submitted this into UE5 Main on CL 47466098.

Thanks for the report and analysis made it much easier to figure out.

Stu

Thanks for your reply, but I think moving the dot product check and fallback after the transform is safer. Here is the modification I make:

[Image Removed]

Oh, the change you mention above makes sense, I will give it a try. My modification is based on the idea that the MESH_FACING_CAMERA_PLANE branch might also results in NAN, if the FacingDir is similar to RefVector/CamViewUp. But now I realize in that case FacingDir will be -CamViewForward so it’s impossible they are two similar vector, my worry is totally unnecessary. Thanks again, your suggestion is really helpful.