# OpenGL Solved (without matrices!): Orthographic screen to viewport transformation

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

## Recommended Posts

Someone please help me before I lose my mind, go insane (or am I there already?), and start just randomly pressing buttons hoping the problem fixes itself. Like that ever works. :p Just for a little background, I'm writing a UI in OpenGL and I need to convert Windows screen coordinates to viewport coordinates. Yes, it's for the mouse. Traditionally, I would just use the Windows screen coordinates for my coordinate system for simplicity, but with the project I'm working on, it would make my life oh-so-much easier if I could set it up like a Cartesian coordinate system. So, given the Windows coordinate system: left = 0 right = n top = 0 bottom = n I want to convert any given point to the viewport: left = -(n/2.0f) right = n/2.0f top = n/2.0f bottom = -(n/2.0f) Ideally, I'd like to use a matrix. Before someone points me to it, I've already tried the orthographic projection matrix:
{
{ 2/(xmax-xmin),  0,              0,             -((xmax+xmin)/(xmax-xmin)) }
{ 0,              2/(ymax-ymin),  0,             -((ymax+ymin)/(ymax-ymin)) }
{ 0,              0,              2/(zmax-zmin), -((zmax+zmin)/(zmax-zmin)) }
{ 0,              0,              0,              1                         }
}

While I've not worked with them enough to be terribly efficient, I can see by just looking at it that it's going to give me something out of the necessary range (or have I already lost my mind??) Alternatively, I could hard code some math in to figure it out, but being the type of person I am, I'd rather have the correct solution rather than one that works for my specific case. Besides, I'd like to reuse this code in the future, so if I could set the viewport, building the matrix from that, and let it handle the transformation for me, that'd be ideal. So, if someone could help me, I'd be eternally grateful!  I forgot to include the hard coded version that I'm using now, so here it is. Assuming a viewport of: VPleft: -100 VPright: 100 VPtop: 75 VPbottom: -75 VPwidth = 200 VPheight = 150 and a screen of: SCleft: 0 SCright: 1024 SCtop: 0 SCbottom: 768 VPx = VPleft + (x/SCwidth) * VPwidth; VPy = -(VPbottom + (y/SCheight) * VPheight); Unluckily, it only works for that particualar viewport... if I flip the Y, I have to manually change the VPy calculation to not negate the value. [/edit] Thanks all! vr [Edited by - virtuallyrandom on June 13, 2007 2:39:52 PM]

##### Share on other sites
Problem solved, and the implementation is simpler than I thought it would be, although a bit lengthy on the front end...

Given two viewports, one for the Screen and one for the Viewport, the formulas below calculate the correct X and Y coordinates for any Viewport.

Given a Screen:
ScreenLeft   = 0ScreenRight  = 1024ScreenBottom = 768ScreenTop    = 0

Given a Viewport:
ViewportLeft   = -100ViewportRight  =  100ViewportBottom = -75ViewportTop    =  75

Make the following calculations:
ScreenInvertX = ScreenRight < ScreenLeft ? -1 : 1ScreenInvertY = ScreenTop < ScreenBottom ? -1 : 1ScreenWidth   = (ScreenRight - ScreenLeft) * ScreenInvertXScreenHeight  = (ScreenTop - ScreenBottom) * ScreenInvertYScreenMinX    = ScreenLeft < ScreenRight ? ScreenLeft : ScreenRightScreenMaxX    = ScreenLeft < ScreenRight ? ScreenRight : ScreenLeftScreenMinY    = ScreenTop < ScreenBottom ? ScreenTop : ScreenBottomScreenMaxY    = ScreenTop < ScreenBottom ? ScreenBottom : ScreenTopViewportInvertX = ViewportRight < ViewportLeft ? -1 : 1ViewportInvertY = ViewportTop < ViewportBottom ? -1 : 1ViewportWidth   = (ViewportRight - ViewportLeft) * ViewportInvertXViewportHeight  = (ViewportTop - ViewportBottom) * ViewportInvertYViewportMinX    = ViewportLeft < ViewportRight ? ViewportLeft : ViewportRightViewportMaxX    = ViewportLeft < ViewportRight ? ViewportRight : ViewportLeftViewportMinY    = ViewportTop < ViewportBottom ? ViewportTop : ViewportBottomViewportMaxY    = ViewportTop < ViewportBottom ? ViewportBottom : ViewportTop

