Eigenes "3D rotate"

Hallo.

Es sollte doch nur ein kleiner test werden, wurde aber leider nichts.
naja ich hab versucht mal von allen matrizen sinusen und cosinusen wegzudenken und eine drei dimensionale
rotation (wonach ich in einem anderen thread gefragt hatte) nachzubauen.
Im moment läuft es nur im bereich von 0 bis 90 grad, aber seht selbst in der jar. Das Problem ist…
es sieht einfach nur äähmm… scheisse aus.

Ich frag mich nur warum das so extrem schlecht geworden ist. weiss einer wie man
das nach diesem prinzip und meinem code verbessern könnte?

Vielen Dank für Ratschläge!

Und bitte seht euch die jar an, die man hier downloaden kann,es sieht wirklcih schlechter aus als man vielleicht denkt.
Hier ist der code für die “rotation”:

code…
[spoiler]```public BufferedImage rotate(BufferedImage sourceImage, double angle){

	double imageWidth = sourceImage.getWidth();
	double imageHeight = sourceImage.getHeight();
	double widthPerAngle = imageWidth / 90;
	double heightPerAngle = imageHeight / imageWidth;
	double newWidth = imageWidth - widthPerAngle * angle + 1;
	
	BufferedImage outputImage = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
	BufferedImage bufferImage = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
	BufferedImage line;
	
	Graphics2D g = (Graphics2D) outputImage.getGraphics();
	
	for(int i = 0; i < outputImage.getWidth(); i++){
		double lineHeight = imageHeight - i * heightPerAngle * angle / 100;
		line = sourceImage.getSubimage(i, 0, 1, sourceImage.getHeight());
		g.drawImage(line, i, ((int) imageHeight - (int) lineHeight) / 2, 1, (int) lineHeight, null); 
	}
	
	bufferImage.getGraphics().drawImage(outputImage.getSubimage(0, 0, outputImage.getWidth(), outputImage.getHeight()), 0, 0, null);
	
	g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR));
	g.fillRect(0, 0, outputImage.getWidth(), outputImage.getHeight());
	g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
	g.drawImage(bufferImage, 0, 0, (int) newWidth, outputImage.getHeight(), null);
	
	g.dispose();
	return outputImage;
}```[/spoiler]

Uebersehe ich was oder fehlt da die jar?

sry, ist bereits geändert. hab direkt nach dem abschicken mit dem bearbeiten angefangen, aber irgendwie kann ich hier
trotz 5mb maximalgrösse keine 2mb jar hochladen…

Und ich kann es nicht runterladen -.- irgendwie bricht der immer ab, kp warum…

So ein BigFail ist das doch gar nicht… Mit Matritzen (VecMath) gehts zwar garantiert “leichter” und an die restlichen 270° kommst du, wenn du die Vorzeichen der Sinus- und Cosinus-Werte überprüfst:

0° - 90° Sinus und Cosinus positiv
90° - 180° Cosinus negativ, Sinus positiv
180° - 270° Sinus und Cosinus negativ
270° - 0° Sinus negativ, Cosinus positiv

Das es so aussieht, dass die Mauer bis 45° immer kürzer und danach wieder länger wird, könnte an dem Weg liegen, den die rechte Seite nimmt. Dieser ist nämlich nur mehr gerade, sollte aber eigentlich ein Kreisbogen (Viertelkreis) sein.

könntest du mir erklären wie das mit matrizen grundsätzlich funktioniert? mit dem ganzen fachchinesisch von google komm ich leider nicht weiter,
weil es nirgendwo für leute erklärt wird die “anfänger” auf diesem gebiet sind…

Welche Wiki-Seite hast du denn gelesen? Zugegeben, die können schon SEHR so geschrieben sein, dass die unmittelbar-intuitive Anwendung für 3D-Kram nicht offensichtlich wird…

Ganz kurz:

