[SOLVED] Perspective Camera "Eye Level"

Anything libgdx related goes here!

[SOLVED] Perspective Camera "Eye Level"

Postby darc » Sun Mar 11, 2018 12:29 am

Hi Guys,

First of all, apologies for the terminology I use in this post, I'm still fairly new to the world of 3D and will likely use the wrong term for a few things.

I'm making a fairly simple card game and have opted to use a perspective camera as it provides me some of the effects I wanted for free (cards getting smaller as they move away from the screen etc).

The issue I'm having is I want to change where the camera treats as eye level (i.e. the point where any models you are looking at have no perspective effect on the Y axis.

For example, the following image is what I currently have:

Image

In this image the area I'm referring to eye level is the center of the screen on the Y axis.

I would like to place the cards closer to the bottom of the screen while maintaining this look. I have tried playing around with the camera position, translation and lookat() values but I always end up with an image similar to this:

Image

Is there a way that I can offset what the camera is centered on so that I maintain the effect of the first image while using the positioning of the image? I understand that this will make the effect of any cards placed closer to the top of the screen more "extreme" as they are further from the camera but that's fine.

Thanks for any help you can provide
Darc
Last edited by darc on Sat Mar 24, 2018 12:02 pm, edited 1 time in total.
darc
 
Posts: 8
Joined: Fri Jun 16, 2017 1:48 pm

Re: Perspective Camera "Eye Level"

Postby CodeMaven » Mon Mar 12, 2018 2:06 pm

I think this comes down to your viewport. A 3d perspective camera is always going to produce a proper 3d scene from the perspetive of the camera at the middle... Anything like you are describing would not be a natureal view of the world as we see it.

So I think wha tyou need to do to emulate this is have the viweport extend vertically off the screen below your physicl screen so the 3d image is being drawn in a much larger window but the bottom half of it is just off the screen.

If I were to guess at what you were trying to achieve I would think that you're trying to make a rolodex view of the player's hand where they can scroll through the cards in their hand with the middle one the currently selected card... and thus you want this view to be at the bottom of the screen rendered in a different perspective than the rest of the table. As you menitioned, the above approach would through the view of the entire game way out. What I would do instead is render in two passes. I would render the cards exactly as in the first image to a framebuffer object, then render the table / play area to the screen and either layer the image from the FBO on top at the bottom as a 2d sprite on top with a different camera or create a decal mesh for it in your view of your 3d table and apply the texture from the FBO to that.

Regards,
Troy.
CodeMaven
 
Posts: 71
Joined: Sat Sep 13, 2014 5:24 pm

Re: Perspective Camera "Eye Level"

Postby tenfour04 » Mon Mar 12, 2018 5:46 pm

If you can decipher the math here, you could write a PerspectiveCamera subclass that applies an offset when creating the projection matrix: http://www.paulbourke.net/stereographics/stereorender/

Also, you can Google off-axis projection matrix.
tenfour04
 
Posts: 1179
Joined: Sat Jun 18, 2011 3:24 pm

Re: Perspective Camera "Eye Level"

Postby darc » Wed Mar 14, 2018 7:43 am

Thanks for the replies.

@tenfour04
That math looks a bit beyond me unfortunately but I may look into off access projection.

@CodeMaven
I had considered rendering to a massive viewport but couldn't get it to work and it felt like a horrible hack.

My current solution is to offset all cards on the y axis to counteract the perspective effect which works but it is not quite as smooth as it was previously.
I really like your frame buffer idea so I think I might implement that when I have a chance and see how it holds up.

And yep, a Rolodex view of the hand is roughly what i'm trying to achieve

Thanks again
darc
 
Posts: 8
Joined: Fri Jun 16, 2017 1:48 pm

Re: Perspective Camera "Eye Level"

Postby CodeMaven » Wed Mar 14, 2018 8:51 am

