Resizing window skewes display (OpenGL) - c++

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)

Related

Resize a circle in openGL keeping the aspect ratio

I wrote this code that prints a circle. The problem comes when I try to resize the window. The aspect ratio is not kept and the circle becomes an oval.
#include<GL/glut.h>
#include<GL/glu.h>
#include<GL/gl.h>
#include<string.h>
#include<stdio.h>
#include <math.h>
#define PI 3.1415
const float DEG2RAD = 3.14159 / 180;
// Keep track of windows changing width and height
GLfloat windowWidth;
GLfloat windowHeight;
void drawCircle(float radius)
{
glBegin(GL_LINE_LOOP);
for (int i = 0; i <= 300; i++) {
double angle = 2 * PI * i / 300;
double x = radius * cos(angle);
double y = radius * sin(angle);
glVertex2d(x, y);
}
glEnd();
}
///////////////////////////////////////////////////////////
// Called to draw scene
void RenderScene(void)
{
// Clear the window with current clearing color
glClear(GL_COLOR_BUFFER_BIT );
// Save the matrix state and do the rotations
glMatrixMode(GL_MODELVIEW);
//glPushMatrix();
glColor3d(1, 0, 0);
drawCircle(100);
glutSwapBuffers();
}
///////////////////////////////////////////////////////////
// This function does any needed initialization on the
// rendering context.
void SetupRC()
{
// Light values and coordinates
//glEnable(GL_DEPTH_TEST); // Hidden surface removal
glClearColor(0,0,0,0);
}
void ChangeSize(int w, int h)
{
GLfloat aspectRatio;
GLfloat nRange = 200.0f;
// Prevent a divide by zero
if (h == 0)
h = 1;
// Set Viewport to window dimensions
glViewport(0, 0, w, h);
// Reset coordinate system
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Establish clipping volume (left, right, bottom, top, near, far)
aspectRatio = (GLfloat)w / (GLfloat)h;
if (w <= h)
{
glOrtho(-nRange, nRange, -nRange*aspectRatio, nRange*aspectRatio, -nRange*2, nRange * 2);
}
else
{
glOrtho(-nRange /aspectRatio, nRange /aspectRatio, -nRange, nRange, -nRange * 2, nRange * 2);
}
// Specify the orthographic (or perpendicular) projection,
// i.e., define the viewing box.
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
///////////////////////////////////////////////////////////
// Entry point of the program
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glClear(GL_COLOR_BUFFER_BIT);
glutInitWindowSize(800, 800);
glutCreateWindow("Circle");
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
SetupRC();
glutMainLoop();
return 0;
}
That's the code. I think that the problem is in the ChangeSize() function. Can someone help me? I tried dividing and multiplaying the range by the aspect ratio defined as width/height by the problem remains.
The projection matrix describes the mapping from 3D points of a scene, to 2D points of the viewport. The projection matrix transforms from view space to the clip space.
The coordinates in the clip space are transformed to the normalized device coordinates (NDC) in the range (-1, -1, -1) to (1, 1, 1) by dividing with the w component of the clip coordinates.
At Orthographic Projection the coordinates in the eye space are linearly mapped to normalized device coordinates and the clip sapce coordinates are equal the normalized device coordiantes, because the w component is 1 (for a carthesian coordinate).
Orthographic Projection Matrix:
r = right, l = left, b = bottom, t = top, n = near, f = far
2/(r-l) 0 0 0
0 2/(t-b) 0 0
0 0 -2/(f-n) 0
-(r+l)/(r-l) -(t+b)/(t-b) -(f+n)/(f-n) 1
Lets assume you have a full HD window:
w = 1920.0;
h = 1080.0;
The window has an aspcet ratio of 1.77778
aspectRatio = w / h = 1.77778
If you set up an orthographic projection matrix like this:
glOrtho(-nRange*aspectRatio, nRange*aspectRatio, -nRange, nRange, -nRange*2, nRange*2 );
this will result in the following orthographic projections matrix (1.0 / 1.77778 == 0.5625):
0.5625/nRange 0 0.0 0.0
0.0 1.0/nRange 0.0 0.0
0.0 0.0 0.5/nRange 0.0
0.0 0.0 0.0 1.0
When a geometry is drawn, then each point of the geometry is transformed by the projection matrix. If a circle is drawn in the XY-plane of the viewport,
then the X-coordinate is scaled by 0.5625/nRange:
X' = X * prjMat[0][0] = X * 0.5625/nRange
while the Y-coordinate is scaled by 1.0/nRange
Y' = Y * prjMat[1][1] = Y * 1.0/nRange
This means, the orthographic projection matrix applies the reciprocal aspect ratio of the viewport to the geometry, when the geometry is transformed from view space to normalized device space.
This causes that the perfect circle is distorted to an ellipse, in normalized device space and looks like this:
If you stretch this ellipse back to the rectangular viewport, the you can see the perfect circle in the window or on the screen:

