Sign in to follow this  

Matrix math in Java

This topic is 2567 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey!

I'm trying to do some matrix transformations & projections in Java for a applet that is going to animate some lines in 3D.
Does this seem ok? I would also like to get some tips about how to do the perspective projection.


public class matrix3D{
private double[][] matrix;

public matrix3D(){
matrix = new double[4][4];
matrix = setIdentity();
}

private double[][] setIdentity(){
double[][] temp = {{1.0, 0.0, 0.0, 0.0},
{0.0, 1.0, 0.0, 0.0},
{0.0, 0.0, 1.0, 0.0},
{0.0, 0.0, 0.0, 1.0}};

return temp;
}

public void resetMatrix(){
matrix = setIdentity();
}

public void setXRotation(double x){
double cos = Math.cos(x);
double sin = Math.sin(x);

double[][] temp = {{1.0, 0.0, 0.0, 0.0},
{0.0, cos, -sin, 0.0},
{0.0, sin, cos, 0.0},
{0.0, 0.0, 0.0, 1.0}};

matrix = multiply(matrix, temp);
}

public void setYRotation(double y){
double cos = Math.cos(y);
double sin = Math.sin(y);

double[][] temp = {{cos, 0.0, sin, 0.0},
{0.0, 1.0, 0.0, 0.0},
{-sin, 0.0, cos, 0.0},
{0.0, 0.0, 0.0, 1.0}};

matrix = multiply(matrix, temp);
}

public void setZRotation(double z){
double cos = Math.cos(z);
double sin = Math.sin(z);

double[][] temp = {{cos, -sin, 0.0, 0.0},
{sin, cos, 0.0, 0.0},
{0.0, 0.0, 1.0, 0.0},
{0.0, 0.0, 0.0, 1.0}};

matrix = multiply(matrix, temp);
}

public void setTranslation(double x, double y, double z){
double[][] temp = {{1.0, 0.0, 0.0, x },
{0.0, 1.0, 0.0, y },
{0.0, 0.0, 1.0, z },
{0.0, 0.0, 0.0, 1.0}};

matrix = multiply(matrix, temp);
}

public void setScaling(double x, double y, double z){
double[][] temp = {{x, 0.0, 0.0, 0.0},
{0.0, y, 0.0, 0.0},
{0.0, 0.0, z, 0.0},
{0.0, 0.0, 0.0, 1.0}};

matrix = multiply(matrix, temp);
}

private double[][] multiply(double[][] a, double[][] b){
double[][] result = new double[4][4];
result = setIdentity();

for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
for(int k = 0; k < 4; k++)
result[i][j] += a[i][k] * b[k][j];

return result;
}

public matrix3D multiply(matrix3D a, matrix3D b){
matrix3D result = new matrix3D();

for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
for(int k = 0; k < 4; k++)
result.matrix[i][j] += a.matrix[i][k] * b.matrix[k][j];

return result;
}

public vertex transformVertex(vertex v){
vertex result = new vertex();

result.setX(v.getX() * matrix[0][0] +
v.getY() * matrix[0][1] +
v.getZ() * matrix[0][2] +
v.getW() * matrix[0][3]);

result.setY(v.getX() * matrix[1][0] +
v.getY() * matrix[1][1] +
v.getZ() * matrix[1][2] +
v.getW() * matrix[1][3]);

result.setZ(v.getX() * matrix[2][0] +
v.getY() * matrix[2][1] +
v.getZ() * matrix[2][2] +
v.getW() * matrix[2][3]);

result.setW(v.getX() * matrix[3][0] +
v.getY() * matrix[3][1] +
v.getZ() * matrix[3][2] +
v.getW() * matrix[3][3]);

return result;
}
}

Share this post


Link to post
Share on other sites
Fixed an error in the multiplication and then I made a function that prints the matrix:

No transformations:
1,00 0,00 0,00 0,00
0,00 1,00 0,00 0,00
0,00 0,00 1,00 0,00
0,00 0,00 0,00 1,00

X rotation (0.78 rad):
1,00 0,00 0,00 0,00
0,00 0,71 -0,70 0,00
0,00 0,70 0,71 0,00
0,00 0,00 0,00 1,00

Y rotation (0.78 rad):
0,71 0,00 0,70 0,00
0,00 1,00 0,00 0,00
-0,70 0,00 0,71 0,00
0,00 0,00 0,00 1,00

Z rotation (0.78 rad):
0,71 -0,70 0,00 0,00
0,70 0,71 0,00 0,00
0,00 0,00 1,00 0,00
0,00 0,00 0,00 1,00

Translation (10.0, 0, 0):
1,00 0,00 0,00 10,00
0,00 1,00 0,00 0,00
0,00 0,00 1,00 0,00
0,00 0,00 0,00 1,00

Scaling (2.0, 1.0, 1.0):
2,00 0,00 0,00 0,00
0,00 1,00 0,00 0,00
0,00 0,00 1,00 0,00
0,00 0,00 0,00 1,00

X rotation + translation:
1,00 0,00 0,00 10,00
0,00 0,71 -0,70 0,00
0,00 0,70 0,71 0,00
0,00 0,00 0,00 1,00

Y rotation + translation:
0,71 0,00 0,70 7,11
0,00 1,00 0,00 0,00
-0,70 0,00 0,71 -7,03
0,00 0,00 0,00 1,00

Z rotation + translation:
0,71 -0,70 0,00 7,11
0,70 0,71 0,00 7,03
0,00 0,00 1,00 0,00
0,00 0,00 0,00 1,00

X + Y + Z rotation:
0,51 -0,50 0,70 0,00
0,85 0,16 -0,50 0,00
0,14 0,85 0,51 0,00
0,00 0,00 0,00 1,00

