OpenGL when switch fron 2D to 3D - opengl

Here's what I want to achieve, I have a flag called switch_2D_3D in the code below, and when it's true I switch to 2D mode, otherwise 3D.
void reshape(GLsizei width, GLsizei height)
{
if (switch_2D_3D)
{
// GLsizei for non-negative integer
// Compute aspect ratio of the new window
if (height == 0)
height = 1; // To prevent divide by 0
GLfloat aspect = (GLfloat)width / (GLfloat)height;
// Reset transformations
glLoadIdentity();
// Set the aspect ratio of the clipping area to match the viewport
glMatrixMode(GL_PROJECTION); // To operate on the Projection matrix
// Set the viewport to cover the new window
glViewport(0, 0, width, height);
if (width >= height)
{
// aspect >= 1, set the height from -1 to 1, with larger width
gluOrtho2D(-1.0 * aspect, 1.0 * aspect, -1.0, 1.0);
}
else
{
// aspect < 1, set the width to -1 to 1, with larger height
gluOrtho2D(-1.0, 1.0, -1.0 / aspect, 1.0 / aspect);
}
winWidth = width;
winHeight = height;
} // 2D mode
else
{
// Prevent a divide by zero, when window is too short
// (you cant make a window of zero width).
if (height == 0)
height = 1;
float ratio = width * 1.0 / height;
// Use the Projection Matrix
glMatrixMode(GL_PROJECTION);
// Reset Matrix
glLoadIdentity();
// Set the viewport to be the entire window
glViewport(0, 0, width, height);
// Set the correct perspective.
gluPerspective(45.0f, ratio, 0.1f, 100.0f);
// Get Back to the Modelview
glMatrixMode(GL_MODELVIEW);
winWidth = width;
winHeight = height;
}// 3D mode
}
Everything works perfectly when drawing only in 2d mode, but when I change the flag to switch to the 3d mode, here comes the problem
Every time I resize the window, the things I draw in the 3d scene(for example a cube) would be come smallerand smaller, eventually disappeared, why is this happening
And if I switch back to 2D mode, everything in 2d mode still works fine, the problem is with the 3d mode
Also, if I start the program with the flag set to false, I would see a cube and it still gets smaller as I resize the window each time
Why is this happening?

You should look at your glLoadIdentity() / glMatrixMode() interactions.
Right now, you have two different behaviors:
In 2D: you're resetting your matrix for whatever is active when you enter the function, presumably GL_MODELVIEW, which causes the gluOrtho2D calls to "stack up".
In 3D: you're always resetting the projection matrix, which seems more correct.
Try swapping the order of the glLoadIdentity and glMatrixMode calls in your first path (2D) only.
It's a wise idea to always explicitly set the matrix you want to modify before actually modifying it.

Related

Resizing window skewes display (OpenGL)

I am making a program that bounces balls of the edge of its window, but I'm having issues with the bounds getting skewed.
If I set the initial resolution to a square window,
int windowWidth = 600;
int windowHeight = 600;
it works fine. As soon as I reshape the window, the bounds on the window get skewed.
When it's square, it looks like this:
When I stretch it by its width, it looks like this:
When I stretch it by its height, it looks like this:
Basically I'm not able to resize the window without skewing the bounds of the window.
This is the code for my reshape function:
void reshape(GLsizei weight, GLsizei height)
{
if (height == 0) height = 1; // To prevent divide by 0
GLfloat aspect = (GLfloat)weight / height; // Get aspect ratio
// Set the viewport to cover the entire window
glViewport(0, 0, weight, height);
// Adjust the aspect ratio of clipping area to match the viewport
glMatrixMode(GL_PROJECTION); // Select the Projection matrix
glLoadIdentity(); // Reset
for (int i = 0; i < numOfBalls; i++)
{
if (weight <= height)
{
balls[i].xLeft = -1.0;
balls[i].xRight = 1.0;
balls[i].yBottom = -1.0 / aspect;
balls[i].yTop = 1.0 / aspect;
}
else
{
balls[i].xLeft = -1.0 * aspect;
balls[i].xRight = 1.0 * aspect;
balls[i]. yBottom = -1.0;
balls[i]. yTop = 1.0;
}
gluOrtho2D(balls[i].xLeft, balls[i].xRight, balls[i].yBottom, balls[i].yTop);
balls[i].xPosMin = balls[i].xLeft + balls[i].ballRadius;
balls[i].xPosMax = balls[i].xRight - balls[i].ballRadius;
balls[i].yPosMin = balls[i].yBottom + balls[i].ballRadius;
balls[i].yPosMax = balls[i].yTop - balls[i].ballRadius;
}
glMatrixMode(GL_MODELVIEW); // Select the model-view matrix
glLoadIdentity(); // Reset
}
*Note: I can post more code if needed...
Try cut this from your loop:
gluOrtho2D(balls[i].xLeft, balls[i].xRight, balls[i].yBottom, balls[i].yTop);
and define your orthographic matrix once.
I think whenever your loop execute, you multiply a new matrix with previously inserted matrix in GL.
The produced orthographic matrix by GL is this:
Now when you set width: 800 and height: 600 your aspect ratio will be 1.33 and matrix for first loop will be:
Now by each loop, GL will multiply new matrix with previous matrix and coordinates will get closer by multiply each by 0.75.
(Also i am not sure)