OpenGL when switch fron 2D to 3D

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.

OpenGL Aspect Ratio Issue

I am using Qt + OpenGl for 2D rendering.
I am a beginner at OpenGL and for the life of me i am not able to figure out this aspect ratio issue. Everytime i think i have understood glOrtho and gViewPort, but very next time i am into another issue with them. While if coordinates are symmetric like between -1 and 1, my code works else it doesn't. I really want to get through these for once and all. All the suggestions i have searched and applied have gone fruitless for me.
My Problem Statement:
I am rendering a square and a triangle and i switch between them with keystroke "R". I am also zooming in and out. While square is maintaining aspect ratio, triangle is not. Coordinates for shapes are:
Square: (-10, -250), (500, -250), (500, -260), (-10, -260);
Triangle: (250, 0), (310, 0), (280, 30)
Basically I am not able to render above triangle. Here is code for same:
My Code
#include <QtGui/QMouseEvent>
#include <qdebug.h>
#include "GLWidget.h"
#include "stdio.h"
#include "qgl.h"
#include "qimage.h"
GLWidget::GLWidget(QWidget *parent) : QGLWidget(parent)
{
setMouseTracking(true);
}
void GLWidget::initializeGL()
{
glDisable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glDisable(GL_COLOR_MATERIAL);
glEnable(GL_BLEND);
glEnable(GL_POLYGON_SMOOTH);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor(1, 1, 1, 0);
glEnable( GL_POINT_SMOOTH ); // For Circular Points
}
void GLWidget::resizeGL(int w, int h)
{
canvas_width = (double)w;
canvas_height = (double)h;
aspect_ratio = canvas_width/canvas_height;
left_plane = 250;
right_plane = 310;
bottom_plane = 0;
top_plane = 60;
z_near_plane = 1;
z_far_plane = -1;
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if( canvas_width > canvas_height ){
glOrtho(left_plane*aspect_ratio, right_plane*aspect_ratio, bottom_plane, top_plane, z_near_plane, z_far_plane);
}else{
glOrtho(left_plane, right_plane, bottom_plane/aspect_ratio, top_plane/aspect_ratio, z_near_plane, z_far_plane);
}
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void GLWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1,0,0); // red
glBegin(GL_POLYGON);
//glVertex2f(-30,0);
//glVertex2f(30,0);
//glVertex2f(0,60);
glVertex2f(250,0);
glVertex2f(310,0);
glVertex2f(280,60);
glEnd();
}
I do not see any traingle because left_plane*aspect_ratio clips my drawing (250 is minimum X, 250*1.4 > 310, 310 is maximum X).
I hope i have made myself clear.
I will try to place images as well (i guess i will have to upload images to some other site and link them here?).
Your problem is that the bounding box (the values assigned to left/right/top/bottom_plane) doesn't have the same aspect ratio as the viewport. If you have the bounding box for the object, you need to find viewport coordinates with the correct aspect ratio (w/h). The viewport needs to be both centered on the bounding box center, and be big enough to fit the bounding box. But the aspect ratio of the viewport has nothing to do with the size of the bounding box.
In general you have a 3D bounding box (8 corner points). You would project each corner onto the screen, then use min/max to get a rectangle that needs to be centered on screen. You then check the aspect ratio ar = wr/hr of that rectangle against the aspect ratio of the viewport a=w/r. If a < ar, you need to fit wr to w, otherwise fit hr to h.
void GLWidget::resizeGL(int w, int h)
{
// First set up the projection.
double canvas_width = (double)w;
double canvas_height = (double)h;
double a = canvas_width / canvas_height;
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-a, +a, -1, +1, -1, +1);
// Now set up the view matrix.
double leftBoundingRectangle = 250;
double rightBoundingRectangle = 310;
double bottomBoundingRectangle = 0;
double topBoundingRectangle = 60;
double widthBoundingRectangle = rightBoundingRectangle - leftBoundingRectangle;
double heightBoundingRectangle = topBoundingRectangle - bottomBoundingRectangle;
double ar = widthBoundingRectangle / heightBoundingRectangle;
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Center on bounding rectangle center.
double tx = (leftBoundingRectangle + rightBoundingRectangle)/2.0, ty = (topBoundingRectangle + bottomBoundingRectangle)/2.0;
glTranslated(tx, ty, 0.0); // or is it -tx, -ty?
// Scale to fit bounding box.
double s;
if (ar > a)
{
s = ... // sorry, but you have to figure this one out for yourself. :)
}
else
{
s = ...
}
glScaled(s,s,s);
}

