I'm working on a scenario that involves some cone meshes that are to be used as spot lights in a deferred renderer. I need to scale, rotate and translate these cone meshes so that they point in the correct direction. According to one of my lecturers I can rotate the cones to align with a direction vector and move them to the correct position by multiplying its model matrix with the matrix returned by this,
glm::inverse(glm::lookAt(spot_light_direction, spot_light_position, up));
however this doesn't seem to work, doing this causes all of the cones to be placed on the world origin. If I then translate the cones manually using another matrix it seems that the cones aren't even facing the right direction.
Is there a better way to rotate objects so that they face a specific direction?
Here is my current code that gets executed for each cone,
//Move the cone to the correct place
glm::mat4 model = glm::mat4(1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
spot_light_position.x, spot_light_position.y, spot_light_position.z, 1);
// Calculate rotation matrix
model *= glm::inverse(glm::lookAt(spot_light_direction, spot_light_position, up));
float missing_angle = 180 - (spot_light_angle / 2 + 90);
float scale = (spot_light_range * sin(missing_angle)) / sin(spot_light_angle / 2);
// Scale the cone to the correct dimensions
model *= glm::mat4(scale, 0, 0, 0,
0, scale, 0, 0,
0, 0, spot_light_range, 0,
0, 0, 0, 1);
// The origin of the cones is at the flat end, offset their position so that they rotate around the point.
model *= glm::mat4(1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, -1, 1);
I've noted this in the comments but I'll mention again that the cones origin is at center of the flat end of the cone, I don't know if this makes a difference or not, I just thought I'd bring it up.
Your order of the matrices seems correct, but the lookAt function expects:
glm::mat4 lookAt ( glm::vec3 eye, glm::vec3 center, glm::vec3 up )
Here eye is the location of the camera, center is the location of the object you are looking at (in your case if you dont have that location, you can use
spot_light_direction + spot_light_position ).
so just change
glm::lookAt(spot_light_direction, spot_light_position, up)
to
glm::lookAt(spot_light_position, spot_light_direction + spot_light_position, up)
Related
I'm trying to render a scene with OpenGL 2.1 but the borders on overlapping shapes are weird. I tested some OpenGL initialisations but without any change. I reduce my issue to a simple test application with 2 sphere with the same result.
I tried several things about Gl_DEPTH_TEST, enable/disable smoothing without success.
Here is my result with 2 gluSphere :
We can see some sort of aliasing when a line will be enough to separate blue and red faces...
I use SharpGL but I think that it's not significant (as I use it only as a an OpenGL wrapper). Here my simplest code to render the same thing (You can copy it in a Form to test it) :
OpenGL gl;
IntPtr hdc;
int cpt;
private void Init()
{
cpt = 0;
hdc = this.Handle;
gl = new OpenGL();
gl.Create(SharpGL.Version.OpenGLVersion.OpenGL2_1, RenderContextType.NativeWindow, 500, 500, 32, hdc);
gl.Enable(OpenGL.GL_DEPTH_TEST);
gl.DepthFunc(OpenGL.GL_LEQUAL);
gl.ClearColor(1.0F, 1.0F, 1.0F, 0);
gl.ClearDepth(1);
gl.MatrixMode(OpenGL.GL_PROJECTION);
gl.Perspective(30, 1, 0.1F, 1.0E+7F);
gl.MatrixMode(OpenGL.GL_MODELVIEW);
gl.LookAt(0, 3000, 0, 0, 0, 0, 0, 0, 1);
}
private void Render(int angle)
{
gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT | OpenGL.GL_STENCIL_BUFFER_BIT);
RenderSphere(gl, 0, 0, 0, 0, 300, Color.Red);
RenderSphere(gl, 0, 0, 100, angle, 300, Color.Blue);
gl.Blit(hdc);
}
private void RenderSphere(OpenGL gl, int x, int y, int z, int angle, int radius, Color col)
{
IntPtr obj = gl.NewQuadric();
gl.PushMatrix();
gl.Translate(x, y, z);
gl.Rotate(angle, 0, 0);
gl.Color(new float[] { col.R / 255f, col.G / 255f, col.B / 255f, col.A / 255f });
gl.QuadricDrawStyle(obj, OpenGL.GLU_FILL);
gl.Sphere(obj, radius, 20, 10);
gl.Color(new float[] { 0, 0, 0, 1 });
gl.QuadricDrawStyle(obj, OpenGL.GLU_SILHOUETTE);
gl.Sphere(obj, radius, 20, 10);
gl.DeleteQuadric(obj);
gl.PopMatrix();
}
Thanks in advance for your advices !
EDIT :
I tested that without success :
gl.Enable(OpenGL.GL_LINE_SMOOTH);
gl.Enable(OpenGL.GL_POLYGON_SMOOTH);
gl.ShadeModel(OpenGL.GL_SMOOTH);
gl.Hint(OpenGL.GL_LINE_SMOOTH_HINT, OpenGL.GL_NICEST);
gl.Hint(OpenGL.GL_POLYGON_SMOOTH_HINT, OpenGL.GL_NICEST);
gl.Hint(OpenGL.GL_PERSPECTIVE_CORRECTION_HINT, OpenGL.GL_NICEST);
EDIT2 : With more faces, image with and without lines
It is ... different... but not pleasing.
The issue has 2 reasons.
The first one indeed is a Z-fighting issue, which is cause by the monstrous distance between the near and far plane
gl.Perspective(30, 1, 0.1F, 1.0E+7F);
and the fact that at perspective projection, the depth is not linear. See also How to render depth linearly ....
This can be improved by putting the near plane as close as possible to the geometry. Since the distance to the object is 3000.0 and the radius of the sphere is 300, the near plane has to be less than 2700.0:
e.g.
gl.Perspective(30, 1, 2690.0F, 5000.0F);
The second issue is caused by the fact, that the sphere consist of triangle primitives. As you suggested in your answer, you can improve that by increasing the number of primitives.
I will provide an alternative solution, by using a clip plane. Clip the red sphere at the bottom and the blue sphere at the top. Exactly in the plane where the spheres are intersecting, so that a cap is cut off from each sphere.
A clip plane can be set by glClipPlane and to be enabled by glEnable.
The parameters to the clipping plane are interpreted as a Plane Equation.
The first 3 components of the plane equation are the normal vector to the clipping plane. The 4th component is the distance to the origin.
So the clip plane equation for the red sphere has to be {0, 0, -1, 50} and for the blue sphere {0, 0, 1, -50}.
Note, when glClipPlane is called, then the equation is transformed by the inverse of the modelview matrix. So the clip plane has to be set before the model transformations like rotation, translation and scale.
e.g.
private void Render(int angle)
{
gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT | OpenGL.GL_STENCIL_BUFFER_BIT);
double[] plane1 = new double[] {0, 0, -1, 50};
RenderSphere(gl, 0, 0, 0, 0, 300, Color.Red, plane1);
double[] plane2 = new double[] {0, 0, 1, -50};
RenderSphere(gl, 0, 0, 100, angle, 300, Color.Blue, plane2);
gl.Blit(hdc);
}
private void RenderSphere(
OpenGL gl, int x, int y, int z, int angle, int radius,
Color col, double[] plane)
{
IntPtr obj = gl.NewQuadric();
gl.ClipPlane(OpenGL.GL_CLIP_PLANE0, plane);
gl.Enable(OpenGL.GL_CLIP_PLANE0);
gl.PushMatrix();
gl.Translate(x, y, z);
gl.Rotate(angle, 0, 0);
gl.Color(new float[] { col.R / 255f, col.G / 255f, col.B / 255f, col.A / 255f });
gl.QuadricDrawStyle(obj, OpenGL.GLU_FILL);
gl.Sphere(obj, radius, 20, 10);
gl.Color(new float[] { 0, 0, 0, 1 });
gl.QuadricDrawStyle(obj, OpenGL.GLU_SILHOUETTE);
gl.Sphere(obj, radius, 20, 10);
gl.DeleteQuadric(obj);
gl.PopMatrix();
gl.Disable(OpenGL.GL_CLIP_PLANE0);
}
Solution 1 (not a good one): Applying gl.Scale(0.0001, 0.0001, 0.0001); to the ModelView matrix
Solution 2 : The near plane has to be as far as possible to avoid compressing z value in a small range. In this case, use 10 instead of 0.1 is enough. The best is to compute an adapted value depending on objects distance (in this case the nearest object is at 2700)
I think we can focus on z is stored non-linearly in the #PikanshuKumar link and the implicit consequencies.
Result :
Only the faces are cutted by a line: there is a straight line as separator at the equator.
Those lines disappear as expected when we increase the number of faces.
You're killing depth buffer precision with the way you setup your projection matrix
gl.MatrixMode(OpenGL.GL_PROJECTION);
gl.Perspective(30, 1, 0.1F, 1.0E+7F);
Essentially this compresses almost all of the depth buffer precision into the range 0.1 to 0.2 or so (I didn't do the math, just eyeballing it here).
In general you should choose the distance for the near clip plane to be as far away as possible, still keeping all the objects in your scene. The distance of the far plane doesn't matter that much (in fact, with the right matrix magic you can place it at infinity), but in general it's also a good idea to keep it as close as possible.
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 have a camera, which is defined through an up vector, a position and a reference point (camera looks at this point). Furthermore I can calculate the view direction, of course.
Now I tried to reflect this camera in a plane (e.g. z = 0). My first attempt was to reflect every single vector in the plane with the belonging reflection matrix and looked like this:
mat4 mReflection = mat4(1, 0, 0, 0,
0, 1, 0, 0,
0, 0, -1, 0,
0, 0, 0, 1);
up = mReflection * up;
position = mReflection * position;
lookAt = mReflection * lookAt;
But this didn't work very well and I don't know why. What is wrong with this method?
I draw cube and want to rotate it using finger movement under the screen. I use this code
Matrix.rotateM(mMVPMatrix, 0, yAngle, 0, 1, 0);
Matrix.rotateM(mMVPMatrix, 0, xAngle, 1, 0, 0);
and I expect the yAngle will rotate cube on y axis of the screen and it's ok
and the xAngle will rotate cube on x axis of the screen, BUT it rotated in the coordinate system of the cube not of the coordinate system of screen.
I can't post images because i have only 1 reputation point :( so I upload images and the links :
NOW
Rotation as it is now
And I want
rotation as I want
You are using Intrinsic Euler angles, which by default rotate the points around local axis. In order to rotate around the global axis you just need to convert them to extrinsic angles. which is as simple as reversing the order so it becomes:
Matrix.rotateM(mMVPMatrix, 0, xAngle, 1, 0, 0);
Matrix.rotateM(mMVPMatrix, 0, yAngle, 0, 1, 0);
You also need to make sure that you post-multiply row vectors, instead of pre-multiplying ccolumn vectors.
I need to get rotation matrix from direction vector (vForward) I also have vRight and vUp vectors. All those vectors are unit vectors.
I just need to get rotation matrix.
To get rotation matrix for rotation in only one plane (xy) parallel to ground, I do this:
XMMATRIX xmResult;
Vec3f vFwd = pPlayer->VForward;
vFwd.z = 0;
vFwd.Normalize();
xmResult = XMMatrixSet( vFwd.y, -vFwd.x, 0, 0,
vFwd.x, vFwd.y, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
Above code only get rotation matrix to rotate around Z axis:
I would like to get the code to rotate around all axis.
This is coordinate system I'm forced to use. I know it is strange:
This is how I'm using my matrix later in code:
XMStoreFloat3((XMFLOAT3*)&vStart, XMVector3Transform(XMLoadFloat3((XMFLOAT3*)&vStart), xmTransformation));
XMStoreFloat3((XMFLOAT3*)&vEnd, XMVector3Transform(XMLoadFloat3((XMFLOAT3*)&vEnd), xmTransformation));
Depending on how you use your matrices, Right, Up and Forward should correspond to the rows or columns of your matrix.
xmResult = XMMatrixSet( vRight.x, vRight.y, vRight.z, 0, vFwd.x, vFwd.y, vFwd.z, 0, vUp.x, vUp.y, vUp.z, 0, 0, 0, 0, 1);