Jump to content
  • Advertisement
Sign in to follow this  
Danicco

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

This topic is 2135 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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

Share this post


Link to post
Share on other sites
Advertisement


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.

Share this post


Link to post
Share on other sites

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!

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!