I'm following the OpenGL tutorials on opengl-tutorial.org, and in tutorial 4 the author proposed, as a side "project", to send two "objects" to OpenGL and we have only ever rendered one object before. Specifically, he asks this:
Draw the cube AND the triangle, at different locations. You will need to generate 2 MVP matrices, to make 2 draw calls in the main loop, but only 1 shader is required.
We define the MVP matrix like this:
GLuint MatrixID = glGetUniformLocation(programID, "MVP");
glm::mat4 Projection = glm::perspective(90.0f, 4.0f / 3.0f, 0.1f, 100.0f);
// Camera matrix
glm::mat4 View = glm::lookAt(
glm::vec3(0,0,3), // Camera is at (4,3,3), in World Space
glm::vec3(0,0,0), // and looks at the origin
glm::vec3(0,1,0) // Head is up (set to 0,-1,0 to look upside-down)
);
glm::mat4 Model = glm::mat4(1.0f);
glm::mat4 MVP = Projection * View * Model;
Now I understand that only one MVP matrix is used per object. But I have a few questions.
How do I create a MVP matrix for each object? Do I have to re-create the View and Model matrices for every object (IE specify a new one for each object)?
I thought glm::lookAt was used as the "camera" of sorts, so I shouldn't have to create a new "Camera" for each object, should I?
What does the Model matrix do? The author says it is a identity matrix right now, but if I change that to say 100, nothing happens! I thought that the Model matrix was the matrix that defined where the model was rendered, but I can't seem to change where the model is rendered without modifying the View matrix (which makes sense).
Typically, one uses one projection and one view matrix per "camera". The view matrix basically represents the position and orientation of the camera, and the projection matrix represents the properties of the camera (field of view, aspect, etc. in some way like a "lens" of a real camera, but don't stretch that analogy too far).
A model matrix typically is used to place the model in some global "world cooridnate system". So, you need a model matrix per object, so each object can be placed independently.
The world coordiante system is not strictly needed at all. What matters to the resulting picture is how the objects are placed relative to the camera - that is there ModelView enters the picture - as the composition of the model and the view transform, directly going from object space to some camera-relative eye space, bypassing any "world space" completely.
In your case, you even further combine this with the projection matrix, so you go directly from object space to clip space.
As you can see, the MVP matrix needs to change if any of the distinct matrices it is composed of does change. You could in theory precalculate an MVP per object - but that is typically not done, since the camera is moveable and all those MVP matrices would have to be recalculated anyways - there is not much to save here. So one typically keeps these matrices separate and multiplies them together directly before the draw call.
What does the Model matrix do? The author says it is a identity matrix right now, but if I change that to say 100, nothing happens!
What does this even mean, "changing a matrix to 100"? From the above code snippets it can be seen that your model matrix is indeed used and affects the MVP matrix. So changing it should have an effect - depending on what you change, of course. If you just changed the line
glm::mat4 Model = glm::mat4(1.0f);
to
glm::mat4 Model = glm::mat4(100.0f);
then it would actually not lead to any observeable change. You would get different clip space coords, but the differences would cancel each other out and the produced image would have been the same. That would be due to how homogenous coordinates work, and is a different matter entirely.
Related
I'm currently using OpenGL(Glad, GLFW, GLM). I just started learning and can't find the correct way to take a translation and actually render it. For example, I want to rotate 1 degree every frame. I've seen tutorials on how to use GLM to make those translations but I can't seem to figure out how to take something like this: glm::mat4 translate = glm::translate(glm::mat4(1.f), glm::vec3(2.f, 0.f, 0.f)); and apply it to an object rendered like this:
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES,3, GL_UNSIGNED_INT, 0);
I'm really not sure what I'm not understanding and any help would be appreciated.
Edit: I know you can just change the vertices and rebind the array, but that seems like it would be pretty slow. Would that work?
I am assuming that you are somewhat familiar with the concept of the different coordinate systems and transformations like the projection, view and model matrix. If not, you should read up on them here https://learnopengl.com/Getting-started/Coordinate-Systems.
In short, the model matrix transforms the object as a whole in the world. In most cases it contains translation, rotation and scaling. It is important to note that the order of matrix multiplication is generally defined as
glm::mat4 model = translate * rotate * scale;
The view matrix is needed to get the view of the camera and the projection matrix adds perspective and is used to determine what is on the screen and will be rendered.
To apply the transformation to the object that you want to draw with your shader, you will need to load the matrix into the shader first.
glm::mat4 model = glm::translate(glm::mat4(1.f), glm::vec3(2.f, 0.f, 0.f));
unsigned int modelMatrixLoc = glGetUniformLocation(ourShader.ID, "model");
glUniformMatrix4fv(modelMatrixLoc , 1, GL_FALSE, glm::value_ptr(model));
Here, the model matrix would be loaded into the shader under the name "model". You can then use this matrix in your shader to transform the vertices on the GPU.
A simple shader would then look like this
#version 460 core
layout (location = 0) in vec3 position_in;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(position_in, 1.0);
}
To ensure that your normal vectors do not get skewed, you can calculate another matrix
glm::mat3 model_normal = glm::transpose(glm::inverse(model));
If you were to add this to the shader, it would look something like this
#version 460 core
layout (location = 0) in vec3 position_in;
layout (location = 1) in vec3 normal_in;
uniform mat4 model;
uniform mat3 model_normal;
uniform mat4 view;
uniform mat4 projection;
out vec3 normal;
void main()
{
gl_Position = projection * view * model * vec4(position_in, 1.0);
normal = model_normal * normal_in;
}
Notice how we can now use a mat3 instead of a mat4? This is because we do not want to translate the normal vector and the translation part of the matrix is located in the fourth column, which we cut away here. This also means that it is important to set the last component of the 4d vector to 1 if we want to translate and to 0 if we do not.
Edit: I know you can just change the vertices and rebind the array, but that seems like it would be pretty slow. Would that work?
You can always edit your model if you want to change its looks. However, the transformations are going to be way slower on the CPU. Remember that the GPU is highly optimized for this kind of task. So I would advise to do most transformations on the GPU.
If you only need to change a part of the vertices of your object, updating the vertices will work better in most cases.
How would I constantly rotate as a function of time?
To rotate as a function of time, there are multiple approaches. To clarify, transformations that are applied to an object in the shader are not permanent and will be reset for the next draw call. If the rotation is performed with the same axis, it is a good idea to specify the angle somewhere and then calculate a single rotation (matrix) for that angle. To do this with GLFW, you can use
const double radians_per_second = ... ;
const glm::vec3 axis = ... ;
// rendering loop start
double time_seconds = glfwGetTime();
float angle = radians_per_second * time_seconds;
glm::mat4 rotation = glm::rotate(glm::mat4(1.f), angle, axis);
On the other hand, if the rotations are not performed on the same axis, you will have to multiply both rotation matrices together.
rotation = rotation * additional_rotation;
In both cases, you need to set the rotation for your model matrix like I explained above.
Also, if I wanted to make a square follow the mouse, would I have to rebuild the vertices every time the mouse moves?
No you do not need to do that. If you just want to move the square to the position of the mouse, you can use a translation. To get the mouse position in world space, it seems that you can use glm::unProject( ... );. I have not tried this yet, but it looks like it could solve your problem. You can take a look at it here
https://glm.g-truc.net/0.9.2/api/a00245.html#gac38d611231b15799a0c06c54ff1ede43.
If you need more information on this topic, you can take a look at this thread where it has already been answered
Using GLM's UnProject.
what's the GLFW/GLAD function to change the camera's position and rotation?
That is just the view matrix. Look here again.
I'm basically trying to create the FPS camera. I figured out movement but I can't figure out rotation.
You can take a look here learnopengl.com/Getting-started/Camera. Just scroll down until you see the section "Look around". I think that the explanation there is pretty good.
I already looked at that but was confused about one thing. If each rendered cube has view, perspective, and model, how would I change the camera's view, perspective, and model?
Ok, I think that I understand the misconception here. Not all 3 matrices are per object. The model matrix is per object, the view matrix per camera and the projection matrix per viewing mode (e.g. perspective with fov of 90° or orthogonal) so usually only once.
I want to rotate an object in a 3d scene. In the code below I simply rotated the WorldMatrix. But what if the scene contained 2 objects instead of one? If I rotated the WorldMatrix both would rotate (in a weird way). How should I rotate a single object in a scene without altering any other model?
// Clear the buffers to begin the scene.
m_OpenGL->BeginScene(0.0f, 0.0f, 0.0f, 1.0f);
// Generate the view matrix based on the camera's position.
m_Camera->Render();
// Get the world, view, and projection matrices from the opengl and camera objects.
m_OpenGL->GetWorldMatrix(worldMatrix);
m_Camera->GetViewMatrix(viewMatrix);
m_OpenGL->GetProjectionMatrix(projectionMatrix);
// Get the light properties.
m_Light->GetDirection(lightDirection);
m_Light->GetDiffuseColor(diffuseLightColor);
m_Light->GetAmbientLight(ambientLight);
// Rotate the world matrix by the rotation value so that the object will spin.
m_OpenGL->MatrixRotationY(worldMatrix, rotation);
// Set the light shader as the current shader program and set the matrices that it will use for rendering.
m_LightShader->SetShader(m_OpenGL);
m_LightShader->SetShaderParameters(m_OpenGL, worldMatrix, viewMatrix, projectionMatrix, 0, lightDirection, diffuseLightColor, ambientLight);
// Render the model using the light shader.
m_Model->Render(m_OpenGL);
// Present the rendered scene to the screen.
m_OpenGL->EndScene();
Each "object" that you wish to render should include, at minimum, its own 4x4 matrix containing rotation and position information. That way, if you want to rotate only a single object, you just edit it's own personal matrix.
The easiest way to manage all of these matrix operations is a general purpose matrix stack.
Unfortunately for you, the built-in OpenGL matrix stack functionality (glPush, glPop, etc.) is deprecated along with most of the old fixed-function pipeline. But fortunately for you, a fellow StackOverflow user posted a bare-bones matrix stack: Replacing glPush/PopMatrix.
Have an "object matrix" for each object, which you push before rendering that object and pop afterwards. With this in place, you can modify the object matrix of each object in order to rotate it (or transform it in any other way).
First of all you should draw your object to rotate.
void DrawObject(Object* object)
{
glTranslate(object->y);
glRotate(object->rotationY, roll, yaw , pitch);
}
According to MSDN, we are multiplying the current matrix with the perspective matrix. What matrix are we talking about here? Also from MSDN:
"The glFrustum function multiplies the current matrix by this matrix, with the result replacing the current matrix. That is, if M is the current matrix and F is the frustum perspective matrix, then glFrustum replaces M with M • F."
Now, from my current knowledge of grade 12 calculus (in which I am currently), M is a vector and M dot product F returns a scalar, so how can one replace a vector with a scalar?
I'm also not sure what "clipping" planes are and how they can be referenced via one float value.
Please phrase your answer in terms of the parameters, and also conceptually. I'd really appreciate that.
I'm trying to learn openGL via this tutorial: http://www.youtube.com/watch?v=WdGF7Bw6SUg. It is actually not really good because it explains nothing or else it assumes one knows openGL. I'd really appreciate a link to a tutorial otherwise, I really appreciate your time and responses! I'm sure I can struggle through and figure things out.
You misunderstand what it's saying. M is a matrix. M•F therefore is also a matrix. It constructs a perspective matrix. See this article for an explanation of how it is constructed and when you want to use glFrustum() vs. gluPerspective():
glFrustum() and gluPerspective() both produce perspective projection matrices that you can use to transform from eye coordinate space to clip coordinate space. The primary difference between the two is that glFrustum() is more general and allows off-axis projections, while gluPerspective() only produces symmetrical (on-axis) projections. Indeed, you can use glFrustum() to implement gluPerspective().
Clipping planes are planes that cut out sections of the world so they don't have to be rendered. The frustum describes where the planes are in space. Its sides define the view volume.
glFrustum generates a perspective projection matrix.
This matrix maps a portion of the space (the "frustum") to your screen. Many caveats apply (normalized device coordinates, perspective divide, etc), but that's the idea.
The part that puzzles you is a relic of the deprecated OpenGL API. You can safely ignore it, because usually you apply gluFrustum() on an identity projection matrix, so the multiplication mentioned in the doc has no effect.
A perhaps more comprehensible way to do things is the following :
// Projection matrix : 45° Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units
glm::mat4 Projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f);
// Or, for an ortho camera :
//glm::mat4 Projection = glm::ortho(-10.0f,10.0f,-10.0f,10.0f,0.0f,100.0f); // In world coordinates
// Camera matrix
glm::mat4 View = glm::lookAt(
glm::vec3(4,3,3), // Camera is at (4,3,3), in World Space
glm::vec3(0,0,0), // and looks at the origin
glm::vec3(0,1,0) // Head is up (set to 0,-1,0 to look upside-down)
);
// Model matrix : an identity matrix (model will be at the origin)
glm::mat4 Model = glm::mat4(1.0f);
// Our ModelViewProjection : multiplication of our 3 matrices
glm::mat4 MVP = Projection * View * Model; // Remember, matrix multiplication is the other way around
Here, I used glm::perspective (the equivalent of old-style gluPerspective() ), but you can use gluFrustum too.
As for the arguments of the function, they are best explained by this part of the doc :
left bottom - nearVal and right top - nearVal specify the points on
the near clipping plane that are mapped to the lower left and upper
right corners of the window, assuming that the eye is located at (0,
0, 0)
If you're unfamiliar with transformation matrices, I wrote a tutorial here; the "Projection Matrix" section especially should help you.
The problem I have is that I cant get "line of sight" vector in OpenGL. I've done some research and found that it should be Z vector after transformations, but it doesn't want to work. I've got this code to retrive velocity of the block( i want it to move foward from "camera"), but all the time it moves irrelevant to camera, but all the time same way compared to rendered world:
GLfloat matrix[16];
glGetFloatv (GL_MODELVIEW_MATRIX, matrix);
GLfloat d = sqrt( matrix[8]*matrix[8] + matrix[9]*matrix[9] + matrix[10]*matrix[10]);
xmov = matrix[8]/d;
ymov = matrix[9]/d;
zmov = matrix[10]/d;
What I've done wrong?
Okay, now after you clarified what you really want to do, I'm pretty sure this is the correct answer:
You are probably used to something called ModelView matrix. Didn't it seem strange for you that it's essentially combined of two parts? Well, it was for me, and after I thought about it for a while, it made sense. The final vertex position is calculated like this:
gl_Position = ProjectionMat * ModelViewMat * VertexPos;
You see, there's no difference for OpenGL whether you move "camera" from origin by [x,y,z] or move objects by [-x,-y,-z] - you'll get the same results. It is however useful to distinguish a "camera" position as something that can be different from the origin.
gl_Position = ProjectionMat * ViewMat * ModelMat * VertexPos;
I think the most natural way to do it is, as I said, split the calculations into two matrices : Model and View. Every object on the scene now has to change the Model matrix, and camera position is set by changing View matrix. Makes sense?
I'll give you an example. If your camera is in [5,0,0] (which corresponds to Translate(-5,0,0)), and your object in [-5,0,0], it will land 10 units from the camera. Now, when you move your camera further away from origin (increasing the first translation distance), distance between "camera" and the object grows.
The object translation is the Model, the camera translation is the View.
So it's not hard to come to conclusion, that if you want to ignore camera position, just strip the View part from the equation; every object will now be drawn without taking camera position into account, thus relative only to your viewport.
gl_Position = ProjectionMat * ModelMat * VertexPos;
Our hypothetical model will now land 5 units along the X axis from the viewport regardless of what you're currently "looking at", and that's, I think, pretty much what you wanted to achieve.
Anyway, you could probably use a nice tutorial about it
In opengl there is one world coordinate system with origin (0,0,0).
What confuses me is what all the transformations like glTranslate, glRotate, etc. do? Do they move
objects in world coordinates, or do they move the camera? As you know, the same movement can be achieved by either moving objects or camera.
I am guessing that glTranslate, glRotate, change objects, and gluLookAt changes the camera?
In opengl there is one world coordinate system with origin (0,0,0).
Well, technically no.
What confuses me is what all the transformations like glTranslate, glRotate, etc. do? Do they move objects in world coordinates, or do they move the camera?
Neither. OpenGL doesn't know objects, OpenGL doesn't know a camera, OpenGL doesn't know a world. All that OpenGL cares about are primitives, points, lines or triangles, per vertex attributes, normalized device coordinates (NDC) and a viewport, to which the NDC are mapped to.
When you tell OpenGL to draw a primitive, each vertex is processed according to its attributes. The position is one of the attributes and usually a vector with 1 to 4 scalar elements within local "object" coordinate system. The task at hand is to somehow transform the local vertex position attribute into a position on the viewport. In modern OpenGL this happens within a small program, running on the GPU, called a vertex shader. The vertex shader may process the position in an arbitrary way. But the usual approach is by applying a number of nonsingular, linear transformations.
Such transformations can be expressed in terms of homogenous transformation matrices. For a 3 dimensional vector, the homogenous representation in a vector with 4 elements, where the 4th element is 1.
In computer graphics a 3-fold transformation pipeline has become sort of the standard way of doing things. First the object local coordinates are transformed into coordinates relative to the virtual "eye", hence into eye space. In OpenGL this transformation used to be called the modelview transformaion. With the vertex positions in eye space several calculations, like illumination can be expressed in a generalized way, hence those calculations happen in eye space. Next the eye space coordinates are tranformed into the so called clip space. This transformation maps some volume in eye space to a specific volume with certain boundaries, to which the geometry is clipped. Since this transformation effectively applies a projection, in OpenGL this used to be called the projection transformation.
After clip space the positions get "normalized" by their homogenous component, yielding normalized device coordinates, which are then plainly mapped to the viewport.
To recapitulate:
A vertex position is transformed from local to clip space by
vpos_eye = MV · vpos_local
eyespace_calculations(vpos_eye);
vpos_clip = P · vpos_eye
·: inner product column on row vector
Then to reach NDC
vpos_ndc = vpos_clip / vpos_clip.w
and finally to the viewport (NDC coordinates are in the range [-1, 1]
vpos_viewport = (vpos_ndc + (1,1,1,1)) * (viewport.width, viewport.height) / 2 + (viewport.x, viewport.y)
*: vector component wise multiplication
The OpenGL functions glRotate, glTranslate, glScale, glMatrixMode merely manipulate the transformation matrices. OpenGL used to have four transformation matrices:
modelview
projection
texture
color
On which of them the matrix manipulation functions act on can be set using glMatrixMode. Each of the matrix manipulating functions composes a new matrix by multiplying the transformation matrix they describe on top of the select matrix thereby replacing it. The functions glLoadIdentity replace the current matrix with identity, glLoadMatrix replaces it with a user defined matrix, and glMultMatrix multiplies a user defined matrix on top of it.
So how does the modelview matrix then emulate both object placement and a camera. Well, as you already stated
As you know, the same movement can be achieved by either moving objects or camera.
You can not really discern between them. The usual approach is by splitting the object local to eye transformation into two steps:
Object to world – OpenGL calls this the "model transform"
World to eye – OpenGL calls this the "view transform"
Together they form the model-view, in fixed function OpenGL described by the modelview matrix. Now since the order of transformations is
local to world, Model matrix vpos_world = M · vpos_local
world to eye, View matrix vpos_eye = V · vpos_world
we can substitute by
vpos_eye = V · ( M · vpos_local ) = V · M · vpos_local
replacing V · M by the ModelView matrix =: MV
vpos_eye = MV · vpos_local
Thus you can see that what's V and what's M of the compund matrix M is only determined by the order of operations in which you multiply onto the modelview matrix, and at which step you decide to "call it the model transform from here on".
I.e. right after a
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
the view is defined. But at some point you'll start applying model transformations and everything after is model.
Note that in modern OpenGL all the matrix manipulation functions have been removed. OpenGL's matrix stack never was feature complete and no serious application did actually use it. Most programs just glLoadMatrix-ed their self calculated matrices and didn't bother with the OpenGL built-in matrix maniupulation routines.
And ever since shaders were introduced, the whole OpenGL matrix stack got awkward to use, to say it nicely.
The verdict: If you plan on using OpenGL the modern way, don't bother with the built-in functions. But keep in mind what I wrote, because what your shaders do will be very similar to what OpenGL's fixed function pipeline did.
OpenGL is a low-level API, there is no higher-level concepts like an "object" and a "camera" in the "scene", so there are only two matrix modes: MODELVIEW (a multiplication of "camera" matrix by the "object" transformation) and PROJECTION (the projective transformation from world-space to post-perspective space).
Distinction between "Model" and "View" (object and camera) matrices is up to you. glRotate/glTranslate functions just multiply the currently selected matrix by the given one (without even distinguishing between ModelView and Projection).
Those functions multiply (transform) the current matrix set by glMatrixMode() so it depends on the matrix you're working on. OpenGL has 4 different types of matrices; GL_MODELVIEW, GL_PROJECTION, GL_TEXTURE, and GL_COLOR, any one of those functions can change any of those matrices. So, basically, you don't transform objects you just manipulate different matrices to "fake" that effect.
Note that glulookat() is just a convenient function equivalent to a translation followed by some rotations, there's nothing special about it.
All transformations are transformations on objects. Even gluLookAt is just a transformation to transform the objects as if the camera was where you tell it to be. Technically they are transformations on the vertices, but that's just semantics.
That's true, glTranslate, glRotate change the object coordinates before rendering and gluLookAt changes the camera coordinate.