Why my cylinder model cannot be rotated in the Z-axis?

I attempted to write the following code to draw a cylinder. The cylinder was drawn in C++ with OpenGL. And I'm given a little tool by the school that I could compile with my own model's cpp file and then able to rotate the model with the mouse. The tool doesn't affect the rotation of my model because it works for the other demo models. However, for some reason which I do not understand, I cannot rotate the cylinder in the Z-axis to see it in its horizontal view. So, the cylinder can only be rotated and seen in these directions:
Why I cannot rotate the cylinder to see it in this direction? The following image was rotated manually in Photoshop to illustrate the direction of view that the model couldn't rotate to:
I don't understand what is the reason for not being to rotate in that direction because the demo shown for other models(written by others) could be rotated freely in all directions.
This is the code that I have written to try in mymodel.cpp:
void drawCylinderObject() {
float topRadius = 5;
float bottomRadius = 5;
float height = 10;
int numOfPolygons = 50;
float basisvec1[3] = {1, 0, 0};
float basisvec2[3] = {0, 0, -1};
float topPosition[3] = {0, height/2.0, 0};
float bottomPosition[3] = {0, -height/2.0, 0};
for(int i=0; i<numOfPolygons; i++) {
float angle1 = (float)i/(float)numOfPolygons*2.0*M_PI;
float angle2 = ((float)i+1.0)/(float)numOfPolygons*2.0*M_PI;
vector<float> point1(3), point2(3), point3(3), point4(3);
for(int j=0; j<3; j++) {
point1[j] = topPosition[j] + topRadius * cos(angle1) * basisvec1[j] + topRadius * sin(angle1) * basisvec2[j];
}
for(int j=0; j<3; j++) {
point2[j] = bottomPosition[j] + bottomRadius * cos(angle1) * basisvec1[j] + bottomRadius * sin(angle1) * basisvec2[j];
}
for(int j=0; j<3; j++) {
point3[j] = bottomPosition[j] + bottomRadius * cos(angle2) * basisvec1[j] + bottomRadius * sin(angle2) * basisvec2[j];
}
for(int j=0; j<3; j++) {
point4[j] = topPosition[j] + topRadius * cos(angle2) * basisvec1[j] + topRadius * sin(angle2) * basisvec2[j];
}
float crossvec1[3] = {point4[0]-point1[0], point4[1]-point1[1], point4[2]-point1[2]};
float crossvec2[3] = {point2[0]-point1[0], point2[1]-point1[1], point2[2]-point1[2]};
float normalVector1[3];
crossProduct(crossvec2, crossvec1, normalVector1);
glBegin(GL_POLYGON);
glNormal3fv(normalVector1);
glVertex3f(point1[0], point1[1], point1[2]);
glVertex3f(point2[0], point2[1], point2[2]);
glVertex3f(point3[0], point3[1], point3[2]);
glVertex3f(point4[0], point4[1], point4[2]);
glEnd();
}
}
And the overwritten function I have is like this, also in mymodel.cpp:
void CRenderView::drawScene()
{
//calls the above function
drawCylinderObject();
}
What I have done basically is just to define 2 perpendicular basis unit vectors and then extend them outwards with a magnitude value. And I loop through this 360 degrees to draw the polygons to form the cylinder. But what is wrong that this way of drawing doesn't allow me to rotate the model freely?
Edit:
The following is part of the code of how the tool draws the scene. Somehow, the tool has a huge chunk of classes. Most of its classes merely sets up the GUI of the tool and then the only part that draws it is the one below in CRenderView.cpp:
void CRenderView::OnPaint()
{
// Device context for painting
CPaintDC dc(this);
// Model is stored in Document
CToolDoc *pDoc = (CToolDoc *)GetDocument();
ASSERT_VALID(pDoc);
// Useful in multidoc templates
HWND hWnd = GetSafeHwnd();
HDC hDC = ::GetDC(hWnd);
wglMakeCurrent(hDC,m_hGLContext);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(m_ClearColorRed,m_ClearColorGreen,m_ClearColorBlue,1.0f);
glPushMatrix();
// Position / translation / scale
glTranslated(m_xTranslation,m_yTranslation,m_zTranslation);
glRotatef(m_xRotation, 1.0, 0.0, 0.0);
glRotatef(m_yRotation, 0.0, 1.0, 0.0);
glScalef(m_xScaling,m_yScaling,m_zScaling);
// Start rendering...
drawScene();
glPopMatrix();
// Double buffer
SwapBuffers(dc.m_ps.hdc);
}
protected:
void drawScene();
void CRenderView::OnMouseMove(UINT nFlags,
CPoint point)
{
if(m_LeftButtonDown)
{
m_yRotation -= (float)(m_LeftDownPos.x - point.x)/3.0f;
m_xRotation -= (float)(m_LeftDownPos.y - point.y)/3.0f;
m_LeftDownPos = point;
InvalidateRect(NULL,FALSE);
}
CView::OnMouseMove(nFlags, point);
}
May I ask, why you rotate the cylinder by recalculating its vertices? Just generate a cylinder model once, then perform any following transformations on the modelview matrix. Also I think you'll want to rotate about Z, not Y.
Update
That "tool" seems to be a slightly extended version of the "MFC OpenGL CView" tutorial. *yuck*
The biggest problem I see, that the CRenderView::OnPaint function has been written by someone, who doesn't know how to properly use OpenGL.
void CRenderView::OnPaint()
{
// Device context for painting
CPaintDC dc(this);
// Model is stored in Document
CToolDoc *pDoc = (CToolDoc *)GetDocument();
ASSERT_VALID(pDoc);
// Useful in multidoc templates
HWND hWnd = GetSafeHwnd();
HDC hDC = ::GetDC(hWnd);
wglMakeCurrent(hDC,m_hGLContext);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(m_ClearColorRed,m_ClearColorGreen,m_ClearColorBlue,1.0f);
glClearColor must be called before glClear, as it sets the value that will be applied upon the glClear call. This way round it will work on the glClear call of the next frame or not at all.
glPushMatrix();
Which matrix is pushed here? This lacks a call to glMatrixMode.
Starting from some arbitrary matrix. And BTW where is the projection set (let me guess, in the OnSize handler, right?).
// Position / translation / scale
glTranslated(m_xTranslation,m_yTranslation,m_zTranslation);
glRotatef(m_xRotation, 1.0, 0.0, 0.0);
glRotatef(m_yRotation, 0.0, 1.0, 0.0);
You confirmed you want to rotate about the Z axis. So why won't you do it here? There only rotation about X and Y here. Problem is: You're using euler angles here, which have some nasty properties and are frowned upon by 3D graphics people. Better use Quaternions for representing rotations (just a suggestion).
glScalef(m_xScaling,m_yScaling,m_zScaling);
// Start rendering...
drawScene();
glPopMatrix();
// Double buffer
SwapBuffers(dc.m_ps.hdc);
}

