I have just read the following:
Let's say we want to have the player view an object located at the
world's origin (0, 0, 0). We want the player to be viewing the object
from the coordinates (100, 100, 100). To do this we need to build a
matrix containing this data. We build this matrix using a function
called XMMatrixLookAtLH(). Here is how you use it:
XMVECTOR vecCamPosition = XMVectorSet(100, 100, 100, 0);
XMVECTOR vecCamLookAt = XMVectorSet(0, 0, 0, 0);
XMVECTOR vecCamUp = XMVectorSet(0, 1, 0, 0);
XMMATRIX matView = XMMatrixLookAtLH(vecCamPosition, vecCamLookAt, vecCamUp);
from: http://www.directxtutorial.com/Lesson.aspx?lessonid=111-6-1
I do not understand why if you want to look at an object located at 0, 0, 0, you should position the camera at 100, 100, 100... surely that would mean the camera was way past it... and then looking back at it?
thing is, if I set my camera to 0.0, 0.0, -1.0 and have my objects at a z index of 1.0... my camera can no longer see my objects...
I don't get it?
It sounds like you have 2D objects "facing" away from the camera when positioned at (0, 0, -1). If you reverse your ordering on your triangle vertices, they should show up.
You are fine to put the camera wherever you wish. If you are looking at 3D objects, you should have no issues.
In DirectX, 2D objects can only be seen when their vertices are in clockwise ordering as seen from the camera (though you can turn this off, or change it to discard counter-clockwise triangles if you wish). DirectX uses the clockwise vs counter-clockwise ordering to discard faces so it doesn't draw the backs of 3D objects.
Related
I have this little toy graphics library in rust I tinker with to learn OpenGL. I initially intended it to do only low-res 2D stuff on it, so I set up the coordinate system for pixel-space orthographic rendering, a system with (0, 0) at the top-left corner of the screen that allows sprites to be "snapped" to pixels for an 8/16-bit style.
Now I'm thinking it would be fun to add a perspective camera to render the sprites in perspective as a sort of paper-doll theater thing, sort of inverse 2.5D, world is 3D but characters are 2D.
So I use the excellent tutorial here:
https://learnopengl.com/Getting-started/Camera
Great stuff, but the algorithm calls for deriving from the up-vector [0, 1, 0], so my world renders upside down (since my "up" is [0, -1, 0]). If I do set the "up" as [0, -1, 0] I instead get the X-axis extending from right to left, as if negating "up" rotates 180° around the Z-axis, which again isn't what I want.
Is it at all possible to do a perspective camera view matrix with a typical orthographic world Y-down coordinate system, and how would that be done practically using e.g. glm?
The trick with the reflection matrix works; The orientation and position of all objects on screen works out and camera movement produces the expected results.
This page is what tipped me off on the fact that the only difference between left and right-hand view matrixes is the direction of the X-axis, and that mirroring/reflections changes the handedness:
https://www.realtimerendering.com/blog/left-handed-vs-right-handed-viewing/
That should make sense, if you compare your hands they only differ in what side the thumb is on, not in what direction the fingers are pointing.
The solution:
Create a standard right-handed view matrix, but define the up-vector as [0, -1, 0]
Create a reflection matrix that flips the view around the Y-axis:
[-1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0
0, 0, 0, 1]
multiply the reflection and view matrix:
reflection_matrix * view_matrix;
This works in OpenGL, but allegedly DirectX works with row-major matrices, so you have to take that into account when multiplying.
This is the code I use to produce the view matrix:
let eye: glm::Vec3 = glm::vec3(160.0, 90.0, 300.0);
let target: glm::Vec3 = glm::vec3(160.0, 90.0, 0.0);
let cam_forward = glm::normalize(&(&eye - &target));
let up = glm::Vec3::new(0.0, -1.0, 0.0);
let cam_right = glm::normalize(&glm::cross(&up, &cam_forward));
let cam_up = glm::normalize(&glm::cross(&cam_forward, &cam_right));
let mut mx_view = glm::look_at(&eye, &target, &cam_up);
let mut mirr: glm::Mat4 = glm::Mat4::identity();
mirr[0] = -1.0;
mx_view = mirr * mx_view;
The camera looks down the Z-axis of my 320 by 180 pixel world.
This is of-course in rust, but, based on the excellent nalgebra-glm crate which pretty closely replicates what glm does for C++, so I think anyone using C++ should also be able to follow.
I do not recommend changing the camera matrix.
Instead, try to use glClipControl(GL_UPPRER_LEFT,GL_ZERO_TO_ONE); in this situation, the front face will be CW.
Or simply multiply the projection matrix P[1][1] by -1.
How to move camera in OpenGL to capture 6 cube face images and save into files (like image below)?
What does "plugin" means?
I'm confused that you need how to calculate camera position&direction vectors for capture each side of dice or how to implement lookat&perspective functions.
for lookat&perspective functions, there are many resources to refer :
Can't understand gluLookAt arguments
gluPerspective parameters- what do they mean?
these functions are usually provided on many libraries, but if you need, then I will post my implementation.
Camera position and direction/up vector is calculated for viewing each side of dice squarely. to do this, you have to care about perspective FOV(field of view) with respect to distance between camera and dice.
If you read above posts carefully, you can determine arguments for these functions.
If once you see each side on screen, I think you need the method combining the result scene of each dice into one screen(or FBO) and save it.
If once you obtain 6 sets of arguments for lookat and perspective, you can use glViewPort.
// if pixel per each side : 10
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
//back side draw
glViewPort(0, 10, 10, 10);
//call glulookat & gluperspective or equivalent functions with arguments so that back side of dice drew on your Viewport fully
gluLookAt(...);
gluPerpective(...);
glDrawElements(...);
//up side draw
glViewPort(10, 0, 10, 10);
gluLookAt(...);
gluPerpective(...);
glDrawElements(...);
//left side draw
glViewPort(10, 10, 10, 10);
gluLookAt(...);
gluPerpective(...);
glDrawElements(...);
...
The above code draw 6 times in each selected viewport of your result FBO.
An example using PyQt5 for making an image of a plane with size X, Y in the z=0 plane.
Xt = X/2 #center of your camera in X
Yt = Y/2 #center of your camera in Y
dist = math.tan(math.radians(60))*Y/2 #Compute the distance of the campera from plane
#assuming a 60 degree projection
aspect = float(self.width)/float(self.height) #aspect ratio of display
center = QtGui.QVector3D(Xt, Yt, 0) #look at this point
eye = QtGui.QVector3D(Xt, Yt, dist) #Point of Camera in space
up = QtGui.QVector3D(0, 1, 0)
self.modelview = QtGui.QMatrix4x4()
self.modelview.lookAt(eye,center,up) #build modelview matrix with given parameters
self.projection = QtGui.QMatrix4x4()
self.projection.perspective(60.0, aspect, dist*0.0001, dist*10000.0) #build projection matrix
repeating this process for each side + adjusting the z distance to your cube should yield the desired result. The you can just write your results to a framebuffer and read that buffer into an array.
I'm new to OpenGL and I'm trying to understand how the projection matrix works in it.
To create a simple case, I define a triangle in the world space and its coordinates are:
(0,1,0), (1,0,0), (-1,0,0)
I set the modelview matrix and projection matrix as below:
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(
0, 0, 2,
0, 0, 0,
0, 1, 0);
glMatrixMode(GL_PROJECTION);
glOrtho(-2, 2, -2, 2, -0.1, -2.0); // does not work
// glOrtho(-2, 2, -2, 2, 0.1, 2.0); // works
From my understanding, gluLookAt() is used to set the viewing matrix. Since OpenGL does not have a concept of "camera", and thus it transforms the entire world to reach the effect of a camera. In the above code, I assume the "camera" is at (0,0,2), looking at (0,0,0). So OpenGL internally moves the triangle backwards along z axis to z=-2.
To define a view frustum, glOrtho() get 6 parameters. To make the triangle visible in the frustum, I set the near and far value to -0.1 and -2.0 respectively and this should indicate that the frustum include [-0.1, -2.0] on z axis.
I searched for similar questions and found out someone states that the last two parameters of glOrtho() is in fact -near and -far. But if this is correct, the following code should work(but it doesn't):
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(
0, 0, -2, // changed 2 to -2, thus the triangle should be transformed to z=2?
0, 0, 0,
0, 1, 0);
glMatrixMode(GL_PROJECTION);
glOrtho(-2, 2, -2, 2, -0.1, -2.0); // -near=-0.1, -far=-2.0, thus the frustum should include [0.1, 2.0], thus include the triangle
If I'm correct, the triangle should be drawn on the screen, so there must be something wrong with my code. Can anyone help?
First of all note, that the fixed function pipeline matrix stack and drawing by glBegin/glEnd sequences is deprecated since more than 10 years.
Read about Fixed Function Pipeline and see Vertex Specification for a state of the art way of rendering.
If you use a view matrix like this:
gluLookAt(0, 0, 2, 0, 0, 0, 0, 1, 0);
Then the values for the near and the far plane have to be positive when you set up the the projection matrix,
glOrtho(-2, 2, -2, 2, 0.1, 2.0);
because, gluLookAt transforms the vertices to view space (in view space the z axis points out of the viewport), but the projection matrix inverts the z-axis.
But be careful, since the triangle is at z=0
(0,1,0), (1,0,0), (-1,0,0)
and the distance from the camera to the triangle is 2, because of the view matrix, the triangle is placed exactly on the far plane (which is 2.0 too). I recommend to increase the distance to the far plane from 2.0 to (e.g.) 3.0:
glOrtho(-2, 2, -2, 2, 0.1, 3.0);
If you change the view matrix,
gluLookAt(0, 0, -2, 0, 0, 0, 0, 1, 0);
then still the (view space) z-axis points out of the viewport, but you look at the "back" side of the triangle. The triangle is still in the center of the view (0, 0, 0), but the camera position has changed. The triangle is still in front of the camera.
If you would do
gluLookAt(0, 0, 2, 0, 0, 4, 0, 1, 0);
then you would look away from the triangle. You would have to project the backside of the view to the viewport to "see" the triangle (glOrtho(-2, 2, -2, 2, -0.1, -3.0);).
Further note, that glOrtho multiplies the current matrix by the orthographic projection matrix. This means you should set the identity matrix, before you use glOrtho, as you do it with the model view matrix:
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-2, 2, -2, 2, 0.1, 2.0);
Explanation
The projection, view and model matrix interact together to present the objects (meshes) of a scene on the viewport.
The model matrix defines the position orientation and scale of a single object (mesh) in the worldspace of the scene.
The view matrix defines the position and viewing direction of the observer (viewer) within the scene.
The projection matrix defines the area (volume) with respect to the observer (viewer) projected onto the viewport.
At orthographic projection, this area (volume) is defined by 6 distances (left, right, bottom, top, near and far) to the viewer's position.
View matrix
The view coordinates system describes the direction and position from which the scene is looked at. The view matrix transforms from the wolrd space to the view (eye) space.
If the coordiante system of the view space is a Right-handed system, then the X-axis points to the left, the Y-axis up and the Z-axis out of the view (Note in a right hand system the Z-Axis is the cross product of the X-Axis and the Y-Axis).
Projection matrix
The projection matrix describes the mapping from 3D points of the view on a scene, to 2D points on the viewport. It transforms from eye space to the clip space, and the coordinates in the clip space are transformed to the normalized device coordinates (NDC) by dividing with the w component of the clip coordinates. The NDC are in range (-1,-1,-1) to (1,1,1). Every geometry which is out of the clippspace is clipped.
At Orthographic Projection the coordinates in the view space are linearly mapped to clip space coordinates and the clip space coordinates are equal to the normalized device coordinates, because the w component is 1 (for a cartesian input coordinate).
The values for left, right, bottom, top, near and far define a box. All the geometry which is inside the volume of the box is "visible" on the viewport.
The Orthographic Projection Matrix looks like this:
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
The z-axis is inverted by the projection matrix.
I'm trying to draw a cylinder in a specific direction with gluCylinder. To specify the direction I use gluLookAt, however, as so many before me, I am not sure about the "up" vector and thus can't get the cylinder to point to the correct direction.
I've read from another SO answer that
The intuition behind the "up" vector in gluLookAt is simple: Look at anything. Now tilt your head 90 degrees. Where you are hasn't changed, the direction you're looking at hasn't changed, but the image in your retina clearly has. What's the difference? Where the top of your head is pointing to. That's the up vector.
It is a simple explanation but in the case of my cylinder I feel like the up vector is totally unimportant. Since a cylinder can be rotated around its axis and still look the same, a different up vector wouldn't change anything. So there should be infinitely many valid up vectors for my problem: all orthogonals to the vector from start point to end point.
So this is what I do:
I have the world coordinates of where the start-point and end-point of the cylinder should be, A_world and B_world.
I project them to viewport coordinates A_vp and B_vp with gluProject:
GLdouble A_vp[3], B_vp[3], up[3], model[16], projection[16];
GLint gl_viewport[4];
glGetDoublev(GL_MODELVIEW_MATRIX, &model[0]);
glGetDoublev(GL_PROJECTION_MATRIX, &projection[0]);
glGetIntegerv(GL_VIEWPORT, gl_viewport);
gluProject(A_world[0], A_world[1], A_world[2], &model[0], &projection[0], &gl_viewport[0], &A_vp[0], &A_vp[1], &A_vp[2]);
gluProject(B_world[0], B_world[1], B_world[2], &model[0], &projection[0], &gl_viewport[0], &B_vp[0], &B_vp[1], &B_vp[2]);
I call glOrtho to reset the camera to its default position: Negative z into picture, x to the right, y up:
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, vp_edgelen, vp_edgelen, 0, 25, -25);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
I translate to coordinate A_vp, calculate the up vector as the normal to the vector A_vp — B_vp and specify the view with gluLookAt:
glTranslatef(A_vp[0], gl_viewport[2] - A_vp[1], A_vp[2]);
glMatrixMode(GL_MODELVIEW);
GLdouble[] up = {A_vp[1] * B_vp[2] - A_vp[2] * B_vp[1],
A_vp[2] * B_vp[0] - A_vp[0] * B_vp[2],
A_vp[0] * B_vp[1] - A_vp[1] * B_vp[0]};
gluLookAt(0, 0, 0,
B_vp[0], gl_viewport[2] - B_vp[1], B_vp[2],
up[0], up[1], up[2]);
I draw the cylinder with gluCylinder:
GLUquadricObj *gluCylObj = gluNewQuadric();
gluQuadricNormals(gluCylObj, GLU_SMOOTH);
gluQuadricOrientation(gluCylObj, GLU_OUTSIDE);
gluCylinder(gluCylObj, 10, 10, 50, 10, 10);
Here is the unexpected result:
Since the cylinder starts at the correct position and since I was able to draw a circle at position B_vp, the only thing that must be wrong is the "up" vector in gluLookAt, right?
gluLookAt() is not necessary to achieve the proper perspective. It is enough to rotate the current z-vector to point to the direction the cylinder should point.
I am a beginner in openGL. I am currently working on a program which take in inputs the width and the length of a board. Given those inputs i want to dynamically position my camera so that i can have a view on the whole board. Let' s say that my window size is 1024x768.
Are there any mathematical formula to compute the different parameters of the opengl function glookat to make it possible ?
the view i want to have on the board should look like this.
It doesn't matter if a board too big will make things look tiny. What matters the most here is to position the camera in a way that the view on the whole board is made possible
So far i am hopelessly randomly changing the parameters of my glookat function till i ran into something decent for a X size width and and Y size Height.
my gluperpective function :
gluPerspective(70 ,1024 / 768,1,1000)
my glooatfunction for a 40 * 40 board
gluLookAt(20, 20, 60, 20, -4, -20, 0, 1, 0);
how i draw my board (plane):
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
gluLookAt(20, 20, 60, 20, -4, -20, 0, 1, 0);
glBindTexture(GL_TEXTURE_2D, texture_sol);
glBegin(GL_QUADS);
glTexCoord2i(0, 0); glVertex3i(width, 0, height);
glTexCoord2i(10, 0); glVertex3i(0, 0, height)
glTexCoord2i(10, 10); glVertex3i(0, 0, 0);
glTexCoord2i(0, 10); glVertex3i(width, 0, 0);
glEnd();
the output looks as follow :
gluLookAt takes 2 points and a vector; the eye and centre positions and the up vector. There's no issue with the last parameter. The first two are relevant to your question.
I see that your board in the world space is extending on the positive X and Y axes with some arbitrary width and height values. Lets take width = height = 1.0 for instance. So the board spans from (0, 0), (1, 0), (1, 1), (0, 1); the Y value is ignored here since the board lies on the Y = 0 plane and have the same value for all vertices; these are just (X, Z) values.
Now coming to gluLookAt, eye is where the camera is in world space and centre is the point where you want the camera to be looking at (in world space)
Say you want the camera to look at centre of the board I presume, so
eye = (width / 2.0f, 0, height/2.0f);
Now you've to position the camera at its vantage point. Say somewhere above the board but towards the positive Z direction since there's where the user is (assuming your world space is right handed and positive Z direction is towards the viewer), so
centre = (width / 2.0f, 5.0f, 1.0f);
Since the farthest point on Z is 0, I just added one more to be slightly father than that. Y is how much above you want to see the board from, I just chose 5.0 as an example. These are just arbitrary values I can come up with, you'll still have to experiment with these values. But I hope you got the essence of how gluLookAt works.
Though this is written as an XNA tutorial, the basic technique and math behind it should carry over to OpenGL and your project:
Positioning the Camera to View All Scene Objects
Also see
OpenGL FAQ
8.070 How can I automatically calculate a view that displays my entire model? (I know the bounding sphere and up vector.)
Edit in response to the comment question
A bounding sphere is simply a sphere that completely encloses your model. It can be described as:
A bounding sphere, S, of a point set P with n points is described by
a center point, c, and a radius, r.
So,
P = the vertices of your model (the board in this case)
c = origin of your model
r = distance from origin of the vertex, in P, farthest from the origin
So the Bounding Sphere for your board would be composed of the origin location (c) and the distance from one corner to the origin (r) assuming the board is a square and all points are equidistant.
For more complicated models, you may employ pre-created solutions [1] or implement your own calculations [2] [3]