Framebuffers can be a pain to work with in libGdx... at least that's what I've found. They shouldn't be, but I've always found it more frustrating than i expect to get the viewport for the FBO to work right so that it renders as I want it to. But I think that's simply because my brain never properly wraps itelf around viewports... Simple in concept, yet my brain always does a core dump on it... Code wise FBOs are very easy to use I just find that get the effect to look good I am always spending days on tweaking things to get it right on a task that I expect to take no more than a few hours of dev time...
CodeMaven
 
Posts: 71
Joined: Sat Sep 13, 2014 5:24 pm

Re: Perspective Camera "Eye Level"

Postby darc » Thu Mar 15, 2018 9:39 am

I know what you mean. I managed to get rendering to a frame buffer going in about 20 minutes last night then spent 2 hours mucking around with the size of the viewport, camera, frame buffer etc trying to get it rendering at the right size. Its going to be a pain to get going but ultimately it appears to be working well so thanks again for the idea
darc
 
Posts: 8
Joined: Fri Jun 16, 2017 1:48 pm

Re: Perspective Camera "Eye Level"

Postby Magnesus » Thu Mar 15, 2018 10:29 am

You could just, you know, set the card positions, so it looks like the image on the bottom... I mean, it seems like an obvious solution. Leave the camera, set the cards to be lower when they are closer to the center.
Magnesus
 
Posts: 1574
Joined: Sun Sep 25, 2011 3:50 pm

Re: Perspective Camera "Eye Level"

Postby darc » Thu Mar 15, 2018 10:38 am

Thats the hacky solution i was talking about but it doesn't work that well. Its fairly hard to offset the y axis based on the z value and have it line up right
darc
 
Posts: 8
Joined: Fri Jun 16, 2017 1:48 pm

Re: Perspective Camera "Eye Level"

Postby litelitelite » Fri Mar 16, 2018 10:18 pm

Assuming you want a different perspective for these models than the rest of them, it makes sense to me to use a different viewport, custom viewport implementation below:

The main class:
Code: Select all
public class MultiViewportTest extends ApplicationAdapter {
  private static final float FRACTION_SCREEN_TO_USE = 0.2f;

  private ModelBatch _modelBatch;
  private ModelInstance[] _modelInstances = new ModelInstance[9];
  private Viewport _viewportFull;
  private Viewport _viewportPartialTop;
  private Viewport _viewportPartialBottom;

  @Override
  public void create() {
    _modelBatch = new ModelBatch();

    _viewportFull = new FitViewport(1000f, 1000f, new PerspectiveCamera());

    // Not mathematically-rigorous correction to FOV accounts for the smaller viewport size.
    PerspectiveCamera partialViewportCam = new PerspectiveCamera();
    partialViewportCam.fieldOfView *= (1.1f * FRACTION_SCREEN_TO_USE);

    _viewportPartialTop = new PartialScreenFitViewport(1000f, 1000f, partialViewportCam, PartialScreenFitViewport.Alignment.ALIGN_TOP, FRACTION_SCREEN_TO_USE);
    _viewportPartialBottom = new PartialScreenFitViewport(1000f, 1000f, partialViewportCam, PartialScreenFitViewport.Alignment.ALIGN_BOTTOM, FRACTION_SCREEN_TO_USE);

    // Create a pattern of cards.
    Model model = new ModelBuilder().createBox(4f, 5.75f, 1f, new Material(ColorAttribute.createDiffuse(Color.LIGHT_GRAY)), VertexAttributes.Usage.Position);
    for (int i = 0; i < _modelInstances.length; i++) {
      final int rowIndex = (i + 1) / 2;
      final float offsetX = i % 2 == 0 ? rowIndex * -7f : rowIndex * 7f;
      final float offsetZ = rowIndex * -10f - 30f;
      _modelInstances[i] = new ModelInstance(model);
      _modelInstances[i].transform.setTranslation(offsetX, 0f, offsetZ);
    }
  }

  @Override
  public void render() {
    Gdx.gl.glClearColor(0f, 0f, 0f, 1f);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);

