You probably don’t want to compute the offset from the current dust position every frame. That makes the dust “chase itself” and can give unstable or confusing results.
Instead, keep a fixed offset behind the car, based on the car direction or camera direction. For example, if the car only moves along Z:
const dustOffset = new THREE.Vector3(0, 0, -2); // behind car, adjust value
dust.position.set(
samplePickRCCar.position.x,
camera.position.y,
samplePickRCCar.position.z + dustOffset.z
);
If the car can rotate, use its world direction:
const dir = new THREE.Vector3();
samplePickRCCar.getWorldDirection(dir);
const dustOffset = dir.multiplyScalar(-2); // behind car
dust.position.copy(samplePickRCCar.position).add(dustOffset);
dust.position.y = camera.position.y;
Then update the particle positions inside the dust local space as you already do. The important part is: the particles should move relative to the dust object, and the dust object should be placed behind the car with a stable offset.
I created a smoke trail for my airplane that consisted of a fixed number of smoke sprites. Every frame or two, I would leave one at the current position of the airplane. The sprite would remain in that location, slowly fading out until I again placed the sprite at the current position of the airplane.
Of course, you have to compute the sprite distance by subtracting the camera map position from the the sprite map position.
For a car on a dirt road, you could do the same thing, but with fewer sprites. In addition to fading, the sprite could grow in size with each frame.
While this approach does not use a shader or linked objects, my understanding is that sprites that share the same texture are processed together in the GPU.