I have a program that reads a 360 mono panorama and reads an IMU, drawing the correct part of the panorama based on the head location.
I am creating two windows, one per display, and do not want to rely on GLUT_STEREO. The draw() calls for each display are therefore independent, but right now they render the same thing, which is a gluSphere to represent the panorama. To draw the correct part of the sphere, IMU data (quaternion) becomes a rotation matrix, and that matrix is multiplied with the projection.
I wish to create a little bit of overlap with the two images, as shown with the following image:
For example, the red rectangle is my left display and the blue rectangle is my right display, but there is some overlap in the middle.
I was reading some article about stereo rendering, and I thought the solution would be to replace the call from gluPerspective() to glFrustum(), and simply modify both the left and right parameter at the same time. I thought subtracting some value to left/right parameter of glFrustum() on the display and adding some value to the left/right parameter of glFrustum() would do the trick. I modified the glutReshapeFunc() callback's projection matrix to do just that:
void resize(int width, int height)
{
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLdouble near = 0.1;
GLdouble far = 100.0;
GLdouble aspect = (double) width / (double) height;
GLdouble top = tan(FOVY / 360 * M_PI) * near;
GLdouble bottom = -top;
GLdouble right = top * aspect;
GLdouble left = -right;
// TODO: Canned value for testing
left += 0.5;
right += 0.5;
glFrustum(left, right, bottom, top, near, far);
// gluPerspective(FOVY, aspect, near, far);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
Unfortunately, this does not do what I expect (and I am really not sure why). I would think modifying both left and right parameter of glFrustum() would keep the same horizontal FOV but move it to the left or right. It seems to either stretch the image in or out.
I have played around with glTranslatef() on the ModelView or glLookAt(), but there place are not clear to me. Why is glFrustum() not having the right behavior please, and what am I missing?
Modify the frustum and the camera.
You need need two different camera matrices to simulate the eye separation and slightly different frustums to eliminate toe-in.
3D Stereo Rendering
Using OpenGL (and GLUT):
/* Misc stuff */
ratio = camera.screenwidth / (double)camera.screenheight;
radians = DTOR * camera.aperture / 2;
wd2 = near * tan(radians);
ndfl = near / camera.focallength;
/* Derive the two eye positions */
CROSSPROD(camera.vd,camera.vu,r);
Normalise(&r);
r.x *= camera.eyesep / 2.0;
r.y *= camera.eyesep / 2.0;
r.z *= camera.eyesep / 2.0;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
left = - ratio * wd2 - 0.5 * camera.eyesep * ndfl;
right = ratio * wd2 - 0.5 * camera.eyesep * ndfl;
top = wd2;
bottom = - wd2;
glFrustum(left,right,bottom,top,near,far);
glMatrixMode(GL_MODELVIEW);
glDrawBuffer(GL_BACK_RIGHT);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt(camera.vp.x + r.x,camera.vp.y + r.y,camera.vp.z + r.z,
camera.vp.x + r.x + camera.vd.x,
camera.vp.y + r.y + camera.vd.y,
camera.vp.z + r.z + camera.vd.z,
camera.vu.x,camera.vu.y,camera.vu.z);
MakeLighting();
MakeGeometry();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
left = - ratio * wd2 + 0.5 * camera.eyesep * ndfl;
right = ratio * wd2 + 0.5 * camera.eyesep * ndfl;
top = wd2;
bottom = - wd2;
glFrustum(left,right,bottom,top,near,far);
glMatrixMode(GL_MODELVIEW);
glDrawBuffer(GL_BACK_LEFT);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt(camera.vp.x - r.x,camera.vp.y - r.y,camera.vp.z - r.z,
camera.vp.x - r.x + camera.vd.x,
camera.vp.y - r.y + camera.vd.y,
camera.vp.z - r.z + camera.vd.z,
camera.vu.x,camera.vu.y,camera.vu.z);
MakeLighting();
MakeGeometry();
glutSwapBuffers();
Replace the glDrawBuffer() calls with appropriate FBO binds.
Related
I have a view matrix:
float left = -(float)viewPortWidth / 240, right = (float)viewPortWidth / 240, down = -(float)viewPortHeight / 240, up = (float)viewPortHeight / 240;
viewMatrix = glm::ortho(left, right, down, up, -1.0f, 1.0f);
I am dividing by 240 to be able to show 16:9 units when it is full screen. Here is how it looks like:
I have a class Camera2D and I want to give it a size capability like Unity or at least achieve something similar. Before, I was using the transformation matrix of the game object attached to the camera and multiplying it by viewMatrix, but that leads to many unwanted effects if I make game object a child of the camera. I tried adding the number of extra vertical and horizontal unites I want to left, right, down, up but it leads to stretching effects.
I want to be able to have a zoom feature where 1 would add one vertical or horizontal unit. How can I do this?
Update 1:
I tried this:
float left = -(float)(viewPortWidth + 240) / 240, right = (float)(viewPortWidth + 240) / 240, down = -(float)(viewPortHeight + 240) / 240, up = (float)(viewPortHeight + 240) / 240;
viewMatrix = glm::ortho(left, right, down, up, -1.0f, 1.0f);
But it still leads to stretching effects.
Try dividing all the parameters.
float zoom = 16.0f / 14.0f; // 114% zoom in
float left = -(float)viewPortWidth / 240, right = (float)viewPortWidth / 240, down = -(float)viewPortHeight / 240, up = (float)viewPortHeight / 240;
viewMatrix = glm::ortho(left / zoom, right / zoom, down / zoom, up / zoom, -1.0f, 1.0f);
Another approach
Or otherwise, if you'd like to control zoom level by specifying the number of units to be shown directly, try:
float n = 14.0f * unit_size; // Align 14 units horizontally in the screen.
const float aspectRatio = (float)viewPortWidth / viewPortHeight;
float left = -n * 0.5f, right = n * 0.5f, down = -n * 0.5f / aspectRatio, up = n * 0.5f / aspectRatio;
viewMatrix = glm::ortho(left, right, down, up, -1.0f, 1.0f);
I'm trying to set up a google maps style zoom-to-cursor control for my opengl camera. I'm using a similar method to the one suggested here. Basically, I get the position of the cursor, and calculate the width/height of my perspective view at that depth using some trigonometry. I then change the field of view, and calculate how to much I need to translate in order to keep the point under the cursor in the same apparent position on the screen. That part works pretty well.
The issue is that I want to limit the fov to be less than 90 degrees. When it ends up >90, I cut it in half and then translate everything away from the camera so that the resulting scene looks the same as with the larger fov. The equation to find that necessary translation isn't working, which is strange because it comes from pretty simple algebra. I can't find my mistake. Here's the relevant code.
void Visual::scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
glm::mat4 modelview = view*model;
glm::vec4 viewport = { 0.0, 0.0, width, height };
float winX = cursorPrevX;
float winY = viewport[3] - cursorPrevY;
float winZ;
glReadPixels(winX, winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
glm::vec3 screenCoords = { winX, winY, winZ };
glm::vec3 cursorPosition = glm::unProject(screenCoords, modelview, projection, viewport);
if (isinf(cursorPosition[2]) || isnan(cursorPosition[2])) {
cursorPosition[2] = 0.0;
}
float zoomFactor = 1.1;
// = zooming in
if (yoffset > 0.0)
zoomFactor = 1/1.1;
//the width and height of the perspective view, at the depth of the cursor position
glm::vec2 fovXY = camera.getFovXY(cursorPosition[2] - zTranslate, width / height);
camera.setZoomFromFov(fovXY.y * zoomFactor, cursorPosition[2] - zTranslate);
//don't want fov to be greater than 90, so cut it in half and move the world farther away from the camera to compensate
//not working...
if (camera.Zoom > 90.0 && zTranslate*2 > MAX_DEPTH) {
float prevZoom = camera.Zoom;
camera.Zoom *= .5;
//need increased distance between camera and world origin, so that view does not appear to change when fov is reduced
zTranslate = cursorPosition[2] - tan(glm::radians(prevZoom)) / tan(glm::radians(camera.Zoom) * (cursorPosition[2] - zTranslate));
}
else if (camera.Zoom > 90.0) {
camera.Zoom = 90.0;
}
glm::vec2 newFovXY = camera.getFovXY(cursorPosition[2] - zTranslate, width / height);
//translate so that position under the cursor does not appear to move.
xTranslate += (newFovXY.x - fovXY.x) * (winX / width - .5);
yTranslate += (newFovXY.y - fovXY.y) * (winY / height - .5);
updateView = true;
}
The definition of my view matrix. Called ever iteration of the main loop.
void Visual::setView() {
view = glm::mat4();
view = glm::translate(view, { xTranslate,yTranslate,zTranslate });
view = glm::rotate(view, glm::radians(camera.inclination), glm::vec3(1.f, 0.f, 0.f));
view = glm::rotate(view, glm::radians(camera.azimuth), glm::vec3(0.f, 0.f, 1.f));
camera.Right = glm::column(view, 0).xyz();
camera.Up = glm::column(view, 1).xyz();
camera.Front = -glm::column(view, 2).xyz(); // minus because OpenGL camera looks towards negative Z.
camera.Position = glm::column(view, 3).xyz();
updateView = false;
}
Field of view helper functions.
glm::vec2 getFovXY(float depth, float aspectRatio) {
float fovY = tan(glm::radians(Zoom / 2)) * depth;
float fovX = fovY * aspectRatio;
return glm::vec2{ 2*fovX , 2*fovY };
}
//you have a desired fov, and you want to set the zoom to achieve that.
//factor of 1/2 inside the atan because we actually need the half-fov. Keep full-fov as input for consistency
void setZoomFromFov(float fovY, float depth) {
Zoom = glm::degrees(2 * atan(fovY / (2 * depth)));
}
The equations I'm using can be found from the diagram here. Since I want to have the same field of view dimensions before and after the angle is changed, I start with
fovY = tan(theta1) * d1 = tan(theta2) * d2
d2 = (tan(theta1) / tan(theta2)) * d1
d1 = distance between camera and cursor position, before fov change = cursorPosition[2] - zTranslate
d2 = distance after
theta1 = fov angle before
theta2 = fov angle after = theta1 * .5
Appreciate the help.
What would I need to do in order to select an object with the mouse in OpenGL? I found something like selection buffer but I also read some where that it was deprecated. So I'm stuck and do not know what to look for. Also I'm using C++ do to do this.
For 2D, here's the code I have working -- you'll have to modify it a bit, but hopefully it will give you some ideas. This code gives you the world coordinates at "0 height" -- if something doesn't have 0 height, this may not select it properly depending on perspective.
// for the current mouse position on the screen, where does that correspond to in the world?
glm::vec2 World::world_position_for_mouse(const glm::vec2 mouse_position,
const glm::mat4 projection_matrix,
const glm::mat4 view_matrix)
{
int window_width;
int window_height;
this->graphics.get_window_dimensions(window_width, window_height);
const int mouse_x = mouse_position[0];
const int mouse_y = mouse_position[1];
// normalize mouse position from window pixel space to between -1, 1
GLfloat normalized_mouse_x = (2.0f * mouse_x) / window_width - 1.0f;
float normalized_mouse_y = 1.0f - (2.0f * mouse_y) / window_height;
glm::vec3 normalized_mouse_vector = glm::vec3(normalized_mouse_x, normalized_mouse_y, 1.0f);
glm::vec4 ray_clip = glm::vec4(normalized_mouse_vector.xy(), -1.0, 1.0);
glm::vec4 ray_eye = glm::inverse(projection_matrix) * ray_clip;
ray_eye = glm::vec4(ray_eye.xy(), -1.0, 0.0);
glm::vec3 ray_world = (glm::inverse(view_matrix) * ray_eye).xyz();
ray_world = glm::normalize(ray_world);
float l = -(camera.z / ray_world.z);
return {camera.x + l * ray_world.x, camera.y + l * ray_world.y};
}
To pan the world by the same "screen units" regardless of zoom, I use this code based on the results of the code above:
float camera_motion = time.get_wall_clock_delta() * camera_motion_per_second;
auto x1 = this->world_position_for_mouse(glm::vec2(1,0), this->cached_projection_matrix, this->cached_view_matrix).x;
auto x2 = this->world_position_for_mouse(glm::vec2(0,0), this->cached_projection_matrix, this->cached_view_matrix).x;
auto camera_change = (x1 - x2) * camera_motion;
where camera_motion is just a multiplier on how fast you want it to move combined with the time delta from the previous frame. Basically the further zoomed out you are, the faster this scrolls you per second. Whatever pixel is on the right edge of your window will take a constant time to get to the left edge regardless of zoom.
I want to display models of different sizes fitted into a view, so that the whole model is visible inside the screen.
What is the best way to do it?
I tried scaling (using glScale) the model using this formula
scaleFactor = ( screenSize / (maxModelSize * constant) )
Where size is height or width, depending on what is bigger.
Constant is 1 / (length of one screen pixel in OpenGL units)
There are two problems with this:
1. After doing some transformations, I want to be able to return to this initial scale (model is scaled to fit window) using Identity. Currently calling identity will bring the model to its original dimensions (before the "fixing" scale).
2. The "constant" is something I got by trial and error, I feels wrong method to me. I also suspect that it is not a constant at all, and depends on screen resolution and god knows what else.
Section 8.070:
The following is from a posting by
Dave Shreiner on setting up a basic
viewing system:
First, compute a bounding sphere for
all objects in your scene. This should
provide you with two bits of
information: the center of the sphere
(let ( c.x, c.y, c.z ) be that point)
and its diameter (call it "diam").
Next, choose a value for the zNear
clipping plane. General guidelines are
to choose something larger than, but
close to 1.0. So, let's say you set
zNear = 1.0; zFar = zNear + diam;
Structure your matrix calls in this
order (for an Orthographic
projection):
GLdouble left = c.x - diam;
GLdouble right = c.x + diam;
GLdouble bottom c.y - diam;
GLdouble top = c.y + diam;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(left, right, bottom, top, zNear, zFar);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
This approach should center your
objects in the middle of the window
and stretch them to fit (i.e., its
assuming that you're using a window
with aspect ratio = 1.0). If your
window isn't square, compute left,
right, bottom, and top, as above, and
put in the following logic before the
call to glOrtho():
GLdouble aspect = (GLdouble) windowWidth / windowHeight;
if ( aspect < 1.0 ) {
// window taller than wide
bottom /= aspect;
top /= aspect;
} else {
left *= aspect;
right *= aspect;
}
The above code should position the
objects in your scene appropriately.
If you intend to manipulate (i.e.
rotate, etc.), you need to add a
viewing transform to it.
A typical viewing transform will go on
the ModelView matrix and might look
like this:
GluLookAt (0., 0., 2.*diam, c.x, c.y, c.z, 0.0, 1.0, 0.0);
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 ?