Transforming verticies with center point and scale factor?

My application is a vector drawing application. It works with OpenGL. I will be modifying it to instead use the Cairo 2D graphics library. The issue is with zooming. With openGL camera and scale factor sort of work like this:
float scalediv = Current_Scene().camera.ScaleFactor / 2.0f;
float cameraX = GetCameraX();
float cameraY = GetCameraY();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
float left = cameraX - ((float)controls.MainGlFrame.Dimensions.x) * scalediv;
float right = cameraX + ((float)controls.MainGlFrame.Dimensions.x) * scalediv;
float bottom = cameraY - ((float)controls.MainGlFrame.Dimensions.y) * scalediv;
float top = cameraY + ((float)controls.MainGlFrame.Dimensions.y) * scalediv;
glOrtho(left,
right,
bottom,
top,
-0.01f,0.01f);
// Set the model matrix as the current matrix
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
hdc = BeginPaint(controls.MainGlContext.mhWnd,&ps);
Mouse position is obtained like this:
POINT _mouse = controls.MainGlFrame.GetMousePos();
vector2f mouse = functions.ScreenToWorld(_mouse.x,_mouse.y,GetCameraX(),GetCameraY(),
Current_Scene().camera.ScaleFactor,
controls.MainGlFrame.Dimensions.x,
controls.MainGlFrame.Dimensions.y );
vector2f CGlEngineFunctions::ScreenToWorld(int x, int y, float camx, float camy, float scale, int width, int height)
{
// Move the given point to the origin, multiply by the zoom factor and
// add the model coordinates of the center point (camera position)
vector2f p;
p.x = (float)(x - width / 2.0f) * scale +
camx;
p.y = -(float)(y - height / 2.0f) * scale +
camy;
return p;
}
From there I draw the VBO's of triangles. This allows me to pan and zoom in. Given that Cairo only can draw based on coordinates, how can I make it so that a vertex is properly scaled and panned without using transformations. Basically GlOrtho sets the viewport usually but I dont think I could do this with Cairo.
Well GlOrtho is able to change the viewport matrix instead of modifying the verticies but how could I instead modify the verticies to get the same result?
Thanks
*Given vertex P, which was obtained from ScreenToWorld, how could I modify it so that it is scaled and panned accordng to the camera and scale factor? Because usually OpenGL would essentially do this
I think Cairo can do what you want ... see http://cairographics.org/matrix_transform/ . Does that solve your problem, and if not, why ?