OpenGL screen layout

I have some questions about the screen set up. Originally when I would draw a triangle the x vector 1 would be all the way to the right and -1 would be all the way to the left. Now I have adjusted it to account for the different aspect ratio of the window. My new question how do I make the numbers which are used to render a 2d tri go along with the pixel values. If my window is 480 pixels wide and 320 tall I want to have to enter this to span the screen with a tri
glBegin(GL_TRIANGLES);
glVertex2f(240, 320);
glVertex2f(480, 0);
glVertex2f(0, 0);
glEnd();
but instead it currently looks like this
glBegin(GL_TRIANGLES);
glVertex2f(0, 1);
glVertex2f(1, -1);
glVertex2f(-1, -1);
glEnd();
Any ideas?
You need to use functions glViewport and glOrtho with correct values. Basically glViewport sets the part of your window capable of rendering 3D-Graphics using OpenGL. glOrtho establishes coordinate system within that part of a window using OpenGL's coordinates.
So for your task you need to know exact width and height of your window. If you are saying they are 480 and 320 respectively then you need to call
glViewport(0, 0, 480, 320)
// or: glViewport ( 0,0,w,h)
somewhere, maybe in your SizeChanging-handler(if you are using WINAPI it is WM_SIZE message)
Next, when establishing OpenGL's scene you need to specify OpenGL's coordinates. For orthographic projection they will be the same as dimensions of a window so
glOrtho(-240, 240, -160, 160, -100, 100)
// or: glOrtho ( -w/2, w/2, -h/2, h/2, -100, 100 );
is siutable for your purppose. Not that here I'm using depth of 200 (z goes from -100 to 100).
Next on your rendering routine you may draw your triangle
Since the second piece of code is working for you, I assume your transformation matrices are all identity or you have a shader that bypasses them. Also your viewport is spanning the whole window.
In general if your viewport starts at (x0,y0) and has WxH size, the normalized coordinates (x,y) you feed to glVertex2f will be transformed to (vx,vy) as follows:
vx = x0 + (x * .5f + .5f) * W
vy = y0 + (y * .5f + .5f) * H
If you want to use pixel coordinates you can use the function
void vertex2(int x, int y)
{
float vx = (float(x) + .5f) / 480.f;
float vy = (float(y) + .5f) / 320.f;
glVertex3f(vx, vy, -1.f);
}
The -1 z value is the closest depth to the viewer. It's negative because the z is assumed to be reflected after the transformation (which is identity in your case).
The addition of .5f is because the rasterizer considers a pixel as a 1x1 quad and evaluates the coverage of your triangle in the middle of this quad.

Keeping aspect ratio when resizing in OpenGL

