Preserve aspect ratio of 2D object on window resize
I have a Windows (XP) ap开发者_开发知识库plication which needs to display a two-dimensional rectangle within a window. The rectangle must not be clipped (i.e. must always lie completely within the viewport), and must preserve its aspect ratio on resize. Currently, the method which handles layout distorts the aspect ratio of the rectangle to match the window. I want the rectangle to scale to the window and be centered in the window (again, without clipping). The method as it stands is below. lWinDist and lMaxDepth are the width and height of the rectangle to be displayed (in 48ths of an inch, if it matters):
void CRoRRecView::RedoLayout( long lWinDist, long lMaxDepth )
{
CDC* pDC = GetDC() ;
if ( pDC != NULL )
{
m_lWinDist = lWinDist;
GetClientRect( m_rectClient ) ;
int nClientWidth = m_rectClient.Width();
int nClientHeight = m_rectClient.Height();
glViewport( 0, 0, nClientWidth, nClientHeight );
glMatrixMode( GL_PROJECTION);
glLoadIdentity();
m_fWinXDist = (float) lWinDist ;
m_fWinYDist = lMaxDepth ;
m_fAspectRatio = m_fWinXDist / m_fWinYDist;
glOrtho(0.0, m_fWinXDist, 0.0, m_fWinYDist, -1, 1 ) ;
glRotatef(180.0, 0,1,0);
glTranslatef( (float)(-1 * lWinDist),0,0 ); // Translate across the x axis
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
ReleaseDC( pDC ) ;
}
}
Try this reshape function. It will preserve your aspect ratio, resize the object, and make the viewport to be centralize.
// initial window screen size
int WIDTH = 400;
int HEIGHT = 300;
// reshape function, call with glutReshapeFunc(reshape) in yout main function
void reshape(int width, int height) {
const float ar_origin = (float) WIDTH / (float) HEIGHT;
const float ar_new = (float) width / (float) height;
float scale_w = (float) width / (float) WIDTH;
float scale_h = (float) height / (float) HEIGHT;
if (ar_new > ar_origin) {
scale_w = scale_h;
} else {
scale_h = scale_w;
}
float margin_x = (width - WIDTH * scale_w) / 2;
float margin_y = (height - HEIGHT * scale_h) / 2;
glViewport(margin_x, margin_y, WIDTH * scale_w, HEIGHT * scale_h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, WIDTH / ar_origin, 0, HEIGHT / ar_origin, 0, 1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity() ;
}
This one should scale as expected:
// g++ main.cpp -lglut -lGL
#include <GL/glut.h>
void display()
{
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
double w = glutGet( GLUT_WINDOW_WIDTH );
double h = glutGet( GLUT_WINDOW_HEIGHT );
double ar = w / h;
glOrtho( -2 * ar, 2 * ar, -2, 2, -1, 1);
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glColor3ub( 255, 0, 0 );
glBegin( GL_QUADS );
glVertex2i( -1, -1 );
glVertex2i( 1, -1 );
glVertex2i( 1, 1 );
glVertex2i( -1, 1 );
glEnd();
glutSwapBuffers();
}
int main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
glutInitWindowSize( 640, 480 );
glutCreateWindow( "Aspect Ratio" );
glutDisplayFunc( display );
glutMainLoop();
return 0;
}
Try this:
#include <GL/glut.h>
size_t win_w = 0;
size_t win_h = 0;
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, win_w, 0, win_h, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glColor3ub(255,0,0);
glPushMatrix();
glTranslatef(win_w/2,win_h/2,0);
glScalef(50,50,50);
glBegin(GL_QUADS);
glVertex2f(-1,-1);
glVertex2f(1,-1);
glVertex2f(1,1);
glVertex2f(-1,1);
glEnd();
glPopMatrix();
glFlush();
glutSwapBuffers();
}
void reshape(int w, int h)
{
win_w = w;
win_h = h;
glViewport(0, 0, w, h);
}
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
glutInitWindowSize(800,600);
glutCreateWindow("Aspect Ratio");
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMainLoop();
return 0;
}
I think you should look at this to get the right numbers:
self.hOffset = (self.wOffset / (16.0 / 9.0));
self.xOffset = ((int)ceil((_backingWidth / 2.0) - ((_backingWidth + self.wOffset) / 2.0)));
self.yOffset = ((int)ceil((_backingHeight / 2.0) - ((_backingHeight + self.hOffset) / 2.0)));
glViewport(self.xOffset, self.yOffset, _backingWidth + self.wOffset, _backingHeight + self.hOffset );
In the above code, wOffset, which is the width of the image, give or take its value changed via a slider. The first line increases the height as the width is increased at the correct ratio, which happens to be 16:9 (it doesn't work well to divide the width by the height to get the ratio; specifying it explicitly does best). The next two lines adjust the x,y coordinates of the image, so that as it is increased or decreased in size, it always stays centered.
This is by far a simpler, easier approach—and lighter on the GPU. If there are reasons why the mega-coding approach I'm seeing here is better, I can't figure it out. Good luck.
精彩评论