6

I have problems to understand the field of view of the perspective camera in libGDX (or maybe my calculations are wrong).

I want to draw a box (for example 800px wide, 480px high and 20px deep). The box is placed between x- and y-axis (in other words: the width on x-axis and the height on y-axis). Now I want to stand behind the box on the x-axis (camera position) and look in a direction in such a way that I see the box on my very right screen side. I made some sketches:

drawing the box

camera on x-axis, looking at box

box should appear on right side

On the last sketch, the box is on the right side of the field of view.

Moreover, the height of the box must fit exactly in the screen. In other words: the top edge of the box must be on my screen top and the bottom edge of the box must be on my screen bottom. Here is some code and calculations to achieve this:

public class Box implements ApplicationListener {

public PerspectiveCamera camera;
public float SCREEN_WIDTH;   
public float SCREEN_HEIGHT; 

@Override
public void create() {
    SCREEN_WIDTH = Gdx.graphics.getWidth(); // 800px
    SCREEN_HEIGHT = Gdx.graphics.getHeight(); // 480px

    // Create camera (90°) and set position
    camera = new PerspectiveCamera(90f, SCREEN_WIDTH, SCREEN_HEIGHT);
    camera.position.set(SCREEN_WIDTH + SCREEN_HEIGHT/2, SCREEN_HEIGHT/2, 0f);
    camera.lookAt(0f, SCREEN_HEIGHT/2, 0f);
    camera.near = 1;
    camera.far = 2 * SCREEN_WIDTH;
    camera.update();
}

@Override
public void render() {
    Gdx.gl.glClearColor(Color.WHITE.r, Color.WHITE.g, Color.WHITE.b, Color.WHITE.a);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT); 

    // Build the box - width is screenwidth and height is screenheight - one corner in origin
    ModelBuilder modelBuilder = new ModelBuilder(); 
    modelBuilder.begin();
    MeshPartBuilder mpb = modelBuilder.part("ID", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal,
            new Material(ColorAttribute.createDiffuse(Color.GREEN)));
    // bottom left corner of the box should be in (0,0,0)
    mpb.box(SCREEN_WIDTH/2, SCREEN_HEIGHT/2, -10f, SCREEN_WIDTH, SCREEN_HEIGHT, 20f);
    Model model = modelBuilder.end();
    ModelInstance instance = new ModelInstance(model);

    // Build some light
    Environment environment = new Environment();
    environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
    environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));

    // Draw the box
    ModelBatch modelBatch = new ModelBatch();
    modelBatch.begin(camera);
    modelBatch.render(instance, environment);
    modelBatch.end();
}
}

To fit the box height in the screen I must step back SCREEN_HEIGHT/2, because x = SCREEN_HEIGHT/2 / tan(45°) = SCREEN_HEIGHT/2 / 1 = SCREEN_HEIGHT/2, that's why I added this length to the x-component in the position setting.

The code represents sketch 2 and give me that correct screenshot:

screenshot of sketch 2

In order to achieve sketch 3, I must rotate my field of view about 45° to the left. But in this connection I must step back a little, because the sides of the field of view are longer than the centerline. I made two hand-drawn sketches:

rotation of field of view

Where h = SCREEN_HEIGHT. First of all, I have to compute x. x = h/2 / sin(45°) = h/sqrt(2). Now I must step back a length of x and look in a new direction (45° left from box). To calculate the point at which I will looking, I calculate the length y on this sketch:

calculation y

y = sin(45°) * h/2 = h * sqrt(2)/4. So, the point is for example (SCREEN_WIDTH + (x-y), SCREEN_HEIGHT/2, y). Now I change the position and looking point in my code:

public void create() {
    SCREEN_WIDTH = Gdx.graphics.getWidth(); // 800px
    SCREEN_HEIGHT = Gdx.graphics.getHeight(); // 480px

    // Create camera (90°) and set position
    camera = new PerspectiveCamera(90f, SCREEN_WIDTH, SCREEN_HEIGHT);
    float x = (float) (SCREEN_HEIGHT / Math.sqrt(2));
    float y = (float) (SCREEN_HEIGHT * Math.sqrt(2)/4f);
    camera.position.set(SCREEN_WIDTH + x, SCREEN_HEIGHT/2, 0f);
    camera.lookAt(SCREEN_WIDTH + x - y, SCREEN_HEIGHT/2, y);
    camera.near = 1;
    camera.far = 2 * SCREEN_WIDTH;
    camera.update();
}

And I get this screen:

screenshot which should represent sketch 3

But the box isn't on the very right side :(

So now the question: what is wrong? Is my thinking of the field of view wrong or my calculations?

Best regards!

Namenlos
  • 1,615
  • 2
  • 18
  • 24

1 Answers1

0

Your understanding of the PerspectiveCamera is a bit off, specifically what it does with the second(viewportWidth) and the third(viewportHeight) argument.

The only thing the PerspectiveCamera does with them is get the aspect ratio and use it to set the projectionMatrix. For the specific code it uses see this line that calculates the aspect ratio and this method that sets the projectionMatrix.

Now this is all well and good, except that you also do the math to deal with the aspect ratio, and doing it twice causes the camera to stretch again. The solution is quite simple, either redo your math to not adjust for the aspect ratio or replace

camera = new PerspectiveCamera(90f, SCREEN_WIDTH, SCREEN_HEIGHT);

with

camera = new PerspectiveCamera(90f, 1, 1);

to make the aspect ratio that libgdx calculates be 1.

I'm not so good with sine and cosine so I can't provide solutions to the first option, but you don't seem to be having any problems with difficult math.

Mim
  • 453
  • 4
  • 6