Einen Punkt im 3D-Raum kann man als einen Vektor (x,y,z,1) beschreiben. (Wozu die 1 gut ist, ist jetzt nicht so wichtig).
So einen Punkt kann man mit einer Matrix multiplizieren. Genaugenommen mit einer 4x4-Matrix, also einer quadratischen “Tabelle” von insgesamt 16 Zahlen. (Wie genau diese Multiplikation abläuft, ist auch nicht so wichtig).
Wenn man so einen Punkt mit einer Matrix multipliziert, bekommt man als Ergebnis einen neuen Punkt. Dieser Punkt hat dann andere Koordinaten als der ursprüngliche Punkt.
Wenn man in so eine 4x4-Matrix ganz bestimmte Zahlenwerte reinschreibt, dann kann diese Matrix verschiedene “Operationen” (d.h. Abbildungen) beschreiben.
Man kann einen Punkt also z.B. verschieben, skalieren oder drehen, indem man ihn jeweils mit einer ganz bestimmten Matrix multipliziert.
Wenn man also einen Punkt ‘a’ ( =(x,y,z,1)) hat, und ihn mit einer geeigneten (Rotations)-Matrix ‘R’ multipliziert, dann ist
Ra = aGedreht
Wenn man ihn stattdessen mit einer Translationsmatrix ‘T’ multipliziert, dann ist
T
a = aVerschoben

Das praktische ist, dass man auch die Matrizen miteinander multiplizieren kann. Wenn man eine Matrix ‘R’ mit einer Matrix ‘T’ multipliziert, kommt wieder eine Matrix raus. Diese Matrix beschreibt dann die Hintereinanderausführung der Abbildungen, die von den Matrizen gemacht werden. So wäre also
RTa = aVerschobenUndGedreht

