my question is why I got three different matrices, and what is the difference, which is the correct one for converting coordinates from clip to eye space.
Details:
I am trying to get projection matrix of OpenGL to implement a virtual scanner, I use this matrix to convert coordinates from clip to eye space.
I found three ways to get it:
glGetFloatv (GL_PROJECTION_MATRIX, projection_tmp);
glm::mat4 Proj = glm::perspective(
fov,
aspect,
zNear, zFar);
compute it manually through the method given by this page: gluperspective source code
Parameters passed to these three method follows:
const double zNear = 1;
const double zFar = 10001;//for computational convenience
const double fov = _model.getCamera().getFieldOfViewAngle();//60
//w(523)h(489)
const double aspect = static_cast<double>(w) / static_cast<double>(h);
Then I get three different matrices:
glGetFloatv:
1.61945 0 0 0
0 1.73205 0 0
0 0 -1.0002 -1
0 0 -2.0002 0
GLM:
-0.145971 0 0 0
0 -0.15612 0 0
0 0 -1.0002 -1
0 0 -2.0002 0
Manual:
0.504724 0 0 0
0 0.57735 0 0
0 0 -1.0002 1
0 0 -2.0002 0
Thanks!
The glGetFloatv version is the only correct one among the three.
The GLM version expects the angle to be in radians, so you should correct the fovy by a factor of tau/360.
The third version is not exactly the one from the linked answer. The code in the linked answer writes negative one to m[11] whereas you posted a positive one. Let's assume that it's a typo and look at the other numbers. Here that code is simply wrong: it multiplies by tan(fov/2) instead of dividing by it. This is why it gets 0.57735 = 1/1.73205.
I don't know why people have trouble implementing it. Just take the formula from the gluPerspective specification and write it in code:
void gluPerspectivInRadians(double m[16],
double fovy, double aspect, double zNear, double zFar)
{
double f = 1/tan(fovy/2);
m[0] = f/aspect; m[1] = 0; m[2] = 0; m[3] = 0;
m[4] = 0; m[5] = f; m[6] = 0; m[7] = 0;
m[8] = 0; m[9] = 0; m[10] = (zFar+zNear)/(zNear-zFar); m[11] = -1;
m[12] = 0; m[13] = 0; m[14] = 2*zFar*zNear/(zNear-zFar); m[15] = 0;
}
Related
I try to construct my own View Matrix in OpenGL.
I'm following this link
https://www.opengl.org/sdk/docs/man2/xhtml/gluLookAt.xml
From the OpenGL doc, I have following.
eye position = eye(xe, ye, ze)
center position = cen(0, 0, 0)
up = up(xu, yu, zu). (e.g. up = (0, 1, 0))
forward vector
f' = cen - eye = (0, 0, 0) - (xe, ye, ze) = (-xe, -ye, -ze)
side vector
s' = f' x up
I don't understand why f' x up, why not up x f'
u' = s' x f'
I do't understand why u' = s' x f', why not u' = f' x s'
we normalize s', u', f'
s = norm(s'), u = norm(u'), f=norm(f')
We construct the rotation matrix with row-major(what we learn in algebra class)
R =
s_x u_x f_x 0
s_y u_y f_y 0
s_z u_z f_z 0
0 0 0 1
translation matrix:
T =
1 0 0 x
0 1 0 y
0 0 1 z
0 0 0 1
we know
M = T*R
View Matrix V = invert(M)
V = invert(T*R) = invert(R)invert(T)
V = transpose(R)invert(T)
transpose(R) =
s_x s_y s_z 0
u_x u_y u_z 0
f_x f_y f_z 0
0 0 0 1
invert(T) =
1 0 0 -x
0 1 0 -y
0 0 1 -z
0 0 0 1
so
View Matrix V = transpose(R)invert(T)
But from the OpenGL doc., f change to -f
The rotation changes to following
R =
s_x u_x -f_x 0
s_y u_y -f_y 0
s_z u_z -f_z 0
0 0 0 1
I Don't understand why we need to change the forward vector to negative.
The cross product order just follows from its definition. It is just like it is. You are setting up a right-handed coordinate system. So if you align the thumb of your right hand with the first factor and the index finger with the second factor, then the middle finger will point in the direction of the cross product (perpendicular to both). There is really not much more to tell about this.
And since you are setting up a right-handed coordinate system, the forward direction of the camera must be mapped to the negative z-direction. That's why the third column of the rotation matrix is inverted. If you don't do this, you end up with a left-handed coordinate system, where the camera looks in the direction of the positive z-axis.
I'm trying to rotate an image by small degrees like 1 degree or below 1 degree,
Consider this is my Source Image and I'm trying to rotate it by 1 degree in MATLAB and OpenCV (C++) :
0 0 0 0
0 255 255 0
0 255 255 0
0 0 0 0
When I rotate it in MATLAB by 1 degree this is the result:
0 0 2.223835069639144 0
2.223835069639553 252.7942370468069 2.527942370468065 0
0 252.7942370468065 252.794237046807 2.223835069639648
0 2.223835069639184 0 0
This is the code in MATLAB:
sourceImage = [0 0 0 0; 0 255.0 255.0 0; 0 255.0 255.0 0; 0 0 0 0];
rotationDegree = 1.0;
shearInX_Y1 = (cosd(rotationDegree)-1)/sind(rotationDegree);
shearInX_Y2 = sind(rotationDegree);
transformationMatrix = [(1 + shearInX_Y1*shearInX_Y2), (2*shearInX_Y1 + ...
((shearInX_Y1)^2)*shearInX_Y2), 0; (shearInX_Y2), (1 + shearInX_Y1*shearInX_Y2), 0; 0, 0, 1];
tform = affine2d(transformationMatrix);
imref = imref2d(size(sourceImage));
imref.XWorldLimits = imref.XWorldLimits-mean(imref.XWorldLimits);
imref.YWorldLimits = imref.YWorldLimits-mean(imref.YWorldLimits);
transformedImg2 = imwarp(sourceImage , imref, tform, 'OutputView', imref, 'Interp', 'bilinear');
transformedImg2
The transformationMatrix in Matlab (which is our rotation matrix) is:
transformationMatrix =
9.998476951563913e-01 -1.745240643728003e-02 0
1.745240643728351e-02 9.998476951563913e-01 0
0 0 1.000000000000000e+00
And the content of "tform.T" is:
9.998476951563913e-01 -1.745240643728003e-02 0
1.745240643728351e-02 9.998476951563913e-01 0
0 0 1
And the content of "rotationMatrix" in OpenCV is:
9.998476951563913e-01 1.745240643728351e-02 -0.008650050796837391
-1.745240643728351e-02 9.998476951563913e-01 0.008802355640446121
But when I do the rotation by 1 degree in OpenCV (C++) This is the result:
(which is same as source image!) which means openCV has a problem with small rotations!
0 0 0 0
0 255 255 0
0 255 255 0
0 0 0 0
This is the code I use for rotation in OpenCV (C++): (Rotation is done with respect to image center)
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
using namespace cv;
using namespace std;
# define INTERPOLATION_METHOD INTER_CUBIC
// INTER_AREA
// INTER_LINEAR
// INTER_NEAREST
Mat rotateImage(Mat sourceImage, double rotationDegree);
int main(){
Mat sourceImage = Mat::zeros(4, 4, CV_64F);
sourceImage.at<double>(1, 1) = 255.0;
sourceImage.at<double>(1, 2) = 255.0;
sourceImage.at<double>(2, 1) = 255.0;
sourceImage.at<double>(2, 2) = 255.0;
double rotationDegree = 1.0;
Mat rotatedImage = rotateImage(sourceImage, rotationDegree);
cout << "sourceImage: \n" << sourceImage << endl << endl;
cout << "rotatedImage : \n" << rotatedImage << endl << endl;
return 0;
}
Mat rotateImage(Mat sourceImage, double rotationDegree){
double rowOfImgCenter;
double colOfImgCenter;
rowOfImgCenter = sourceImage.rows / 2.0 - 0.5;
colOfImgCenter = sourceImage.cols / 2.0 - 0.5;
Point2d imageCenter(colOfImgCenter, rowOfImgCenter);
Mat rotationMatrix;
rotationMatrix = getRotationMatrix2D(imageCenter, rotationDegree, 1.0);
Mat rotatedImage;
warpAffine(sourceImage, rotatedImage, rotationMatrix, sourceImage.size(), INTERPOLATION_METHOD);
return rotatedImage;
}
Any idea would be appreciated.
I have recently been trying to render a 3D sphere in OpenGL using triangles. I have been testing and modifying code from various websites and have finally found a winning combination. The only problem is that there are visible gaps in the sphere. Any thoughts on what would be causing this?
Code to render sphere
float Slices = 30;
float Stacks = 60;
float Radius = 20.0;
for (int i = 0; i <= Stacks; ++i){
float V = i / (float) Stacks;
float phi = V * glm::pi <float> ();
for (int j = 0; j <= Slices; ++j){
float U = j / (float) Slices;
float theta = U * (glm::pi <float> () * 4);
float x = cosf (theta) * sinf (phi);
float y = cosf (phi);
float z = sinf (theta) * sinf (phi);
x *= Radius;
y *= Radius;
z *= Radius;
Vertex *v = new Vertex {{x,y,z}, //Position
{255,0,0}}; //Color
screenToBuffer(v, 1);
delete []v;
}
}
Problem
Try and set it to GL_TRIANGLE_STRIP​
What might be the problem is that it considers each group of three vertices to be only one triangle.
Like so
Indices: 0 1 2 3 4 5 ...
Triangles: {0 1 2} {3 4 5}
The GL_TRIAGLE_STRIP will do this.
Indices: 0 1 2 3 4 5 ...
Triangles: {0 1 2}
{1 2 3} drawing order is (2 1 3) to maintain proper winding
{2 3 4}
{3 4 5}
See this answer for a proper way to do it.
https://stackoverflow.com/a/7958376/1943599
I am implementing perspective from scratch for an academic project. I am using "Computer Graphics: principles and practices", by Foley, van Dam, Feiner and Hughes (second edition in C).
I just followed the book by implementing all the matrices transformations needed to traslate, rotate, shear, scale, project, transform from perspective to parallel canonical view volumes and for clipping. The book apparently uses a right-handed coordinate system. However, I ended up with primitives appearing in a left-handed coordinate system and I cannot explain why.
Here are the matrices that I used:
Translation:
1, 0, 0, dx
0, 1, 0, dy
0, 0, 1, dz
0, 0, 0, 1
Rotation (to align a coordinate system (rx, ry, rz) to XYZ):
rx1, rx2, rx3, 0
ry1, ry2, ry3, 0
rz1, rz2, rz3, 0
0 , 0 , 0 , 1
Scale:
sx, 0 , 0 , 0
0 , sy, 0 , 0
0 , 0 , sz, 0
0 , 0 , 0 , 1
Shear XY:
1, 0, shx, 0
0, 1, shy, 0
0, 0, 1 , 0
0, 0, 0 , 1
Projecting onto a plane at z = d, with PRP at origin, looking in the positive z direction:
1, 0, 0 , 0
0, 1, 0 , 0
0, 0, 1 , 0
0, 0, 1/d, 0
Then given VRP, VPN, PRP, VUP, f and b (and the direction of projection dop), reduce the space to the canonical viewing volume for perspective using P:
rz = VPN / |VPN|
rx = (VUP x rz) / |VUP x rz|
ry = rz x rx
P = ScaleUniform(-1 / (vrp1Z + b)) *
Scale(-2 * vrp1Z / deltaU, -2 * vrp1Z / deltaV, 1) *
Shear(-dopX / dopZ, -dopY / dopZ) *
T(PRP) *
R(rx, ry, rz) *
T(-VRP)
Where vrp1 is ShearXY * T(-PRP) * (0, 0, 0, 1), deltaU and deltaV the width and height of the viewing window. dop is computed as CW - PRP, where CW is the center of the viewing window.
Then Projection(d) * P gives me the projection matrix.
I projected simple lines representing the unit vectors on x, y and z, but the representation that I obtained drawn on the screen was clearly a left handed coordinate system... Now I need to work in a right handed coordinate system, so is there a way to know where I did wrong?
Here is the code I used:
As you can see, the Z component of the scale matrix is of opposite sign, since clipping wasn't working properly because something was right-handed and something left-handed, but I couldn't discern what exactly, so I swapped the sign of the scale because it wasn't needed in a left-hand system.
Vector rz = vpn.toUnitVector();
Vector rx = vup.cross(rz).toUnitVector();
Vector ry = rz.cross(rx).toUnitVector();
Vector cw = viewWindow.getCenter();
Vector dop = cw - prp;
Matrix t1 = Matrix::traslation(-vrp[X], -vrp[Y], -vrp[Z]);
Matrix r = Matrix::rotation(rx, ry, rz);
Matrix t2 = Matrix::traslation(-prp[X], -prp[Y], -prp[Z]);
Matrix partial = t2 * r * t1;
Matrix shear = Matrix::shearXY(-dop[X] / dop[Z], -dop[Y] / dop[Z]);
Matrix inverseShear = Matrix::shearXY(dop[X] / dop[Z], dop[Y] / dop[Z]);
Vector vrp1 = shear * t2 * Vector(0, 0, 0, 1);
Matrix scale = Matrix::scale(
2 * vrp1[Z] / ((viewWindow.xMax - viewWindow.xMin) * (vrp1[Z] + b)),
2 * vrp1[Z] / ((viewWindow.yMax - viewWindow.yMin) * (vrp1[Z] + b)),
1 / (vrp1[Z] + b)); // HERE <--- WAS NEGATIVE
Matrix inverseScale = Matrix::scale(
((viewWindow.xMax - viewWindow.xMin) * (vrp1[Z] + b)) / (2 * vrp1[Z]),
((viewWindow.yMax - viewWindow.yMin) * (vrp1[Z] + b)) / (2 * vrp1[Z]),
(vrp1[Z] + b));
float zMin = -(vrp1[Z] + f) / (vrp1[Z] + b);
Matrix parallel = Perspective::toParallelCvv(zMin);
Matrix inverseParallel = Perspective::inverseToParallelCvv(zMin);
Matrix perspective = Perspective::copAtOrigin(-vrp1[Z]);
projection = perspective * shear * partial;
canonicalView = parallel * scale * shear * partial;
canonicalToProjection = perspective * inverseScale * inverseParallel;
How can I swap two colors using a color matrix? For instance swapping red and blue is easy. The matrix would look like:
0 0 1 0 0
0 1 0 0 0
1 0 0 0 0
0 0 0 1 0
0 0 0 0 1
So how can I swap any two colors in general? For example, there is Color1 with R1, G1, B1 and Color2 with R2, G2, B2.
EDIT: By swap I mean Color1 will translate into color2 and color2 will translate into color1. Looks like I need a reflection transformation. How to calculate it?
GIMP reference removed. Sorry for confusion.
This appears to be the section of the color-exchange.c file in the GIMP source that cycles through all the pixels and if a pixel meets the chosen criteria(which can be a range of colors), swaps it with the chosen color:
for (y = y1; y < y2; y++)
{
gimp_pixel_rgn_get_row (&srcPR, src_row, x1, y, width);
for (x = 0; x < width; x++)
{
guchar pixel_red, pixel_green, pixel_blue;
guchar new_red, new_green, new_blue;
guint idx;
/* get current pixel-values */
pixel_red = src_row[x * bpp];
pixel_green = src_row[x * bpp + 1];
pixel_blue = src_row[x * bpp + 2];
idx = x * bpp;
/* want this pixel? */
if (pixel_red >= min_red &&
pixel_red <= max_red &&
pixel_green >= min_green &&
pixel_green <= max_green &&
pixel_blue >= min_blue &&
pixel_blue <= max_blue)
{
guchar red_delta, green_delta, blue_delta;
red_delta = pixel_red > from_red ?
pixel_red - from_red : from_red - pixel_red;
green_delta = pixel_green > from_green ?
pixel_green - from_green : from_green - pixel_green;
blue_delta = pixel_blue > from_blue ?
pixel_blue - from_blue : from_blue - pixel_blue;
new_red = CLAMP (to_red + red_delta, 0, 255);
new_green = CLAMP (to_green + green_delta, 0, 255);
new_blue = CLAMP (to_blue + blue_delta, 0, 255);
}
else
{
new_red = pixel_red;
new_green = pixel_green;
new_blue = pixel_blue;
}
/* fill buffer */
dest_row[idx + 0] = new_red;
dest_row[idx + 1] = new_green;
dest_row[idx + 2] = new_blue;
/* copy alpha-channel */
if (has_alpha)
dest_row[idx + 3] = src_row[x * bpp + 3];
}
/* store the dest */
gimp_pixel_rgn_set_row (&destPR, dest_row, x1, y, width);
/* and tell the user what we're doing */
if (!preview && (y % 10) == 0)
gimp_progress_update ((gdouble) y / (gdouble) height);
}
EDIT/ADDITION
Another way you could have transformed red to blue would be with this matrix:
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
-1 0 1 0 1
The only values that really matter are the bottom ones in this matrix.
This would be the same as saying subtract 255 from red, keep green the same, and then add 255 to blue. You could cut the alpha in half like so as well like so:
-1 0 1 -0.5 1
So (just like the gimp source) you just need to find the difference between your current color and your target color, for each channel, and then apply the difference. Instead of channel values from 0 to 255 you would use values from 0 to 1.
You could have changed it from red to green like so:
-1 1 0 0 1
See here for some good info:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms533875%28v=vs.85%29.aspx
Good luck.
I solved it by creating a reflection matrix via D3DXMatrixReflect using a plane that's perpendicular to the vector AB and intersects the midpoint of the AB.
D3DXVECTOR3 AB( colorA.r-colorB.r, colorA.g-colorB.g, colorA.b-colorB.b );
D3DXPLANE plane( AB.x, AB.y, AB.z, -AB.x*midpoint.x-AB.y*midpoint.y-AB.z*midpoint.z );
D3DXMatrixReflect