X + Y + Z rotation + translation:
0,51 -0,50 0,70 5,05
0,85 0,16 -0,50 8,52
0,14 0,85 0,51 1,39
0,00 0,00 0,00 1,00

X + Y + Z rotation + translation + scaling:
1,01 -0,50 0,70 5,05
1,70 0,16 -0,50 8,52
0,28 0,85 0,51 1,39
0,00 0,00 0,00 1,00


It seems more resonable now!

Share this post


Link to post
Share on other sites
I'm totally stuck on the perspective projection!
Take this for example:
The "canonical space mapping for perspective projections" at the bottom of the page.

http://www.cs.unc.edu/~mcmillan/comp136/Lecture16/projection.html

What is right, left bottom & top?

Share this post


Link to post
Share on other sites
I did this a little while ago but don't have the source handy. I'll try to explain by my memory's fuzzy. Here goes.

Observe this here:

http://www.codeguru.com/dbfiles/get_image.php?id=10123&lbl=3DPROJ22_GIF&ds=20061023

You're defining that square floating in front of the viewer.

You can conjure up the figures in question from the aspect ratio.

For example, a 640x480 screen has an aspect ratio of 4:3.

We might pull a near plane distance figure out of our rear, like 1, and then base the rest on 1 multiplied with our aspect ratio.

Right might be 4/3, left might be -4/3, top and bottom are 1 and -1, respectively.

I'm gonna go dig up that code and then come back and post.

Share this post


Link to post
Share on other sites
Here it is!

In my program I had condensed those figures into just width & height. Like so:

public static Matrix getProjectionMatrix(double focus, double near, double width, double height)
{
double matrix[][] = {
{ (2 * near) / width , 0, 0, 0 },
{ 0, (2 * near) / height, 0, 0 },
{ 0, 0, focus / (focus - near), -(focus / (focus - near))},
{ 0, 0, 1, 0 }
};

return new Matrix(4, 4, matrix);
}


I called this like so:

Matrix projection = Main.getProjectionMatrix(50, 3, 1, 0.75);


The 1 & 0.75 figure comes from the aspect ratio of 4:3.

For every vertex, which contains 4 elemenents (x,y,z,w), we multiply it by the 4x4 matrix to yield a vector4 result, then divide the x and the y each by w.

(In my code I also divide z and w by w, and I can't think of why at the moment.)

Taking the x and y yields a point we can use to rasterize.

I don't see anything in here about multiplying the result by the pixel size of the viewport, so disregard that.

I don't know where the figures 3 and 50 came from, I presume I pulled them from my rear and decided they looked about right. Tweaking them will adjust how zoomed things look. You should be able to conjure up such figures from a given viewing angle and viewport size. I think.

Share this post


Link to post
Share on other sites
Starting to look better but still strange!
It's supposed to be a cube! :P




public void setProjection(int width, int height, double zNear, double zFar){
fov = Math.toRadians(fov);
double right = (width / height);
double left = -(width / height);
double top = 1.0;
double bottom = -1.0;

double a_a = (2.0 * zNear) / (right - left);
double a_c = -(right + left) / (right - left);
double a_d = width / 2.0;
double b_c = -(bottom + top) / (bottom - top);
double b_b = (2.0 * zNear) / (bottom - top);
double b_d = height / 2.0;
double c_c = (zFar + zNear) / (zFar - zNear);

double d_c = -(2.0 * zFar * zNear) / (zFar - zNear);

double[][] temp = {{a_a, 0.0, a_c, a_d},
{0.0, b_b, b_c, b_d},
{0.0, 0.0, c_c, d_c},
{0.0, 0.0, 1.0, 0.0}};

matrix = multiply(matrix, temp);
}

Share this post


Link to post
Share on other sites
Without projection matrix:


With projection matrix:


Latest code:

public void setProjection(int width, int height, double zNear, double zFar){
double right = (width / height);
double left = -(width / height);
double top = 1.0;
double bottom = -1.0;

double a_a = (2.0 * zNear) / (right - left);
double a_c = -((right + left) / (right - left));
double b_b = (2.0 * zNear) / (top - bottom);
double b_c = -((top + bottom) / (top - bottom));
double c_c = zFar / (zFar - zNear);
double c_d = -((zNear * zFar) / (zFar - zNear));
double d_c = 1.0;

double[][] temp = {{a_a, 0.0, a_c, 0.0},
{0.0, b_b, b_c, 0.0},
{0.0, 0.0, c_c, c_d},
{0.0, 0.0, d_c, 0.0}};

matrix = multiply(matrix, temp);
}


I think i need some beer! :P

Share this post


Link to post
Share on other sites
I do this:

Matrix flattenedMatrix = projection.multiplyByMatrix(transform);

for(int i = 0; i < vertices.length; i++)
{
double[] result = flattenedMatrix.multiplyByVector(vertices[i]);
result[0] /= result[3]; //x = x / w
result[1] /= result[3]; //y = y / w
points.add(pointFromVertex(result));
}


If w is 1f, than something borked earlier in the chain.

I was wrong earlier, I actually do multiple the result. The pointFromVertex method is defined as:

public static Point pointFromVertex(double[] vertex)
{
Point p = new Point();
p.translate((int) Math.round(vertex[0] * 400) + 200, (int) Math.round(vertex[1] * 300) + 150);
return p;
}


This code is assuming a 400x300 viewport. Of course you should never hardcode such values. I don't know what was going through my head, but that's that.

No idea why I use translate to set the point, either. Don't do that, that's redonkulous. You might trawl through the code I linked to earlier. It's exactly what you're doing, and pretty much nothing else.

Here it is in action. That's a jar, don't let Windows save it as a zip.

Share this post


Link to post
Share on other sites

This topic is 2567 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this