I am trying to create an Augmented Reality application for vehicle tracking. In the application I want to place a quad model to track the vehicle. For this purpose I draw a quad at a particular screen coordinate every time the texture is updated. The problem is, I am not able to place the quad model at the exact position on the texture.
Below image shows what exactly I want
As in the above image a green quad needs to be placed exactly at the behind of the vehicle.
I have done it in the following ways,
Viewport size ( 0, 0, 1280, 720 )
Render a Texture
Created a Texture of size 1280x720 and rendered with default modelViewProjection (Identical matrix)
GLfloat vVertices[] = { -1.f, 1.f, 0.0f, // Position 0
0.0f, 0.0f, // TexCoord 0
-1.f, -1.f, 0.0f, // Position 1
0.0f, 1.0f, // TexCoord 1
1.f, -1.f, 0.0f, // Position 2
1.0f, 1.0f, // TexCoord 2
1.f, 1.f, 0.0f, // Position 3
1.0f, 0.0f // TexCoord 3
};
GLushort indices[] = { 0, 1, 2, 0, 2, 3 };```
Drawing a Quad in 3D world
I have used the following model view projection
m_unWidth= 1280
m_unHeight = 720
float aspect = (float) m_unWidth / (float) m_unHeight;
Mperspective = glm::mat4( 1.0f );
Mperspective = glm::perspective( glm::radians(45.0f), aspect, 0.1f, 100.0f );
Mview = glm::mat4(1.0f);
Mview = glm::translate( Mview, glm::vec3(0.0f, 0.0f, -2.0f));
glm::mat4 Mmodel = glm::mat4(1.0f);
Mmodel = glm::translate( Mmodel, glm::vec3( POSITION.X, POSITION.Y, 0.0 ));
Mmodel = glm::rotate( Mmodel, glm::radians(-80.0f), glm::vec3( 1.0f, 0.0f, 0.0f ));
I have used coordinate conversion function to map into [-1,1].
NDCPoint ACCOverlay::ConvertToNDC( unsigned int unX_i, unsigned int unY_i ) {
const int width = m_unWidth;
const int height = m_unHeight;
float x = float(unX_i) * 2 / float(width) - 1;
float y = 1 - float(unY_i) * 2 / float(height);
NDCPoint point;
point.x = x;
point.y = y;
point.z = 1.0f;
return point;
}
Could you please explain how to map quad model at the correct place of texture in perspective projection.
Related
i am trying to get the mouse cursor in world space pos from window space(-1,1 window width and height) using the viewprojectionmatrix.
This is how i calculate my projection matrix:
static mat4 Perspective4x4(float FOV, float AspectRatio, float FarC, float NearC)
{
// Positive x is right
// Positive y is up
// Positive z is forward into the screen
float Cotangent = 1.0f / tanf((FOV)*0.5f);
float Depth = NearC - FarC;
float A = (-FarC - NearC) / Depth;
float B = 2.0f * FarC * NearC / Depth;
return
{
Cotangent/AspectRatio, 0.0f, 0.0f, 0.0f,
0.0f, Cotangent, 0.0f, 0.0f,
0.0f, 0.0f, -A, -B,
0.0f, 0.0f, 1.0f, 0.0f,
};
}
...
float WidthOverHeight = ...;
mat4 ProjectionMatrix = Perspective4x4(DegreesToRadians(90.0f), WidthOverHeight, 50.0f, 0.1f);
This is how i calculate my view matrix:
static mat4 Translate4x4(vec3 V)
{
return
{
1.0f, 0.0f, 0.0f, V.X,
0.0f, 1.0f, 0.0f, V.Y,
0.0f, 0.0f, 1.0f, V.Z,
0.0f, 0.0f, 0.0f, 1.0f
};
}
...
mat4 ViewMatrix = Translate4x4(-CameraPosition);
This is how i calculate the cursor position in worldspace:
mat4 ProjectionMatrix = ...;
mat4 ViewMatrix = ...;
vec2 CursorP = GetBilateralCursorPos(Input); // Values between -1 and 1
v4_f32 WorldSpacePNear = Inverse(ProjectionMatrix) * V4F32(CursorP, -1.0f, 1.0f);
WorldSpacePNear /= WorldSpacePNear.W;
WorldSpacePNear = Inverse(ViewMatrix) * WorldSpacePNear;
v4_f32 WorldSpacePFar = Inverse(ProjectionMatrix) * V4F32(CursorP, 1.0f, 1.0f);
WorldSpacePFar /= WorldSpacePFar.W;
WorldSpacePFar = Inverse(ViewMatrix) * WorldSpacePFar;
WorldSpacePFar.Z *= -1.0f;
WorldSpacePNear.Z *= -1.0f;
EDIT: I also tried dividing by W at the end but it doesnt work properly either.
This is how i send the matrices to OpenGL (legacy):
// I send them transposed because my matrices are row-major
mat4 ProjectionMatrix = Transpose(...);
mat4 ViewMatrix = Transpose(...);
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(ProjectionMatrix.E);
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(ViewMatrix.E);
The resulting positions do not seem to take in account the view projection because a change in the camera position will offset them making them not accurate.
NOTES:
vec4, vec2, mat4 are custom float-based math types. (they are what you would expect)
I know legacy OpenGL is deprecated, in fact im going to switch to modern opengl very soon, i just want to get this working.
i fixed the issue calculating the cursor pos like this:
mat4 ProjectionMatrix = ...;
mat4 ViewMatrix = ...;
// Z Distance from camera pos
float WorldDistanceFromCameraZ = 1.0f;
vec2 CursorP = GetBilateralCursorPos(Input); // Values between -1 and 1
vec4 ProbeZ = V4F32(World->Camera.P - WorldDistanceFromCameraZ*World->Camera.P.Z, 1.0f);
ProbeZ = (ProjectionMatrix*ViewMatrix) * ProbeZ;
vec4 ClipP = V4F32(CursorP.X*ProbeZ.W, CursorP.Y*ProbeZ.W, ProbeZ.Z, ProbeZ.W);
vec4 WorldP = Inverse(ProjectionMatrix*ViewMatrix) * ClipP;
I am trying to implement a particle system in Opengl with Kotlin and Joml. Following the advices of a tutorial I only have to create a projection matrix for the vertex shader and to do perspective division with ortho(). This is my code for the matrix
var projection = Matrix4f()
projection.ortho(0.0f, 1280.0f, 0.0f, 720.0f, -1.0f, 1.0f)
partShader.setMat4("projection", projection, false )
and my vertex shader:
layout (location = 0) in vec4 vertex;
out vec2 TexCoords;
uniform mat4 projection;
uniform vec2 offset; //position vector
void main() {
float scale = 10.0f;
TexCoords = vertex.zw;
gl_Position = projection * vec4((vertex.xy * scale) + offset, 0.0, 1.0);
}
The result is that the quad is positioned in the bottom left corner of the screen, it is a square now and it's size is correct. Before it has been at screen center with offset vector (0,0) and with it's left bottom corner. Moreover it is not possible to move it by the offset vector any longer. I don't really understand why. This is my init and render method
class ParticleGenerator (var shader : ShaderProgram, var texture : Texture2D, var amount : Int) {
var particleList = mutableListOf<Particle>()
var lastUsedParticle = 0
var unusedParticles = 0
var firstUnusedParticle = 0
lateinit var partTex: Texture2D
private var vao = 0
private var vbo = 0
private var ibo = 0
private var indexcount = 0
init {
var particle_quad : FloatArray = floatArrayOf(
0.0f, 1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f, 0.0f
)
var index_quad = intArrayOf(
0, 1, 2,
2, 3, 0
)
indexcount = index_quad.size
this.vao = ARBVertexArrayObject.glGenVertexArrays()
ARBVertexArrayObject.glBindVertexArray(vao)
this.vbo = GL15.glGenBuffers()
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo)
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, particle_quad, GL15.GL_STATIC_DRAW)
this.ibo = GL15.glGenBuffers()
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ibo)
GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, index_quad, GL15.GL_STATIC_DRAW)
glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 4, GL_FLOAT, false, 16, 0)
glBindVertexArray(0)
}
fun render() {
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL11.GL_ONE)
glBindVertexArray(vao)
glDrawArrays(GL_TRIANGLE_STRIP, 0, indexcount)
glDisable(GL_BLEND)
glBindVertexArray(0)
}
Moreover it is not possible to move it by the offset vector any longer. I don't really understand why.
It is certainly possible to move your object by changing offset. You just set it up in a way that offset is now in pixels (relative to an assumed 1280x720 viewport size, which might or might not be the exact pixel size of your viewport). So if without the projection matrix, an offset of 1 meant to move half the viewport width (which is 2 in NDC), and offset of 1 now means just 1/1280 of the viewport width.
I'm trying to understand the OpenGL MVP matrices, and as an exercice I'd like to draw a rectangle filling my window, using the matrices. I thought I would easily find a tutorial for that, but all those I found simply seem to put random values in their MVP matrices setup.
Say my rectangle has these coordinates:
GLfloat vertices[] = {
-1.0f, 1.0f, 0.0f, // Top-left
1.0f, 1.0f, 0.0f, // Top-right
1.0f, -1.0f, 0.0f, // Bottom-right
-1.0f, -1.0f, 0.0f, // Bottom-left
};
Here are my 2 triangles:
GLuint elements[] = {
0, 1, 2,
2, 3, 0
};
If I draw the rectangle with identity MVP matrices, it fills the screen as expected. Now I want to use a frustum. Here are its settings:
float m_fov = 45.0f;
float m_width = 3840;
float m_height = 2160;
float m_zNear = 0.1f;
float m_zFar = 100.0f;
From this I can compute the width / height of my window at z-near & z-far:
float zNearHeight = tan(m_fov) * m_zNear * 2;
float zNearWidth = zNearHeight * m_width / m_height;
float zFarHeight = tan(m_fov) * m_zFar * 2;
float zFarWidth = zFarHeight * m_width / m_height;
Now I can create my view & projection matrices:
glm::mat4 projectionMatrix = glm::perspective(glm::radians(m_fov), m_width / m_height, m_zNear, m_zFar);
glm::mat4 viewMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -m_zNear));
I'd now expect this to make my rectangle to fill the window:
glm::mat4 identity = glm::mat4(1.0f);
glm::mat4 rectangleModelMatrix = glm::scale(identity, glm::vec3(zNearWidth, zNearHeight, 1));
But doing so, my rectangle is way too big. What did I miss?
SOLUTION: as #Rabbid76 pointed out, the problem was the computation of my z-near size, which must be:
float m_zNearHeight = tan(glm::radians(m_fov) / 2.0f) * m_zNear * 2.0f;
float m_zNearWidth = m_zNearHeight * m_width / m_height;
Also, I now need to specify my object coordinates in normalized view space ([-0.5, 0.5]) rather than device space ([-1, 1]). Thus my vertices must now be:
GLfloat vertices[] = {
-0.5f, 0.5f, 0.0f, // Top-left
0.5f, 0.5f, 0.0f, // Top-right
0.5f, -0.5f, 0.0f, // Bottom-right
-0.5f, -0.5f, 0.0f, // Bottom-left
};
The projected height, of an object on a plan which is parallel to the xy plane of the view is
h' = h * tan(m_fov / 2) / -z
where h is the height of the object on the plane, -z is the depth and m_fov is the field of view angle.
In your case m_fov is 45° and -z is -0.1 (-m_zNear), thus tan(m_fov / 2) / z is ~4,142.
Since the height of the quad is 2, the projected height of the quad is ~8,282.
To create a quad which fits exactly in the viewport, use a filed of view angle of 90° and a distance to the object of 1, because tan(90° / 2) / 1 is 1. e.g:
float m_fov = 90.0f;
glm::mat4 projectionMatrix = glm::perspective(glm::radians(m_fov), m_width / m_height, m_zNear, m_zFar);
glm::mat4 viewMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -1.0f));
If tan(m_fov / 2) == -z, then an object with the bottom of -1 and the top of 1 fits into the viewport.
Because of the division by z, the projected size of on object on the viewport decrease linear by the distance to the camera.
I'm making a solar system in OpenGL and I want the planets to be able to orbit other planets as well as rotate around their own centers.
This is the code I'm currently using to make the planets orbit a specific point:
Model = glm::translate(Model, glm::vec3(-orbit_radius_, 0.0f, 0.0f));
Model = glm::rotate(Model, glm::radians(orbit_speed_) / 100.0f, glm::vec3(0.0f, 1.0f, 0.0f));
Model = glm::translate(Model, glm::vec3(orbit_radius_, 0.0f, 0.0f));
How would I combine this with a transformation that spins the object around itself?
I got it to work by just splitting then transformations and then combining them at the end.
rotate_ = glm::translate(rotate_, glm::vec3(-orbit_radius_, 0.0f, 0.0f));
rotate_ = glm::rotate(rotate_, glm::radians(orbit_speed_) / 100.0f, glm::vec3(0.0f, 1.0f, 0.0f));
rotate_ = glm::translate(rotate_, glm::vec3(orbit_radius_, 0.0f, 0.0f));
spin_ = glm::rotate(spin_, glm::radians(spin_speed_) / 100.0f, glm::vec3(0.0f, 1.0f, 0.0f));
final_ = rotate_ * spin_;
If you want to spinn and rotate an object, the I recommend to create an object which has its center at (0, 0, 0)
The self spinning of the object has to be do first. Then translate and rotate the object:
Model = rotate * translate * spinn
e.g.:
rot_angle += glm::radians(orbit_speed_) / 100.0f;
spin_angle += glm::radians(orbit_speed_) / 100.0f;
glm::vec3 tvec = glm::vec3(orbit_radius_, 0.0f, 0.0f);
glm::vec3 axis = glm::vec3(0.0f, 1.0f, 0.0f)
glm::mat4 translate = glm::translate(glm::mat(1.0f), tvec);
glm::mat4 rotate = glm::rotate(glm::mat(1.0f), rot_angle, axis);
glm::mat4 spin = glm::rotate(glm::mat(1.0f), spin_angle , axis);
Model = rotate * translate * spin;
With this solution rot_angle and spin_angle have to be incremented in every frame by a constant step.
If you don't want to increment the angles, then you have to store 2 matrices, instead of the angles. 1 for the rotation and on for the spin:
mat4 rotate(1.0f);
mat4 spin(1.0f);
glm::vec3 tvec = glm::vec3(-orbit_radius_, 0.0f, 0.0f);
glm::vec3 axis = glm::vec3(0.0f, 1.0f, 0.0f)
float rot_angle = glm::radians(orbit_speed_) / 100.0f;
float spin_angle = glm::radians(spin_speed_) / 100.0f;
rotate = glm::translate(rotate, tvec);
rotate = glm::rotate(rotate, rot_angle, axis );
rotate = glm::translate(rotate, -tvec);
spin = glm::rotate(spin, spin_angle, axis);
Model = rotate * spin;
I want to rotate my camera around the scene and an object which is in the center. I've tried doing it this way:
glm::mat4 view;
float radius = 10.0f;
float camX = sin(SDL_GetTicks()/1000.0f) * radius;
float camZ = cos(SDL_GetTicks()/1000.0f) * radius;
view = glm::lookAt(glm::vec3(camX, 0.0f, camZ), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
glUniformMatrix4fv(glGetUniformLocation(shader, "viewingMatrix"), 1, false, &view[0][0]);
but my object loads up further away on the screen and the object rotates around the scene, not the camera.
That's my vertex shader:
void main()
{
FragPos = vec3(modelMatrix * vec4(aPos, 1.0));
Normal = mat3(transpose(inverse(modelMatrix))) * aPos;
TexCoord = aTexture;
vec4 transformedPosition = projectionMatrix * viewingMatrix * vec4(FragPos, 1.0f);
gl_Position = transformedPosition;
}
How do I make it such that the camera is the one rotating around in the scene without the object rotating around?
I'm following this tutorial and I'm trying to work out the what happens in the first animation.
https://learnopengl.com/Getting-started/Camera
modelMatrix
glm::mat4 modelMat(1.0f);
modelMat = glm::translate(modelMat, parentEntity.position);
modelMat = glm::rotate(modelMat, parentEntity.rotation.z, glm::vec3(0.0f, 0.0f, 1.0f));
modelMat = glm::rotate(modelMat, parentEntity.rotation.y, glm::vec3(0.0f, 1.0f, 0.0f));
modelMat = glm::rotate(modelMat, parentEntity.rotation.x, glm::vec3(1.0f, 0.0f, 0.0f));
modelMat = glm::scale(modelMat, parentEntity.scale);
int modelMatrixLoc = glGetUniformLocation(shader, "modelMatrix");
glUniformMatrix4fv(modelMatrixLoc, 1, false, &modelMat[0][0]);
The target of the view (2nd parameter of glm::lookAt) should be the center of the object. The position of the object (and the center of the object) is changed by the model matrix (modelMatrix) in the vertex sahder.
You have to add the world position of the object to the 1st and 2nd parameter of glm::lookAt. The position of the object is the translation of the model matrix.
Further the object is to far away from the camera, because the radius is to large.
To solve your issue, the code has to look somehow like this:
glm::mat4 view;
float radius = 2.0f;
float camX = sin(SDL_GetTicks()/1000.0f) * radius;
float camZ = cos(SDL_GetTicks()/1000.0f) * radius;
view = glm::lookAt(
glm::vec3(camX, 0.0f, camZ) + parentEntity.position,
parentEntity.position,
glm::vec3(0.0f, 1.0f, 0.0f));
glUniformMatrix4fv(
glGetUniformLocation(shader, "viewingMatrix"), 1, false, glm::value_ptr(view));