Wenn man z.B. einen Punkt im 3D-Raum um 23° um die x-Achse rotieren will, muss man sich nicht groß Gedanken machen über “Oh, ja, hier die Koordinate mit Sinus und da mit Cosinius und da dieses-und-jenes Vorzeichen… brech”, sondern schreibt stattdessen einfach
aGedreht = R * a
(für eine geeignete Matrix R - nämlich die hier http://i.msdn.microsoft.com/dynimg/IC554559.png )

Okay ich hab mir deine erklärung durchgelesen, und ausserdem versucht diesen englischen beitrag
durchzulesen: (http://rodrigo-silveira.com/3d-programming-transformation-matrix-tutorial/#.UmVsovkvU9Z)
jetzt verstehe ich das ganz schon ein bisschen besser.

Aber: wie kann ich jetzt das ganze auf ein ganz klassisches beispiel anwenden?
Zum beispiel einen stinknormalen würfel - wie erstelle ich dann ein “3D mesh” von diesem würfel?

So und dann braucht ja jeder punkt aus diesem mesh oder bild oder was auch immer statt der variablen x y z eine
4x4 matrix, richtig? Welcher punkt der matrix spricht denn jetzt nochmal für was?

Bitte nehmts mir nicht übel, ich würd mich echt freuen wenn ihr mir helfen könntet es zu ende zu verstehen…

*** Edit ***

Gut, matrizen multiplizieren hab ich jetzt auch verstanden - ist ja einfach nur zeile mal spalte.
(wobei ich da auch noch eine frage hab: hier ist es ja, (nicht wie normalerweise bei der multiplikation) nicht
egal welche matrix auf welcher seite steht, oder? AxB ist ja nicht dasselbe wie BxA weil ja C dann immer die zeilen
der ersten und die spalten der zweiten hat, oder?)
Ich brauch nur noch einen schub von jemandem, um das programmiertechnisch zu verstehen…

Hmnaja… Jeder Eckpunkt des Würfels ist erstmal wirklich nur ein 3D-Punkt. Die Eckpunkte eines Einheitswürfels wären eben gerade


x y z
0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1

(da kommt dann jeweils noch eine 4. Koordinate dazu, die immer 1 ist)

Und wenn man jetzt den gesamten Würfel ein Stück drehen will, erstellt man sich die Matrix, die die Drehung beschreibt, und Multipliziert mit dieser Matrix jeden Eckpunkt des Würfels. Was rauskommt, sind die Eckpunkte eines gedrehten Würfels.

Dass die Reihenfolge der Matrixmultiplikationen NICHT egal ist, erkennt man z.B. daran, dass es einen Unterschied macht, ob man

Okay vielen Dank!!
Ihr seid hier echt die besten, man bekommt wirklich die lösung für alles, um das mal so in die Runde zu werfen…
man kann gar nicht wirklich genug danken… aber naja

Noch eine frage zu der rotationsmatrix die du eben gepostet hattest -

Rx = 1 0 0 0
0 cos(?) -sin(?) 0
0 sin(?) cos(?) 0
0 0 0 1

Was die beiden werte 1 sind, scheint ja nicht so wichtig zu sein (könnte es vllt doch einer grob erklären?)
und das komplette gerüst - wie genau ist das aufgebaut? vielleicht auch nur grob, nur so vom prinzip?
Und in der mitte sind ja die trigonometrischen rechnungen mit dem “?” - was genau soll diese durchgestrichene null im Bild darstellen?
Also welchen wert muss ich da in der rechnung einsetzen? x, y, und z? also jeweils?
und noch eine sache: Wie genau ist die punkt matrix aufgebaut - sagen wir ein punkt x:10, y:10, z:10, 1 ist das dann:

A = [10 10 10 1] oder

A=

|10| ?
|10|
|10|
|1|

Zuerst das letzte: Die „Punkt-Matrix“, wie du sie nennst (also der Punkt oder Vektor) ist in den hier besprochenen Fällen eigentlich immer das zweite, also


    /10\
A = |10|
    |10|
    \ 1/

Da das aber wie man sieht ziemlich blöd hinzuschreiben ist, schreibt man einfach (10,10,10,1), und vertraut darauf, dass jeder, der das liest, weiß, dass das „senkrecht gemeint“ ist. Formal richtig wäre eigentlich (10,10,10,1)[SUP]T[/SUP], wobei das ‚T‘ für „Transponiert“ steht.

Die „durchgestrichenen Nullen“ sind der griechische Buchstabe „Theta“. Und das ist der Winkel, um den gedreht werden soll (in Radians!).

Und das „komplette Gerüst“ ist (so blöd das klingen mag) einfach so aufgebaut, dass es stimmt. Ja. :smiley: Vielleicht mal an einem Beispiel: Angenommen man hat in 2D einen Punkt (x,y). Wenn man diesen Punkt nun um den Ursprung rotiert, dann hat der rotierte Punkt die Koordinaten
xNeu = x * cos(a) - y * sin(a)
yNeu = x * sin(a) + y * cos(a)
(Wobei ‚a‘ der Rotationswinkel (in Radians!) ist)

Jetzt schau’ dir mal an, wie eine Rotationsmatrix in 2D aussieht:
http://docs.oracle.com/javase/7/docs/api/java/awt/geom/AffineTransform.html#setToRotation(double)

Und wenn man diese Matrix mit einem Vektor multipliziert, dann ist die Rechnung, die tatsächlich durchgeführt wird … genau die, die oben für ‚xNeu‘ und ‚yNeu‘ steht!

Genauso ist es für 3D: In der Matrix steht das, was darin stehen muss, damit (wenn man sie mit einem Vektor multipliziert) genau das Rechengewurschtel auf x,y und z ausgeführt wird, das notwendig ist, um einen gedrehten Punkt zu erhalten.

Der Grund, weswegen man für 3D-Sachen immer eine 4x4-Matrix nimmt, ist, dass man mit dieser Matrix Translationen abbilden kann. Mit einer 3x3-Matrix könnte man nur Dinge wie Rotationen, Skalierungen und Scherungen abbilden, aber eben KEINE Verschiebung. Dadurch, dass man noch eine Dimension dazunimmt, rechnet man in Homogenen Koordinaten, und damit kann eine Verschiebung des Punktes mit so einer Matrix beschrieben werden (siehe auch http://de.wikipedia.org/wiki/Homogene_Koordinaten , da sind die Matrizen auch nochmal ganz nett zusammengefasst )

Hm, räumliche Drehungen lassen sich ganz gut mit Quaternions machen.
Ich hatte schon länger mal vor, da ein Beispiel zu machen. Die Mathematik ist da ja eher nicht das Problem.
Wenn ich z.B. eine Art Drahtmodell eines Würfels darstellen möchte, dann - denk ich - müsst ich ja wohl irgendwie ein eigenes Shape für Linien generieren, das auch ne Z-Koordinate hat.
Dazu bräuchte es dann ne 3D-Tranformation.
Für die Darstellung würden dann nur die X,Y Koordinaten verwendet.
Im Grunde ist das die 2,5 D Darstellung an die ich mal dachte, damals als ich mich bei Dingenskirchen… naja… im alten Forum angemeldet hatte.
Da bin ich genau genommen noch kein Stück weiter.
Vielleicht greife ich das demnächst doch mal wieder auf…

okay dann noch mal auf meine P-Matrix bezogen:

Ich hab die P-Matrix (10 10 10 1) (T :D) und will um 90 grad drehen. (mal neben bei: um welchen punkt wird eigentlich gedreht? ich muss ja eh jeden punkt des würfels mit der matrix multiplizieren? geht das automatisch durch den mittelpunkt?)
Das heisst ich muss in die R-Matrix (rotations-.) einsetzen, wenn r = toRadians(90) :


1    0         0     0 
0  cos(r) -sin(r)  0
0  sin(r)  cos(r)  0
0    0        0       1

und diese RMat jeweils mit allen 8 PMat’s des würfels multiplizieren, und dann den neuen würfel zeichnen? (ein würfel hat doch 8 ecken, also 8 PMat’s…?)

Ja, mit den Matrizen, das ist so ne Sache. Eigentlich braucht man ja für ne Rotation nur ne 3x3 Matrix.
Für allgemeine Transformationen wird die dann aber üblicherweise zu ner 4x4 Matrix erweitert und die Verschiebung gleich mit rein gebaut.
Wie das dann mathematisch gehandhabt wird, hab ich auch noch nicht wirklich durchdrungen.
Wenn ich in 3D drehen wollte, würde ich den Drehmittelpunkt in den Ursprung verschieben, die Drehung ausführen und dann wieder zurück verschieben.

Die Rotation anwenden mach ich zu Fuß so:
Ich schreib mir links die Rotationsmatrix (3x3) hin, oben den zu drehenden Punkt (1x3).
Im Schnitt der Reihen der Rotationsmatrix [a,b,c,d,e,f,g,h,j] und der Spalte [x,y,z] des Punktes ensteht dann die Ergebnismatrix.


               x
               y
               z
a b c  (a*x + b*y + c*z)
d e f  (d*x + e*y + f*z)
g h j  (g*x + h*y + j*z)


Es stellt sich dann mitunter als schwierig dar herauszufinden, um welche Achse man wie weit drehen muss, um einen bestimmten Zustand zu erreichen.
Da kommt man dann fast zwangsläufig zu Euler’schen Winkeln, wo es dann wieder verschiedene Konventionen gibt.
Dann dreht man halt in der Regel 3 mal und muss da dann auch die richtige Reihenfolge der Drehungen beachten.
Mit Quaternionen kann man da einiges vereinfachen. Transparenter wird das damit halt auch eher nicht.

Hier noch ein Beispiel für eine 90° Rotation des Punkte (0,0,1) um die X-Achse


           0
           0
           1
1  0  0   (0)
0  0  1   (1)
0 -1  0   (0)


Das Ergebnis wäre also (0,1,0)

Wenn man nichts anderes macht, geht es immer um den Ursprung. Wenn man um einen anderen Punkt drehen will (also NICHT um den Ursprung) muss man

  • Das Objekt so verschieben, dass der Punkt, um den gedreht werden soll, im Ursprung liegt
  • Die Rotation durchführen
  • Das Objekt wieder zurückschieben, da hin, wo es vorher war.
    Das ist genau das, was in 2D mit AffineTransform (Java Platform SE 7 ) gemacht wird.

Sieht erstmal kompliziert aus, aber … da wird ja nicht viel gemacht: Man bestimmt die Matrix
ZurückVerschiebung * Rotation * ZumUrsprungVerschiebung
(also das Produkt aus drei Matrizen), und mit dieser Matrix multipliziert man dann nacheinander alle Punkte. Beim Würfel… ja, alle 8.

BTW: Es könnte verwirrend sein, den Punkt auch als „Matrix“ zu bezeichnen. Streng genommen kann er zwar als Matrix angesehen werden (nämlich als eine Matrix, die nur eine Spalte hat), aber … es ist doch unüblich…

naja ich verstehe es so besser… glaube ich.
Ich bin gerade dabei eine klasse “matrix” aufzubauen, um später nicht viel zeit damit zu verbringen.
Jedenfalls, ich hab grad irgendwo gelesen das eine matrixmulpiplikation nur möglich ist wenn

MatA(zeilen) == MatB(spalten) ansonsten gehts nicht.
Stimmt das nun, oder nicht?
Denn auf wikipedia gibts ein bildliches beispiel, wo das eben nicht so geht…

*** Edit ***

ach ne stimmt nicht, der typ hat sich vertippt, es ist ja genau andersrum also spalten == zeilen sonst geht nicht.

So eine „Matrix“-Klasse gibt es natürlich schon in 1000facher Ausführung, und keine ist perfekt. Spätestens wenn man Determinanten berechnen will, WILL man den Code dafür eigentlich gar nicht mehr selbst schreiben :wink: Aber wenn es nur um eine Klasse für die 3D-Sache mit den wichtigsten Funktionen (rotate,translate,scale etc) geht, kann das zur Übung vermutlich nicht schaden.

Ist mir schon klar das es das zich mal gibt - aber da ich ja diesen ganzen jram seit heute nur lerne weil ich fuer basisdinge KEIN opengl verwenden und das lernen will, und weil ich alles mal verstehen will, mach ichs halt allein :wink:

By the way… was sind determi dingsbums und wozu braeuchte man das in einem programm?

Die Determinante war eigentlich nur ein Beispiel für eine der Methoden, die man “selten” braucht und die “aufwändig” zu implementieren sind. Repräsentativer wäre vielleicht das Invertieren einer Matrix (was nur geht, wenn die Determinante != 0 ist ;)) : Das braucht man gelegentlich, und sich die geschlossene Form davon selbst zu erarbeiten hat ein IMHO recht ungünstiges Verhältnis zwischen Nutzen und Aufwand+Fehlerpotential.

naja, da man für 3d eh nur multiplikation braucht (oder?), und ich diese bereits implementiert hab,
glaube ich versuche ich mal mit ein paar pixeln zu spielen…
(Achtun achtung: 1000 neue fragen und probleme in anmarsch…)

Btw meine matrix klasse:

code
[spoiler]```package de.skysoldier.matrix3d;

import java.io.PrintStream;

public class Matrix {

private int rows;
private int cols;
private int matrixData[][];

public Matrix(){
	this(new int[1][1]);
}

public Matrix(int matrixData[][]){
	this(matrixData.length, matrixData[0].length);
	this.matrixData = matrixData;
}

public Matrix(int rows, int cols){
	this.rows = rows;
	this.cols = cols;
	this.matrixData = new int[rows][cols];
	this.fill(0);
}

public Matrix multiply(Matrix multiplier){
	if(getCols() != multiplier.getRows()){
		throw new ArithmeticException("matrices not multipliable! (multiplicand cols (" + getCols() + ") != multiplier rows (" + multiplier.getRows() + ") )");
	}
	else{
		Matrix product = new Matrix(getRows(), multiplier.getCols());
		for(int i = 0; i < getRows(); i++){
			for(int j = 0; j < multiplier.getCols(); j++){
				int n = 0;
				for(int k = 0; k < getCols(); k++){ // or multiplier.getRows(); , no difference
					n += getCellData(i, k) * multiplier.getCellData(k, j);
				}
				product.setCellData(i, j, n);
			}
		}
		return product;
	}
}

public void print(PrintStream printer){
	if(printer != null){
		printer.println(toString());
	}
	else{
		System.out.println(toString());
	}
	System.out.println("");
}

public String toString(){
	String dataString = "";
	for(int i = 0; i < getRows(); i++){
		for(int j = 0; j < getCols(); j++){
			dataString += matrixData**[j] + " ";
		} 
		dataString += "

";
}
return dataString;
}

public void fill(int n){
	for(int i = 0; i < matrixData.length; i++){
		for(int j = 0; j < matrixData[0].length; j++){
			matrixData**[j] = n;
		}
	}
}

public int getRows(){
	return rows;
}

public int getCols(){
	return cols;
}

public int[][] getMatrixData(){
	return matrixData;
}

public int getCellData(int row, int col){
	return matrixData[row][col];
}

public void setCellData(int row, int col, int n){
	matrixData[row][col] = n;
}

}```[/spoiler]