• Create Account

How should I deal with 2D rotations in NDC space?

Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

2 replies to this topic

#1Danicco  Members   -  Reputation: 323

Like
0Likes
Like

Posted 09 September 2013 - 12:13 AM

I'm having some trouble figuring this out, if it's a math problem or my quaternion rotation or a design issue...

I have an image class that has a texture and creates a Canvas class (which has a VBO) and I need to specify it's width and height.

At first, I didn't use an orthographic matrix for projection, and I just calculated it in NDC:

void Canvas::SetCanvas(int canvasWidth, int canvasHeight)
{
float canvasWidthNDC = canvasWidth / screenWidth;
float canvasHeightNDC = canvasHeight / screenHeight;

//Create a simple Quad VBO
};


So basically I'd calculate the size of the pixel I want, divide it by the screen's resolution, and I'd have a [-1,1] value for the width/height I specified.

But with this code I was having issues with rotations, it seems the object was getting skewed when it was anything other than 0 or 180 degrees.

I assume it's because the Quad was set to, for example a 100x100 image, a 0.052 width x 0.092 height.

This looks fine if don't rotate, but if I do, the height/width doesn't matter anymore because they're not square.

When the screen resolution was square, the rotation looked fine.

So I looked for solutions and I ended up getting an orthographic matrix projection given by:

Matrix4x4 Math::GetOrthographicProjection(float left, float right, float bottom, float top)
{
Matrix4x4 result;
result[0][0] = 1;
result[1][1] = 1;
result[2][2] = 1;
result[3][3] = 1;

result[0][0] = 2 / (right - left);
result[1][1] = 2 / (top - bottom);
result[2][2] = -1;
result[3][0] = -(right + left) / (right - left);
result[3][1] = -(top + bottom) / (top - bottom);

return result;
};


And I multiply this by the object's transformation matrix, and I'd get the vertex's values exactly as I specified in pixels (it'll always be 100x100) and rotations started to look perfect with this.

But now I'm having a problem with objects and different resolutions.

I got a 2D background larger than the player's screen (like a platform/maze game) where the camera follows the player, but depending on the player's resolution he's being able to see more or less than intended.

I'd like to keep it in NDC so it's always resolution unrelated, but then my rotation (yes, the background rotates depending on the player actions, and the visible "box" for the background needs to be accounted for) gets all messed up.

What's the proper way to deal with this?

Is there a way to implement proper 2D rotation in NDC without getting distortions? I assume I'd need to map the buffer and change it constantly, not sure if it's a good idea.

Or do games usually limit the image to a specific size and use the scale component to adjust depending on the screen's resolution? (then I'd need to worry about uncommon resolutions, such as if the user changes the orientation of the mobile and the screen changes too)

Or is this an issue of game resource design?

Any help is highly appreciated!

Edit: I forgot to add, this might be related too (if it's a math issue), but this is my quaternion's rotation matrix:

Matrix4x4 Quaternion::GetMatrix()
{
Matrix4x4 result;

//Quaternion is normalized
result[0][0] = 1 - (2 * y * y) - (2 * z * z);
result[0][1] = (2 * x * y) - (2 * w * z);
result[0][2] = (2 * x * z) + (2 * w * y);
result[0][3] = 0;

result[1][0] = (2 * x * y) + (2 * w * z);
result[1][1] = 1 - (2 * x * x) - (2 * z * z);
result[1][2] = (2 * y * z) + (2 * w * x);
result[1][3] = 0;

result[2][0] = (2 * x * z) - (2 * w * y);
result[2][1] = (2 * y * z) - (2 * w * x);
result[2][2] = 1 - (2 * x * x) - (2 * y * y);
result[2][3] = 0;

result[3][0] = 0;
result[3][1] = 0;
result[3][2] = 0;
result[3][3] = 1;

return result;
}


And I'd rotate the 2D image just by the Z axis.

Edited by Danicco, 09 September 2013 - 12:19 AM.

#2haegarr  Crossbones+   -  Reputation: 2370

Like
0Likes
Like

Posted 09 September 2013 - 02:13 AM

At first, I didn't use an orthographic matrix for projection, and I just calculated it in NDC:
void Canvas::SetCanvas(int canvasWidth, int canvasHeight)
{
float canvasWidthNDC = canvasWidth / screenWidth;
float canvasHeightNDC = canvasHeight / screenHeight;

//Create a simple Quad VBO
};
So basically I'd calculate the size of the pixel I want, divide it by the screen's resolution, and I'd have a [-1,1] value for the width/height I specified. ...

What do you mean with "sIze of the pixel" here? You divide the canvas size component-wise by the screen resolution, all being positive values, so the resulting values will be in [0,1].

... But with this code I was having issues with rotations, it seems the object was getting skewed when it was anything other than 0 or 180 degrees.
I assume it's because the Quad was set to, for example a 100x100 image, a 0.052 width x 0.092 height.
This looks fine if don't rotate, but if I do, the height/width doesn't matter anymore because they're not square.
When the screen resolution was square, the rotation looked fine.

The consecutive NDC to DCS transformation will consider the (typically) non-square dimensions of the viewport by doing a non-uniform scaling. This MUST cause the deformations you observed; see e.g. the 2nd paragraph in the wikipedia article here. If you screen resolution is equal in both dimensions, then the scaling is uniform and the said deformations don't occur.

... But now I'm having a problem with objects and different resolutions.
I got a 2D background larger than the player's screen (like a platform/maze game) where the camera follows the player, but depending on the player's resolution he's being able to see more or less than intended.
I'd like to keep it in NDC so it's always resolution unrelated, but then my rotation (yes, the background rotates depending on the player actions, and the visible "box" for the background needs to be accounted for) gets all messed up. ...

Doing things in NDC does not help here. You just use a normalized co-ordinate system, but you neglect to consider the aspect ratio. People may use 4:3, or 16:9, or 16:10, or some less typical aspect ratio. The NDC doesn't consider this, but the DCS does, and hence you're still out of luck.

So let us think about solutions...

The first thing is to go away from NDC. If you want to use a resolution independent co-ordinate system, then I suggest to normalize by using the vertical extent for both dimensions, so resulting in ranges [-1,+1] for the vertical and [-a,+a] for the horizontal dimension, where a means the aspect ratio of the screen size. Please notice that I've written "screen size" but not "screen resolution", because you cannot rely on square pixels. Well, this is a problem usually neglected.

The second thing is to place elements relative to the center of the viewport. Using symmetrical ranges already hints at that.

The third thing is to think about what users with different aspect ratios are allowed to see. If all users are required to see exactly the same amount of the canvas, then there are exactly 2 ways: Either you scale non-uniformly and get some stretching in one dimension, or else you keep the ratio and surround some letter- or pillarbox. On the other hand you may consider to show or hide a bit of the canvas for the case that the user's screen size doesn't match the design's aspect ratio.

#3Danicco  Members   -  Reputation: 323

Like
0Likes
Like

Posted 09 September 2013 - 06:53 PM

I hadn't thought of using a letterbox and force a specified ratio, that's a great idea.

I'll just have to calculate the difference once during startup and set the viewport accordingly, and I think I won't have to worry about the player seeing less or more than intended.

Thank you very much for the idea!

Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

PARTNERS