What is the math behind the angled ortho camera and sprite stretching?

Hello!

I recently started a project and decided it was worth the while to go all in and migrate the 2D setup to 3D. Luckily I wasn’t too deep into it, but as I’m fairly new to programming, figuring stuff out is always a hurdle of its own.

I went from this: (2D flat w/ orthographic camera)

Which was pixel perfect for the most part

To this: 3D ortho pointing at 45 degrees, and everything weirdly stretched to 1.5x of its Y axis

My question is, what are the calculations I’d have to make to get it back to aspect correctly? How do I extract the exact ammount of stretching that’s equivalent to the angled view? I’ve seen a video of the guys from Dungeon of Endless doing it, where they moved it to a grid until they got it perfectly, which I also tried, but it seems to be a very… messy way of doing it. I’m assuming it needs code.

I’m using the camera’s ortho size at 90. I’ve been analyzing this picture to try and figure it out a bit, and I’m not entirely sure if the whole world is just rotated, if the walls are at 90º or not, or if there’s something shady and wizardous going on that I’m clearly missing. I know you can rotate the sprite but I’m hoping to be able to make them cast shadows eventually, and I’m not sure if that would mess up with the projection or not (streching being more convenient)

I apologize if this has been tackled before, I really want to understand the math behind it, and it boggles me quite a lot I can’t even put a name on it to be able to search something similar.

(yes I literally challenged myself to attempt to clone the EtG engine having the barebones of C# skills)

Some math and beautiful artwork heading your way:
3275700--253254--upload_2017-11-3_14-18-19.png
Imagine that the camera is the circle and that the object being viewed is the box. The detailed drawing above is a side view of this example-- the object is “laying flat” on the “ground” of your scene.

The height of the object is h. When we view it using a flat projection from a viewpoint that may be at an angle to the horizontal, the apparent height is ho, which is the distance between the top and bottom points of the object when projected onto a plane whose normal is the same direction that the camera is pointing-- this is the same as the shortest (perpendicular) distance between the two lines that come off of the top and bottom of the object at the same angle as the camera, θ (in radians).

After figuring this out, we can make a right triangle with a known side and angle, and use some trig to come up with:

ho`` = h * cos θ

When we use the 0 rad case (unrotated camera), we just get

ho`` = h

which checks out. The apparent height is just the height of the object before we rotate.

When we apply a rotation, the apparent height changes predictably by a factor of cos θ.

Now, your goal is to preserve the apparent height by applying some h to a new θ. The apparent height (call it ho1) before rotating the camera is just the actual object height, which we can call h1. We then change the angle to 45° (π/4 rad), and desire a new actual height h2 that gives an apparent height ho2 that is the same as before the angle changed (this was h1). And so

ho2 = ho1 = h1 = h2 * cos θ

and

h2 = h1`` / cos θ

And there we have it. If you want to angle your camera by θ radians, or πθ/180 degrees, then you can divide the height by cos θ to get the new height that will make the apparent height not change. For 45°, this is approximately 1.41 times the original height. If you wanted, you could even have the relevant renderers react to the main camera’s angle by changing their y scale dynamically…

Could I ask why you want to do it this way? I know it can help with perspective, but it’s not always necessary, and your method seems pretty different from Gungeon’s if you’re trying to emulate them.

Let me know if I misunderstood anything.

3275700--253254--upload_2017-11-3_14-18-19.png

3 Likes

Thank you so much!! Okay so a lot happened since. I couldn’t stand still and I thought I wasn’t going to get a reply so I started scouring the internet for it and I managed to recover some stuff from actually re-watching the Dungeon of the Endless GDC video closely. They used a formula which is pretty similar to what you’re describing here.

Their formula worked because they used 60º and the Y scaling result was an integer of 2 and they did it so as to avoid huge float numbers. After tinkering a while I found out mine wasn’t initially working because I was doing the math wrong ( y * 1 / cos(45) instead of y * (1/cos(45)) and later on realizing that I had to use degrees and not radians )
I’m more of an art guy, you might have noticed by now…

After getting that 1.41 you talk about I was still experiencing pixel bleeding (didn’t even know how to call it until today) which the snap to pixel grid material option seemed to fix, to a degree, but I wasn’t getting perfect squared pixels yet, something was still off.

Just a few hours ago I stumbled into a wonderful reddit post where a lot of devs jumped in to talk about this way of approaching fake 2d top down, and it just so happened that one of the Gungeon devs explained it there, so I gave it a second(eleventh) chance and tried to replicate it.

What I managed to get is that the camera is always at 0º and the sprites are rendered flat vertical, and the floor and other stuff is rendered at 45º with x1.38 Y scaling, which I just typed randomly until the tearing went away but now I realize that it’s pretty much the 1.41 of the angle equation. Art guy, I know.
At least it looked like everything was on an angle on the picture, but then when I tried breaking it using flat vertical walls and horizontal floor with 45º camera angle (original configuration) it seemed to aspect correctly all the same.

There’s one more bit to add though: while some of the approachs were more gentle on the tearing, I did notice that it truly became unbreakable when I changed the size of my ortho from 90 to 180 which is the exact half of my game’s screen height (640x360). I don’t know if I had tried this before, but, you know, press all the buttons until it works, right?

Honestly, after all this hassle, I’m really questioning whether I should truly migrate to 3D or not… I wanted to do it so I could push the rendering limits to their best but after all that I’ve read, everyone is saying that you can pretty much achieve the same on both. I might have to leave that one for another topic.

Anyways, I truly appreciate that you’ve taken the time to answer so educationally. I’m making this whole step-by-step wall of text reply so that any future googler might benefit from it later on.

Thank you!

1 Like