To convert a Screen coordinate to a Viewport coordinate:
F(ScreenX) = ScreenX * ((ScreenInvertX * ViewportInvertX) / (ScreenWidth / ViewportWidth)) + ViewportLeftF(ScreenY) = ScreenY * ((ScreenInvertY * ViewportInvertY) / (ScreenHeight / ViewportHeight)) + ViewportTop

And to convert a Viewport coordinate back to a Screen coordinate
F(ViewportX) = (ViewportX - ViewportLeft) *               ((ScreenInvertX * ViewportInvertX) / (ViewportWidth / ScreenWidth)) +               (ScreenMinX * ScreenInvertX * ViewportInvertX)F(ViewportY) = (ViewportY - ViewportTop) *               ((ScreenInvertY * ViewportInvertY) / (ViewportHeight / ScreenHeight)) +               (ScreenMinY * ScreenInvertY * ViewportInvertY)

Looks like a ton, huh? You can precalculate almost all of it and reduce it to one multiplication and one addition for each X and Y coordinate being converted from screen to viewport coordinates. Converting from viewport back to screen is almost as fast: one addition, one subtraction, and one multiplication.

Here comes the cool part (well, for us geeks, at least...) Using the screen and viewports defined above, I calculated:
ScreenInvertX = 1ScreenInvertY = -1ScreenWidth   = 1024ScreenHeight  = 768ScreenMinX    = 0ScreenMaxX    = 1024ScreenMinY    = 0ScreenMaxY    = 768ViewportInvertX = 1ViewportInvertY = 1ViewportWidth   = 200ViewportHeight  = 150ViewportMinX    = -100ViewportMaxX    = 100ViewportMinY    = -75ViewportMaxY    = 75       Screen  World        ScreenFor x= -10     -101.953125  -10       0       -100         0       256     -50          256       512     0            512       768     50           768       1024    100          1024       1034    101.953125   1034For y= -10     76.953125    -10       0       75           0       192     37.5         192       384     0            384       576     -37.5        576       768     -75          768       778     -76.953125   778

For this viewport and screen, we expect the coordinate to move from the left to the right and top to bottom. Since it's a Cartesian coordinate system, we'd expect it to, and it apparently does, run from -x to +x and +y to -y.

