Code Snippet: Faking unlimited viewing distance

Started by Irony, December 29, 2013, 01:11:04 PM

Previous topic - Next topic

Irony

From time to time, I will post a few code snippets that proved useful while working on Armada. Maybe someone can use something for their own projects.

One problem that I ran into was the limited viewing distance. I thought that just setting the farplane value high enough would solve it, but there is actually a hard limit given by the hardware and/or OpenGl (not sure about that).
I wanted to have a planet that is far away but still visible (and approachable). While it would be possible to fake something with billboards, I found a solution that uses real geometry and actually pretty simple.


public class Planet extends Object3D { //It's better to not use Object3D directly for your game objects, but I do it here for simplicity

  private SimpleVector realPosition, camPos = new SimpleVector();
  private SimpleVector v = new SimpleVector; // temporary vector
  private final static MAX_VIEWING_DISTANCE = 5000; //this should be a safe value for most devices; make sure your farplane value is higher than this

  public Planet(SimpleVector position) {
      ... // init Object3D here

     translate(position);
     realPosition = new SimpleVector(position); //Save "real" position of planet in another vector as the object's physical position will change
  }

  // this is called in every frame update
  public void update() {
   
    world.getCamera().getPosition(camPos); //position of used camera

    float dist = realPosition.distance(camPos);  //calculate distance camera<->Planet

    if (dist>MAX_VIEWING_DISTANCE) { //outside of the viewing distance -> do projection magic

setScale(MAX_VIEWING_DISTANCE / dist);    //shrink the object

     // Now that we made the object smaller, put it closer to the camera

v.set(realPosition); //Set helper vector to planet position
v.sub(camPos); //calculate direction vector from camera to planet
v.normalize(v); //and normalize it
v.scalarMul(MAX_VIEWING_DISTANCE); // Calculate new position within viewing distance
        v.add(camPos);  // add camera vector to get final object position

  //Move it!
        clearTranslation();
        translate(v);
   }

   else { //planet is within viewing distance; reset size and position to real values
setScale(1);
        clearTranslation();
translate(realPosition);
  }

Irony

Added to the Wiki. I think we should still keep this thread open in case there any questions.

Lobby

I think so. Could you also add a link there to this Topic? Then it would make a lot more sense ;) .

Nice code snippet, but don't forget that it is possible to determine a difference between a really far object and a not that far, scaled object. The really far object will look more flat than the other one (but I think nobody will ever notice that in a real program).

Irony

Quote from: Lobby on December 30, 2013, 10:56:14 AM
I think so. Could you also add a link there to this Topic? Then it would make a lot more sense ;) .

Nice code snippet, but don't forget that it is possible to determine a difference between a really far object and a not that far, scaled object. The really far object will look more flat than the other one (but I think nobody will ever notice that in a real program).

Thought it would be easy enough to find but here you go
http://www.jpct.net/wiki/index.php/Fake_unlimited_viewing_distance

About your objection: Are you sure this effect isn't only present in stereoscopic (real) 3D?

Lobby

I meant you could link from the wiki article to this Topic here ;) .

Quote from: Irony on December 30, 2013, 11:22:09 AM
About your objection: Are you sure this effect isn't only present in stereoscopic (real) 3D?

I'm really sure. Look at this short sample:
world = new World();

piv = Object3D.createDummyObj();
camPiv = Object3D.createDummyObj();
camPiv.addParent(piv);
camPiv.translate(0, 0, -10);

Light light = new Light(world);
light.setPosition(new SimpleVector(-5, -5, -5));

TextureManager mgr = TextureManager.getInstance();
Resources res = getResources();
mgr.addTexture("texture", new Texture(res.openRawResource(R.raw.tex)));

SimpleVector vec;

Object3D sp1 = Primitives.getSphere(3.5f);
sp1.rotateY(0.4f);
sp1.rotateX(-0.1f);
vec = sp1.getZAxis();
vec.scalarMul(25f);
sp1.translate(vec);
sp1.calcTextureWrapSpherical();
sp1.setTexture("texture");
world.addObject(sp1);

Object3D sp2 = Primitives.getSphere(40f);
sp2.rotateY(-0.4f);
sp2.rotateX(-0.1f);
vec = sp2.getZAxis();
vec.scalarMul(400f);
sp2.translate(vec);
sp2.calcTextureWrapSpherical();
sp2.setTexture("texture");
world.addObject(sp2);


Which leads to this:


It's a really extreme sample, but I think it's visible that the left sphere seems to be more flat.

EgonOlsen

It's different, because the scaling ignores the perspective projection. However, the farer away the object is, the less this counts. So for objects that are far away, this is a reasonable approach.

Irony