Making bullet tracer "motion-blur" look smooth

I have checked some posts about achieving a motion blur effect with bullet tracers, from what I have seen, it involves the manipulation of Bezier curves/beam curves and bullet position relative to the camera a frame earlier.

The issue is that I am not properly manipulating the beams, as the result does not look properly smoothed like what I want to achieve, the issue is not with the handler itself (not setting bezier curve positions or knowing where the bullet is) but how the math is done.

Intended result (Looks more natural, smoothed out, not weird):

Actual result (Weird zig-zag curves or too linear):


The code

local setCurveP1 = function(beam, pos)
	local vel = (pos-beam.Attachment0.WorldPosition)
	if vel.Magnitude ~= 0 then
		beam.Attachment0.WorldAxis = vel.Unit
	else
		beam.Attachment0.WorldAxis = Vector3.yAxis
	end
	beam.CurveSize0 = vel.Magnitude
end

local setCurveP2 = function(beam, pos)
	local vel = (pos-beam.Attachment1.WorldPosition)
	if vel.Magnitude ~= 0 then
		beam.Attachment1.WorldAxis = -vel.Unit
	else
		beam.Attachment1.WorldAxis = -Vector3.yAxis
	end
	beam.CurveSize1 = vel.Magnitude
end

game:GetService("RunService").Heartbeat:Connect(function(Step)
	for _, Data in list do --for every bullet
		local Beam = data[1] --beam object

		local OldPosition = Data[2]
		local OldVelocity = Data[3]
		local NewPosition = Data[4]
		local NewVelocity = Data[5]

		Beam.Attachment0.WorldPosition = Camera.CFrame:PointToWorldSpace(
			PreviousCameraCFrame:PointToObjectSpace(OldPosition)
		) --previous position relative to current camera
		Beam.Attachment1.WorldPosition = NewPosition
		
		local TranslatedOldVelocity = Camera.CFrame:PointToWorldSpace(
			PreviousCameraCFrame:PointToObjectSpace(OldVelocity)
		) --previous velocity relative to current camera
		
		setCurveP1(Beam, Beam.Attachment0.WorldPosition + TranslatedOldVelocity*Step)
		setCurveP2(Beam, Beam.Attachment1.WorldPosition - NewVelocity*Step)
		--these two set the bezier curves of the beam so it is not a relevant function
		--math for these functions were obtained from the roblox docs related to beams
		
		Data[3] = NewVelocity
		Data[2] = NewPosition
	end
	
	PreviousCameraCFrame = Camera.CFrame
end)

I can’t seem to find a solution to what I could be doing wrong, I am mainly seeking for a better way to properly replicate a bullet tracer effect with motion blur.

3 Likes

The beam documentation explains how control points are calculated from beam parameters. Using that, I wrote this function to set the control points:

local function setBeamControlPoints(
	beam: Beam,
	attachment0: Attachment, attachment1: Attachment,
	controlPoints: {Vector3}
)
	attachment0.WorldPosition = controlPoints[1]
	attachment1.WorldPosition = controlPoints[4]

	local vec12: Vector3 = controlPoints[2] - controlPoints[1]
	local vec34: Vector3 = controlPoints[4] - controlPoints[3]
	
	attachment0.WorldAxis = vec12
	attachment1.WorldAxis = vec34
	beam.CurveSize0 = vec12.Magnitude
	beam.CurveSize1 = vec34.Magnitude
end

A bezier curve doesn’t actually pass through the second and third control points. The answer you linked did a bit of math so that they will:

local refit2: Vector3 = (-5*controlPoints[1] + 18*controlPoints[2] - 9*controlPoints[3] + 2*controlPoints[4]) / 6
local refit3: Vector3 = (2*controlPoints[1] - 9*controlPoints[2] + 18*controlPoints[3] - 5*controlPoints[4]) / 6

By keeping track of previous positions in view space and reprojecting them back into world space, you can get results like in that answer. You may also want to increase transparency when screen space motion is high.

That method works well for a “motion blurred” point, but I think your bullets are line shaped? If I’m not mistaken it wouldn’t work for lines, because a “blurred” point will become a line (bezier curve in this case), but a “blurred” line will become another kind of surface (possibly a bezier surface?).

Something similar could work for lines, but I’m not sure if the blur could be curved. Maybe I can look into it some more.

Interesting question!

1 Like