So, to confirm that it works for more than just this test case, we need to run a representative sample of each viewport we could create. We just proved a normal Cartesian graph, so I'll skip that and we'll try these. For each of the tests, the test coordinate runs from left to right and top to bottom, so we should see a constant movement from left to right and top to bottom.
----------------------------------------X-reversed, Y-normal CartesianLeft:    10Right:  -10Bottom: -10Top:     10        Screen  World         ScreenFor x=  -10     10.1953125    -10        0       10            0        256     5             256        512     0             512        768     -5            768        1024    -10           1024        1034    -10.1953125   1034For y=  -10     10.26041667   -10        0       10            0        192     5             192        384     0             384        576     -5            576        768     -10           768        778     -10.26041667  778Expected: 10 to -10, 10 to -10Got:      10 to -10, 10 to -10----------------------------------------X-normal, Y-reversed CartesianLeft:   -10Right:   10Bottom:  10Top:    -10        Screen   World           ScreenFor x=  -10      -10.1953125     -10        0        -10             0        256      -5              256        512      0               512        768      5               768        1024     10              1024        1034     10.1953125      1034For y=  -10      -10.26041667    -10        0        -10             0        192      -5              192        384      0               384        576      5               576        768      10              768        778      10.26041667     778Expected: -10 to 10, -10 to 10Got:      -10 to 10, -10 to 10----------------------------------------WindowsLeft:    0Right:   800Bottom:  600Top:     0        Screen     World        ScreenFor x=  -10        -7.8125      -10        0          0            0        256        200          256        512        400          512        768        600          768        1024       800          1024        1034       807.8125     1034For y=  -10        -7.8125      -10        0          0            0        192        150          192        384        300          384        576        450          576        768        600          768        778        607.8125     778Expected: 0 to 800, 0 to 600Got:      0 to 800, 0 to 600----------------------------------------Subset Cartesian blockLeft:   20Right:  30Bottom: 40Top:    50        Screen    World            ScreenFor x=  -10       19.90234375      -10        0         20               0        256       22.5             256        512       25               512        768       27.5             768        1024      30               1024        1034      30.09765625      1034For y=  -10       50.13020833      -10        0         50               0        192       47.5             192        384       45               384        576       42.5             576        768       40               768        778       39.86979167      778Expected: 20 to 30, 50 to 40Got:      20 to 30, 50 to 40----------------------------------------Reversed subset blockLeft:   80Right:  60Bottom: 120Top:    110        Screen   World           ScreenFor x=  -10      80.1953125      -10        0        80              0        256      75              256        512      70              512        768      65              768        1024     60              1024        1034     59.8046875      1034For y=  -10      109.8697917     -10        0        110             0        192      112.5           192        384      115             384        576      117.5           576        768      120             768        778      120.1302083     778Expected: 80 to 60, 110 to 120Got:      80 to 60, 110 to 120----------------------------------------And just for fun...Extreme irregular blockLeft:    100000Right:   100001Bottom: -1Top:     2000        Screen   World          ScreenFor x=  -10      99999.99023    -10        0        100000         0        256      100000.25      256        512      100000.5       512        768      100000.75      768        1024     100001         1024        1034     100001.0098    1034For y=  -10      2026.054688    -10        0        2000           0        192      1499.75        192        384      999.5          384        576      499.25         576        768      -1             768        778      -27.0546875    778Expected: 100000 to 100001, 2000 to -1Got:      100000 to 100001, 2000 to -1

Groovy.

I had mentioned earlier how this could be brought down to one multiplication and one division. The easiest way (if you haven't figure it out yet) is to just precalculate when the viewport is assigned and store them for use later. The functions could resolve down to this (possibly smaller/faster, mathematicians?):
vp_mod_x = (ScreenInvertX * ViewportInvertX) / (ScreenWidth / ViewportWidth);vp_mod_y = (ScreenInvertY * ViewportInvertY) / (ScreenHeight / ViewportHeight);sc_mod_x = (ScreenInvertX * ViewportInvertX) / (ViewportWidth / ScreenWidth);sc_mod_y = (ScreenInvertY * ViewportInvertY) / (ViewportHeight / ScreenHeight);sc_add_x = ScreenMinX * ScreenInvertX * ViewportInvertX;sc_add_y = ScreenMinY * ScreenInvertY * ViewportInvertY;...// get a screen x/y coordinateViewportX = ScreenX * vp_mod_x + ViewportLeftViewportX = ScreenY * vp_mod_y + ViewportTop// get a viewport x/y coordinateScreenX = (ViewportX - ViewportLeft) * sc_mod_x + sc_add_x;ScreenY = (ViewportY - ViewportTop) * sc_mod_y  + sc_add_y;

So, umm... I guess that's it. If anyone has any problems, suggestions, corrections, etc., please let me know :)

vr

1. 1
Rutin
46
2. 2
3. 3
4. 4
5. 5
JoeJ
18

• 13
• 10
• 12
• 10
• 13
• ### Forum Statistics

• Total Topics
632998
• Total Posts
3009802
• ### Who's Online (See full list)

There are no registered users currently online

×