title: LibGDX - 3D motion effect
body: <p>I need to implement motion effect (hyper speed) in 3D space. For this purpose I decided to use decals. I place 1000 particles in limited space in random order and use camera position to move them in opposite direction to create hyper speed effect. Perhaps, it's not a perfect solution and would be better to create particles right in front of the camera or use shader and frame buffer instead. Anyway, the problem is I need to stretch these particles in right direction depending on the movement of camera. In fact, they are already stretched, but needed to be rotated. To clarify that, let's look at the screenshots below: </p>
  
  <p><a href="https://i.stack.imgur.com/e5l5L.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/e5l5L.png" alt="left &amp; right dir"></a></p>
  
  <p><a href="https://i.stack.imgur.com/PDf4k.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/PDf4k.png" alt="up &amp; down dir"></a></p>
  
  <p><a href="https://i.stack.imgur.com/eo2BY.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/eo2BY.png" alt="diagonal dir"></a></p>
  
  <p><a href="https://i.stack.imgur.com/7tfMt.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/7tfMt.png" alt="forward &amp; backward dir"></a></p>
  
  <p>Well, I hope the main idea is clear. Also, I want to provide my class:</p>
  
  <pre><code>public class MotionEffect {
  
  Camera camera;
  Vector3 oldCameraPos, displacement;
  
  Vector3 prjCamPos;
  Vector3 prjOldPos;
  
  Vector3 rotationVec;    
  DecalBatch decalBatch;
  private Texture dustTex;
  
  
  private class Particle{
  
      float size = 1.5f;
      final float areaSize = 250;
      Decal decal;
      float x, y, z;
      float dx, dy ,dz;
      float r, g, b, a;
  
      public Particle(TextureRegion texRegion){
  
          decal = Decal.newDecal(size, size, texRegion, true);
  
          r=1; g=1; b=1;
          init();
      }
  
      private void init() {
          a = 1;
  
          x = MathUtils.random(-areaSize, areaSize) + Settings.SunPosition.x;
          y = MathUtils.random(-areaSize, areaSize) + Settings.SunPosition.y;
          z = MathUtils.random(-areaSize, areaSize) + Settings.SunPosition.z;
  
      }
  
      public void update(Vector3 displacement, float alpha, float angle) {
  
          dx = -displacement.x;
          dy = -displacement.y;
          dz = -displacement.z;
  
          x += dx;
          y += dy;
          z += dz;
  
  
          if ((x &lt; -areaSize || x &gt; areaSize) || (y &lt; -areaSize || y &gt; areaSize) || (z &lt; -areaSize || z &gt; areaSize)) init();
  
  
          decal.setColor(r, g, b, 1);
          decal.setPosition(x, y, z);
  
          decal.setWidth(size * 20 * alpha);
  
          //decal has to be always faced to the camera
          decal.setRotation(camera.direction, camera.up);
  
          decal.rotateZ(angle); //rotate decal according to the camera movement
          //decal.rotateY(90); //rotate particle to forward - backward dir
  
      }
  
      public void render(DecalBatch decalBatch){
          float distance = decal.getPosition().dst(camera.position);
          if(distance&gt;270) return;
          if(distance&lt;30) return;           
          decalBatch.add(decal);
      }
  }
  
  int countOfParticles=1000;
  Particle[] particles;
  
  Vector2 original;
  Vector2 direction;
  
  public MotionEffect(Camera camera){
  
      this.camera = camera;             
  
      oldCameraPos = new Vector3(camera.position);
      displacement = new Vector3();
  
      decalBatch = new DecalBatch(new DustGroupStrategy(camera));
      dustTex = new Texture(Gdx.files.internal("textures/shine.png"));
      TextureRegion dustTexReg = new TextureRegion(dustTex);
  
      particles = new Particle[countOfParticles];
      for(int i=0; i&lt;countOfParticles; i++){
          particles[i] = new Particle(dustTexReg);
      }
  
      rotationVec = new Vector3();
  
      original = new Vector2(0, 10);
      direction = new Vector2();
  
      prjCamPos = new Vector3();
      prjOldPos = new Vector3();
  }
  
  private float calcMovement(Camera camera, float delta) {
      float lerp = 0.1f;
      Vector3 cameraPosition = camera.position;
  
      float x = (cameraPosition.x - oldCameraPos.x) * lerp ;
      float y = (cameraPosition.y - oldCameraPos.y) * lerp ;
      float z = (cameraPosition.z - oldCameraPos.z) * lerp ;
  
      oldCameraPos.x += x;
      oldCameraPos.y += y;
      oldCameraPos.z += z;
  
      prjCamPos.set(cameraPosition);
      prjCamPos.add(.1f,.1f,.1f);
      prjOldPos.set(oldCameraPos);
      prjOldPos.add(.1f,.1f,.1f);
      camera.project(prjCamPos);
      camera.project(prjOldPos);
      Gdx.app.log("sds", prjCamPos+" - "+prjOldPos);
  
      float del = .1f;
      x /= del;
      y /= del;
      z /= del;
  
      displacement.set(x, y, z);
      float alpha = (Math.abs(displacement.x) + Math.abs(displacement.y) + Math.abs(displacement.z)) / 3f;
      if(alpha&gt;1) alpha = 1;
      return alpha;
  }
  
  //ATTEMPT TO CALC THE ANGLE
  public float getAngle(Vector3 oldPoint, Vector3 newPoint) {
      float angle = (float) Math.toDegrees(Math.atan2(newPoint.y - oldPoint.y, newPoint.x - oldPoint.x));
  
      if(angle &lt; 0){
          angle += 360;
      }
  
      return -angle;
  }
  
  
  //ATTEMPT TO CALC THE ANGLES
  float angleX, angleY, angleZ;
  
  private void calcAngle(){
      Vector3 thisPoint = oldCameraPos;
      Vector3 targetPoint = camera.position;
  
      float someSmallEpsilon = 0.01f;
  
      Vector3 difference = targetPoint.sub(thisPoint);
  
      angleX = 0;
  
      if (difference.len() &lt; someSmallEpsilon)
      {
          // Objects are at the same location - can't
          // sensibly compute a direction to look at
          return;
      }
      Vector3 direction = difference.nor();
  
      Vector3 xAxis = new Vector3(1,0,0);
  
      float dotProduct = xAxis.dot(direction);
      angleX = (float) Math.acos(dotProduct);
  
      if (angleX &lt; someSmallEpsilon)
      {
          // Angle is already right. Nothing to do
          return;
      }
  
      Vector3 axis = xAxis.crs(direction).nor();
      Quaternion result = new Quaternion();
      result.setFromAxisRad(axis, angleX);
  
  }
  
  public void render(Camera camera){
  
      float alpha = calcMovement(camera, Gdx.graphics.getDeltaTime());
  
      if(alpha&lt;=0) return;
  
      //calcAngle();
      float angle = getAngle(prjCamPos, prjOldPos);
  
      for(int i=0; i&lt;countOfParticles; i++){
          particles[i].update(displacement, alpha, angle);
          particles[i].render(decalBatch);
      }
      decalBatch.flush();
  }
  }
  </code></pre>
  
  <p>Any help would be appreciated.</p>