OpenGL - maintaining aspect ratio upon window resize - c

I am drawing a polygon in a square window. When I resize the window, for instance by fullscreening, the aspect ratio is disturbed. From a reference I found one way of preserving the aspect ratio. Here is the code:
void reshape (int width, int height) {
float cx, halfWidth = width*0.5f;
float aspect = (float)width/(float)height;
glViewport (0, 0, (GLsizei) width, (GLsizei) height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(cx-halfWidth*aspect, cx+halfWidth*aspect, bottom, top, zNear, zFar);
glMatrixMode (GL_MODELVIEW);
}
Here, cx is the eye space center of the zNear plane in X. I request if someone could please explain how could I calculate this. I believe this should be the average of the initial first two arguments to glFrustum(). Am I right? Any help will be greatly appreciated.

It looks like what you want to do is maintain the field of view or angle of view when the aspect ratio changes. See the section titled 9.085 How can I make a call to glFrustum() that matches my call to gluPerspective()? of the OpenGL FAQ for details on how to do that. Here's the short version:
fov*0.5 = arctan ((top-bottom)*0.5 / near)
top = tan(fov*0.5) * near
bottom = -top
left = aspect * bottom
right = aspect * top
See the link for details.

The first two arguments are the X coordinates of the left and right clipping planes in eye space. Unless you are doing off-axis tricks (for example, to display uncentered projections across multiple monitors), left and right should have the same magnitude and opposite sign. Which would make your cx variable zero.
If you are having trouble understanding glFrustrum, you can always use gluPerspective instead, which has a somewhat simplified interface.

Related

Get texture coordinates of mouse position in SDL2?

I have the strict requirement to have a texture with resolution (let's say) of 512x512, always (even if the window is bigger, and SDL basically scales the texture for me, on rendering). This is because it's an emulator of a classic old computer assuming a fixed texture, I can't rewrite the code to adopt multiple texture sizes and/or texture ratios dynamically.
I use SDL_RenderSetLogicalSize() for the purpose I've described above.
Surely, when this is rendered into a window, I can get the mouse coordinates (window relative), and I can "scale" back to the texture position with getting the real window size (since the window can be resized).
However, there is a big problem here. As soon as window width:height ratio is not the same as the texture's ratio (for example in full screen mode, since the ratio of modern displays would not match of the ratio I want to use), there are "black bars" at the sides or top/bottom. Which is nice, since I want always the same texture ratio, fixed, and SDL does it for me, etc. However I cannot find a way to ask SDL where is my texture rendered exactly inside the window based on the fixed ratio I forced. Since I need the position within the texture only, and the exact texture origin is placed by SDL itself, not by me.
Surely, I can write some code to figure out how those "black bars" would change the origin of the texture, but I can hope there is a more simple and elegant way to "ask" SDL about this, since surely it has the code to position my texture somewhere, so I can re-use that information.
My very ugly (can be optimized, and floating point math can be avoided I think, but as the first try ...) solution:
static void get_mouse_texture_coords ( int x, int y )
{
int win_x_size, win_y_size;
SDL_GetWindowSize(sdl_win, &win_x_size, &win_y_size);
// I don't know if there is more sane way for this ...
// But we must figure out where is the texture within the window,
// which can be changed since the fixed ratio versus the window ratio (especially in full screen mode)
double aspect_tex = (double)SCREEN_W / (double)SCREEN_H;
double aspect_win = (double)win_x_size / (double)win_y_size;
if (aspect_win >= aspect_tex) {
// side ratio correction bars must be taken account
double zoom_factor = (double)win_y_size / (double)SCREEN_H;
int bar_size = win_x_size - (int)((double)SCREEN_W * zoom_factor);
mouse_x = (x - bar_size / 2) / zoom_factor;
mouse_y = y / zoom_factor;
} else {
// top-bottom ratio correction bars must be taken account
double zoom_factor = (double)win_x_size / (double)SCREEN_W;
int bar_size = win_y_size - (int)((double)SCREEN_H * zoom_factor);
mouse_x = x / zoom_factor;
mouse_y = (y - bar_size / 2) / zoom_factor;
}
}
Where SCREEN_W and SCREEN_H are the dimensions of the my texture, quite misleading by names, but anyway. Input parameters x and y are the window-relative mouse position (reported by SDL). mouse_x and mouse_y are the result, the texture based coordinates. This seems to work nicely. However, is there any sane solution or a better one?
The code which calls the function above is in my event handler loop (which I call regularly, of course), something like this:
void handle_sdl_events ( void ) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_MOUSEMOTION:
get_mouse_texture_coords(event.motion.x, event.motion.y);
break;
[...]

Texturing a sphere in OpenGL with glTexGen

I want to get an earth texture on sphere. My sphere is an icosphere built with many triangles (100+) and I found it confusing to set the UV coordinates for whole sphere. I tried to use glTexGen and effects are quite close but I got my texture repeated 8 times (see image) . I cannot find a way to make it just wrap the whole object once. Here is my code where the sphere and textures are created.
glEnable(GL_TEXTURE_2D);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glBegin(GL_TRIANGLES);
for (int i = 0; i < new_sphere->NumOfTrians; i++)
{
Triangle *draw_Trian = new_sphere->Trians+i;
glVertex3f(draw_Trian->pnts[0].coords[0], draw_Trian->pnts[0].coords[1], draw_Trian->pnts[0].coords[2]);
glVertex3f(draw_Trian->pnts[1].coords[0], draw_Trian->pnts[1].coords[1], draw_Trian->pnts[1].coords[2]);
glVertex3f(draw_Trian->pnts[2].coords[0], draw_Trian->pnts[2].coords[1], draw_Trian->pnts[2].coords[2]);
}
glDisable(GL_TEXTURE_2D);
free(new_sphere->Trians);
free(new_sphere);
glEnd();
You need to define how your texture is supposed to map to your triangles. This depends on the texture you're using. There are a multitude of ways to map the surface of a sphere with a texture (since no one mapping is free of singularities). It looks like you have a cylindrical projection texture there. So we will emit cylindrical UV coordinates.
I've tried to give you some code here, but it's assuming that
Your mesh is a unit sphere (i.e., centered at 0 and has radius 1)
pnts.coords is an array of floats
You want to use the second coordinate (coord[1]) as the 'up' direction (or the height in a cylindrical mapping)
Your code would look something like this. I've defined a new function for emitting cylindrical UVs, so you can put that wherever you like.
/* Map [(-1, -1, -1), (1, 1, 1)] into [(0, 0), (1, 1)] cylindrically */
inline void uvCylinder(float* coord) {
float angle = 0.5f * atan2(coord[2], coord[0]) / 3.14159f + 0.5f;
float height = 0.5f * coord[1] + 0.5f;
glTexCoord2f(angle, height);
}
glEnable(GL_TEXTURE_2D);
glBegin(GL_TRIANGLES);
for (int i = 0; i < new_sphere->NumOfTrians; i++) {
Triangle *t = new_sphere->Trians+i;
uvCylinder(t->pnts[0].coords);
glVertex3f(t->pnts[0].coords[0], t->pnts[0].coords[1], t->pnts[0].coords[2]);
uvCylinder(t->pnts[1].coords);
glVertex3f(t->pnts[1].coords[0], t->pnts[1].coords[1], t->pnts[1].coords[2]);
uvCylinder(t->pnts[2].coords);
glVertex3f(t->pnts[2].coords[0], t->pnts[2].coords[1], t->pnts[2].coords[2]);
}
glEnd();
glDisable(GL_TEXTURE_2D);
free(new_sphere->Trians);
free(new_sphere);
Note on Projections
The reason it's confusing to build UV coordinates for the whole sphere is that there isn't one 'correct' way to do it. Mathematically-speaking, there's no such thing as a perfect 2D mapping of a sphere; hence why we have so many different types of projections. When you have a 2D image that's a texture for a spherical object, you need to know what type of projection that image was built for, so that you can emit the correct UV coordinates for that texture.

OpenCV in C - drawing a circle that should appear only in upper half of the image?

I have to draw a circle for several images. For each image to the radius of curvature is different with a constant center.
The problem is : no matter how big the circle is it shouldn't cross to upper half of the image. It's OK if it becomes invisible or only a part of it is visible in the lower half.
I am using OpenCV 2.4.4 in C lang.
The values for the circle is found by:
for(angle1 = 0; angle1<360; angle1++)
{
x [angle1]= r * sin(angle1) + axis_x;
y [angle1]= r * cos(angle1) + axis_y;
}
FYI:
cvCircle( img,center_circle, r,cvScalar( 0, 0, 255,0 ),2,8,0);
Draws circle in the entire image. Which I don't want to happen.
How can I do it? Rem: no part of the circle should appear in upper half of the image.
And the code should be in OpenCV's C lang.
In MALTAB is pretty easy. I only have to select the pixels and map them on the image.
I am new to OpenCV and operations like img->data.i/f/s/db[50] =50; is showing error.
A pretty naive approach is to create a copy of the upper half of image, draw the complete circle, and then copy back the upper half to original image. This may not be the best approach but it works. Here is how it can be achieved:
void drawCircleLowerHalf(IplImage* image, CvPoint center, int radius, CvScalar color, int thickness, int line_type, int shift)
{
CvRect roi = cvRect(0,0,image->width, image->height/2);
IplImage* upperHalf = cvCreateImage(cvSize(image->width, image->height/2), image->depth, image->nChannels);
cvSetImageROI(image, roi);
cvCopy(image,upperHalf);
cvResetImageROI(image);
cvCircle(image, center, radius, color, thickness, line_type, shift);
cvSetImageROI(image, roi);
cvCopy(upperHalf, image);
cvResetImageROI(image);
cvReleaseImage(&upperHalf);
}
Just call this function with the same arguments as of cvCircle.

Why doesn't this code draw a triangle?

I'm very new to OpenGL and I just wrote up a section of code using SDL 2 that to my knowledge should have drawn a triangle, but this code doesn't seem to work and so I am not done learning. I've got all the initialization code SDL 2 documentation says I need already written in, and the functions returned by dynamic loading ARE callable. When I execute this code instead of a triangle I get a black (but cleared) window. Why does this code not draw the triangle I want, and why is the window cleared to black by this code? I want to know the technical details behind mainly the first question so I can depend on it later.
(*main_context.glViewport)(0, 0, 100, 100);
(*main_context.glBegin)(GL_TRIANGLES);
(*main_context.glColor4d)(255, 255, 255, 255);
(*main_context.glVertex3d)(1, 1, -50);
(*main_context.glVertex3d)(1, 30, 1);
(*main_context.glVertex3d)(30, 1, 1);
(*main_context.glEnd)();
(*main_context.glFinish)();
(*main_context.glFlush)();
SDL_GL_SwapWindow(window);
Update:
I've revised my code to include different coordinates and I got the triangle to draw, but I cannot get it to draw when farther away. Why is that?
(*main_context.glVertex3d)(2, -1, 1); /* Works. */
(*main_context.glVertex3d)(2, -1, 3); /* Doesn't work. */
Unless you are setting up a projection and/or modelview matrix elsewhere in your code, it's using the default (identity matrix) transform, which is an orthographic projection with (-1, -1) at the bottom left and (1, 1) at the top right. glViewport only changes the portion of the default framebuffer being rendered to, it has no bearing on the projection whatsoever.
With an orthographic projection, the Z coordinate does not affect the screen-space position of a point, except that points outside the Z clipping planes will not be rendered. In this case, that's everything outside of -1 <= z <= 1. Given that one of your points is (1, 1, -50), this seems to be your problem.

Orthographic Projection with OpenGL and how to implement camera or object movement in space

I have made a cube with display list using GL_POLYGON.I have initialised it in the origin of the coordinates that means in (0,0,0).In my display function which is called in glutDisplayFunc I use the code:
glLoadIdentity();
glOrtho(0,0,0,0,1,1);
glMatrixMode(GL_MODELVIEW);
I want to use orthographic projection using glOrtho.Well, my question is that: Is it normal that I still can see my cube considering that my window size is 600x600?
What's more, I would like some guidelines on how to move my cube or my camera with the relative OpenGL functions.Let's say I would like to move my camera back(to z axis) or my cube to the front(to -z axis).How can I do that?
First of you also need to set glMatrixMode() to GL_PROJECTION before you call glOrtho(), So it would look like this instead.
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(...); // Replace ... with your values
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
To move the sceen you can simply call one or more of the following functions.
glTranslate*()
glRotate*()
glScale*()
You can click the above links to read how and what each function does. But basically:
glTranslate*() translates/moves the current selected matrix.
glRotate*() rotates the current selected matrix.
glScale*() scales the current selected matrix.
You can also use glPushMatrix() and glPopMatrix() to push and pop the current matrix stack.
Extra
Also be aware that you're using old and deprecated functions. You shouldn't use them, instead you're now suppose to calculate and create your own Matrix Stack.
Edit
Camera & Objects
Basically you do that by combining the above functions. Might sound harder that it actually is.
I will create an example of 1 camera and 2 objects, basically to give you the idea of how it works.
void render()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
// The Camera Rotations & Translation
glRotatef(camera_pitch, -1f, 0f, 0f);
glRotatef(camera_yaw, 0f, 1f, 0f);
glTranslate(-camera_x, -camera_y, -camera_z);
// Object 1
glPushMatrix();
glRotatef(...);
glTranslate(...);
// Render Object 1
glPopMatrix();
// Object 2
glPushMatrix();
glRotatef(...);
glTranslate(...);
// Render Object 2
glPopMatrix();
}
Again replace the ... with your own values.
The reason why need to translate the camera coordinates negatively is because why aren't moving a camera, we are actually "pushing" (translating, etc) everything away from the camera/center (Thereby the camera is in the center at all times).
Important the order in which you rotate then translate or translate and then rotate, is important. When needing to the camera transformations you always need to rotate then translate.
Edit
gluLookAt ?
gluLookAt does 100% the same, like in my example.
Example:
// The Camera Rotations & Translation
glRotatef(camera_pitch, -1f, 0f, 0f);
glRotatef(camera_yaw, 0f, 1f, 0f);
glTranslate(-camera_x, -camera_y, -camera_z);
This is my own function which does 100% the same as gluLookAt. How do I know? Because I've looked at the original gluLookAt function, and then I made the following function.
void lookAt(float eyex, float eyey, float eyez, float centerx, float centery, float centerz)
{
float dx = eyex - centerx;
float dy = eyey - centery;
float dz = eyez - centerz;
float pitch = (float) Math.atan2(dy, Math.sqrt(dx * dx + dz * dz));
float yaw = (float) Math.atan2(dz, dx);
pitch = -pitch;
yaw = yaw - 1.57079633f;
// Here you could call glLoadIdentity() if you want to reset the matrix
// glLoadIdentity();
glRotatef(Math.toDegrees(pitch), -1f, 0f, 0f);
glRotatef(Math.toDegrees(yaw), 0f, 1f, 0f);
glTranslatef(-eyex, -eyey, -eyez);
}
You might need to change the Math.* calls, since the above code isn't written in C.

Resources