I implemented the following code:
void TestGlPlot::resizeGL(int width, int height)
{
setupViewport(width, height);
}
void TestGlPlot::setupViewport(int width, int height)
{
/* Prevent divide by zero --------------------------------------------------------------------*/
if (height == 0) height = 1;
/* Calculate aspect ratio --------------------------------------------------------------------*/
float aspectRatio = (float)width / (float)height;
/* Set viewport to cover the window ----------------------------------------------------------*/
glViewport(0, 0, width, height);
/* Set aspect ratio --------------------------------------------------------------------------*/
glMatrixMode(GL_PROJECTION); /* switch to projection matrix */
glLoadIdentity();
/*
if (width >= height)
{
gluOrtho2D(-0.5*aspectRatio, 0.5*aspectRatio, 0.0, 1.0);
}
else
{
gluOrtho2D(-0.5, 0.5, 0.0*aspectRatio, 1.0*aspectRatio);
}
glMatrixMode(GL_MODELVIEW);
*/
gluOrtho2D(-1, 1, 0.0, 1.0);
glMatrixMode(GL_MODELVIEW);
}
void TestGlPlot::paintEvent(QPaintEvent *event) {
makeCurrent();
setupViewport(width(), height());
glMatrixMode(GL_MODELVIEW);
/* Set white background ----------------------------------------------------------------------*/
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(255,255,255,0);
/* Paint OpenGL events -----------------------------------------------------------------------*/
glColor4f(1.0, 0.0, 0.0, 1.0);
/* light grey */
glColor4f(0.0, 0.0, 0.0, 0.3);
gluPartialDisk(plainQuad, 0.1, 1, 20, 4, -60, 120);
/* Paint QPainter events ---------------------------------------------------------------------*/
QPainter painter(this);
/* Draw grey border around plot --------------------------------------------------------------*/
painter.setPen(QColor("#808080"));
painter.drawRect(0, 0, width()-1, height()-1);
painter.setFont(font);
/* Translate coordinate system to (0,0) center -----------------------------------------------*/
QMatrix m;
m.translate(width()*0.5, height()*0.5);
painter.setMatrix(m);
/* Left side descriptions for radius ---------------------------------------------------------*/
painter.drawText(-0.17*width(), 0.38*height(), tr("100m"));
painter.drawText(-0.27*width(), 0.28*height(), tr("200m"));
painter.drawText(-0.37*width(), 0.18*height(), tr("300m"));
painter.drawText(-0.47*width(), 0.08*height(), tr("400m"));
painter.drawText(0.45*width(), -0.01*height(), tr("60°"));
painter.drawText(0.26*width(), -0.38*height(), tr("30°"));
painter.drawText(-0.47*width(), -0.01*height(), tr("300°"));
painter.drawText(-0.28*width(), -0.38*height(), tr("330°"));
painter.end();
}
i tried different methods for resize handling (keeping the shape of the partialDisk object without stretching it) but every method failed. I also want to keep the coordinate handling of the unit-circle (so i can normalize my measurements and draw them into a polar-plot).
To keep the aspect ratio, you have several options in general:
Always scale to one dimension. For example, you just define that you want always see the horizontal range [-0.5,0.5]. In this case, you have to correct the vertical range by the factor (1/aspect_viewport).
Use some equivalent of letterboxing. So you define a "region of interest" which you always want to see completely, but you might see more in width or height, depending on the window aspect (that would be basically equivalent to the black bars when watching letterboxed movies). There are two cases to consider: the aspect of the viewport is greater than the aspect of your region, so it is wider. In that case, you should map the full height and increase the horitonal range by a factor of (aspect_viewport/aspect_region). Otherwise, the aspect of the window is lower than aspect of your region, so you should use the full width and scale up the vertical range by (aspect_region/aspect_viewport). Note that in both cases, the factor is >= 1.
In your code, you almost have implemented method 2, but you got the aspectRatio < 1 case wrong, it should be
if (width >= height)
gluOrtho2D(-0.5f*aspectRatio, 0.5f*aspectRatio, 0.0f, 1.0f);
else
gluOrtho2D(-0.5f, 0.5f, 0.0, 1.0/aspectRatio);

How do you adjust an opengl viewport size without scaling its contents