    renderToViewport(_viewportPartialTop);
    renderToViewport(_viewportPartialBottom);
    renderToViewport(_viewportFull);
  }

  private void renderToViewport(final Viewport viewport) {
    // Must "apply" each time viewport changes.
    viewport.apply();
    _modelBatch.begin(viewport.getCamera());
    for (ModelInstance modelInstance : _modelInstances) {
      _modelBatch.render(modelInstance);
    }
    _modelBatch.end();
  }

  @Override
  public void resize(final int width, final int height) {
    _viewportFull.update(width, height, false);
    _viewportPartialTop.update(width, height, false);
    _viewportPartialBottom.update(width, height, false);
    super.resize(width, height);
  }

  public static void main(String[] args) throws Exception {
    new LwjglApplication(new MultiViewportTest());
  }
}


The custom viewport:
Code: Select all
public class PartialScreenFitViewport extends FitViewport {
  public enum Alignment {
    ALIGN_TOP, ALIGN_BOTTOM;
  }

  private final Alignment _alignTo;
  private final float _fractionScreenToUse;

  // One solution to finding the correct gutters to use...
  private final Viewport _fullScreenViewport;

  public PartialScreenFitViewport(final float worldWidth, final float worldHeight, final Camera camera, final Alignment alignTo, final float fractionScreenToUse) {
    super(worldWidth, worldHeight * fractionScreenToUse, camera);
    _alignTo = alignTo;
    _fractionScreenToUse = fractionScreenToUse;
    _fullScreenViewport = new FitViewport(worldWidth, worldHeight);
    // Haven't got to the bottom of why this is required here.
    update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
  }

  @Override
  public void update(final int screenWidth, final int screenHeight, final boolean centerCamera) {
    _fullScreenViewport.update(screenWidth, screenHeight, centerCamera);

    Vector2 scaled = getScaling().apply(getWorldWidth(), getWorldHeight(), screenWidth, screenHeight * _fractionScreenToUse);
    int viewportWidth = Math.round(scaled.x);
    int viewportHeight = Math.round(scaled.y);

    int screenY = _alignTo == Alignment.ALIGN_TOP ? (screenHeight - viewportHeight) - _fullScreenViewport.getTopGutterHeight() : _fullScreenViewport.getBottomGutterHeight();
    setScreenBounds((screenWidth - viewportWidth) / 2, screenY, viewportWidth, viewportHeight);

    apply(centerCamera);
  }
}


Result:
multiViewportTest.png
multiViewportTest.png (3.22 KiB) Viewed 315 times
litelitelite
 
Posts: 19
Joined: Sat Jul 15, 2017 9:43 pm

Re: Perspective Camera "Eye Level"

Postby darc » Sat Mar 24, 2018 11:48 am

Hi @litelitelite

Thankyou so much for your reply. After tinkering with your code for a few hours I've managed to get the exact result I was after without having to resort to the frame buffer option (which while it worked, felt much more hacky than this solution).

Just incase its useful to others, here is my modified version of the class you posted:

Code: Select all

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.viewport.FitViewport;
import com.badlogic.gdx.utils.viewport.Viewport;

public class PartialOffsetViewport extends FitViewport {
    public PartialOffsetViewport(final float worldWidth, final float worldHeight, final Camera camera) {
        super(worldWidth, worldHeight, camera);
        update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
    }

    @Override
    public void update(final int screenWidth, final int screenHeight, final boolean centerCamera) {
        Vector2 scaled = getScaling().apply(getWorldWidth(), getWorldHeight(), screenWidth, screenHeight);
        int viewportWidth = Math.round(scaled.x);
        int viewportHeight = Math.round(scaled.y);

        setScreenBounds(0, 0, viewportWidth, viewportHeight);

        apply(centerCamera);
    }
}


The only real difference is I've made it so the viewport can be placed at any location (0,0 for me so it is in the lower left corner).

I'm going to have to get creative when it comes to moving cards between views but I should be able to accomplish that easily enough.

Thanks again to everyone for their help
Attachments
Untitled.png
The final result. The horrible green background shows the bounds of the custom Viewport
Untitled.png (42.19 KiB) Viewed 227 times
darc
 
Posts: 8
Joined: Fri Jun 16, 2017 1:48 pm


Return to Libgdx

Who is online

Users browsing this forum: Google [Bot] and 26 guests