When my window is resized, i don't want the contents to scale but just to increase the view port size. I found this while searching on stackoverflow (http://stackoverflow.com/questions/5894866/resize-viewport-crop-scene) which is pretty much the same as my problem. However I'm confused as to what to set the Zoom to and where, i tried it with 1.0f but then nothing was shown at all :s
This is resize function code at the moment which does scaling:
void GLRenderer::resize() {
RECT rect;
int width, height;
GLfloat aspect;
GetClientRect(hWnd, &rect);
width = rect.right;
height = rect.bottom;
if (height == 0) {
height = 1;
}
aspect = (GLfloat) width / height;
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, aspect, 0.1, 100.0);
glMatrixMode(GL_MODELVIEW);
}
And my function to render a simple triangle:
void GLRenderer::render() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslated(0, 0, -20);
glBegin(GL_TRIANGLES);
glColor3d(1, 0, 0);
glVertex3d(0, 1, 0);
glVertex3d(1, -1, 0);
glVertex3d(-1, -1, 0);
glEnd();
SwapBuffers(hDC);
}
You can change the zoom in y (height) with the "field of view" parameter to gluPerspective. The one that is 45 degrees in your code. As it is currently always 45 degrees, you will always get the same view angle (in y). How to change this value as a function of the height of the window is not obvious. A linear relation would fail for big values (180 degrees and up). I would try to use arctan(height/k), where 'k' is something like 500.
Notice also that when you extend the window in x, you will already get what you want (the way your source code currently is). That is, you get a wider field of view. That is because you change the aspect (second argument) to a value depending on the ratio between x and y.
Height and Width is measured in pixels, so a value of 1 is not good.
Notice that you are using deprecated legacy OpenGL. See Legacy OpenGL for more information.

Resize viewport, crop scene

I have a 3d scene drawn by OpenGL to a resizeable window. Now, when the window gets resized, I do not want to scale the viewport, rather I want to keep the scene at a fixed size and show a larger portion of it (or crop the scene image). This is my current code:
GLfloat ratio;
// Protect against a divide by zero
if ( height == 0 )
height = 1;
ratio = ( GLfloat )width / ( GLfloat )height;
// Setup our viewport.
glViewport( 0, 0, ( GLint )width, ( GLint )height );
// change to the projection matrix and set our viewing volume.
glMatrixMode( GL_PROJECTION );
glLoadIdentity( );
gluPerspective( 60.0f, ratio, 0.1f, 1000.0f );
// Switch back to the modelview
glMatrixMode( GL_MODELVIEW );
If I keep the ratio fixed, then the scene image simply gets scaled, but I want to keep it at fixed size and simply show a wider view. Any ideas on this?
Adjust the fov parameters. Technically what you want to do is easier if done using glFrustum instead of gluPerspective.
// Protect against a divide by zero
if ( height == 0 )
height = 1;
// Setup our viewport.
glViewport( 0, 0, ( GLint )width, ( GLint )height );
// change to the projection matrix and set our viewing volume.
glMatrixMode( GL_PROJECTION );
glLoadIdentity( );
// supply some sensefull value for this; ideally let the user adjust it somehow
exterm float Zoom;
// near far should tightly wrap the actually visible set of objects. Hardcoded values
// like 0.1 ... 1000.f are problematic. Also your choosen value range slices your viewport
// into 10000 depth slices. Say you get only a 16 bit depth buffer already in the lineary
// slicing ortho projection a OpenGL length units in depth would recieve only about 6
// slices. In perspective mode the slice density follows a 1/depth law. So already at depth
// 10 you'll run into depth resolution problems.
glFrustum(-Zoom * width, Zoom * width, -Zoom * height, Zoom * height, near, far);
// Switch back to the modelview
glMatrixMode( GL_MODELVIEW );
Take note that this code belongs into the display function. Any tutorial that sets viewport and projection in a window reshape handler is very bad style; don't follow it.
Ideally, since you have set the viewport to 0,0,width,height, the image should remain in the same size. Can you check the coordinates you send for the image. Does that remain constant or does it scale along with width/ height? Can you post the code for adding vertices.