Rotations- und sonstige Matrizen

Okay, hier wie versprochen der neue Thread für $title.

 @Marco13  hatte mal diesen link gepostet:

http://i.msdn.microsoft.com/dynimg/IC554559.png

wie sich herausgestellt hat waren IC554559.png die x rotation, …60.png die y rotation und 61 die z rotation.
Soweit so gut, ich habe die dinger nach dem selben prinzip implementiert, (ein bisschen zu manuell, aber was solls:)

siehe Hier:
[spoiler]``` public Matrix createRotationDataX(double angle){
if(!(getRows() == 4 && getCols() == 4)){
throw new UnsupportedOperationException(„rotation matrix should have size 4x4“);
}
else{
double cosAngle = Math.cos(angle);
double sinAngle = Math.sin(angle);
matrixData[0][0] = 1;
matrixData[0][1] = 0;
matrixData[0][2] = 0;
matrixData[0][3] = 0;
matrixData[1][0] = 0;
matrixData[1][1] = cosAngle;
matrixData[1][2] = -sinAngle;
matrixData[1][3] = 0;
matrixData[2][0] = 0;
matrixData[2][1] = sinAngle;
matrixData[2][2] = cosAngle;
matrixData[2][3] = 0;
matrixData[3][0] = 0;
matrixData[3][1] = 0;
matrixData[3][2] = 0;
matrixData[3][3] = 0;
return this;
}
}

public Matrix createRotationDataY(double angle){
	if(!(getRows() == 4 && getCols() == 4)){
		throw new UnsupportedOperationException("rotation matrix should have size 4x4");
	}
	else{
		double cosAngle = Math.cos(angle);
		double sinAngle = Math.sin(angle);
		matrixData[0][0] = cosAngle;
		matrixData[0][1] = 0;
		matrixData[0][2] = sinAngle;
		matrixData[0][3] = 0;
		matrixData[1][0] = 0;
		matrixData[1][1] = 1;
		matrixData[1][2] = 0;
		matrixData[1][3] = 0;
		matrixData[2][0] = -sinAngle;
		matrixData[2][1] = 0;
		matrixData[2][2] = cosAngle;
		matrixData[2][3] = 0;
		matrixData[3][0] = 0;
		matrixData[3][1] = 0;
		matrixData[3][2] = 0;
		matrixData[3][3] = 1;
		return this;
	}
}

public Matrix createRotationDataZ(double angle){
	if(!(getRows() == 4 && getCols() == 4)){
		throw new UnsupportedOperationException("rotation matrix should have size 4x4");
	}
	else{
		double cosAngle = Math.cos(angle);
		double sinAngle = Math.sin(angle);
		matrixData[0][0] = cosAngle;
		matrixData[0][1] = -sinAngle;
		matrixData[0][2] = 0;
		matrixData[0][3] = 0;
		matrixData[1][0] = sinAngle;
		matrixData[1][1] = cosAngle;
		matrixData[1][2] = 0;
		matrixData[1][3] = 0;
		matrixData[2][0] = 0;
		matrixData[2][1] = 0;
		matrixData[2][2] = 1;
		matrixData[2][3] = 0;
		matrixData[3][0] = 0;
		matrixData[3][1] = 0;
		matrixData[3][2] = 0;
		matrixData[3][3] = 1;
		return this;
	}
}```[/spoiler]

das problem nun, ich bekomm es nicht hin die punkte eines würfel sowohl um die x als auch um die y achse zu drehen.
Was ich mache ist den punkt erst einmal mit der x und dann mit der y achse zu multiplizieren - das ergebnis: die animation sieht aus als ob
nur um die y achse gedreht werden würde. Was muss man dafür tun?

Wenn ich immer wieder die matrizen multipliziere, wird der winkel ja sozusagen „draufaddiert“. Das heisst ein mehrmaliger aufruf von

rotateAllPointsX(1)
[spoiler] public void rotateAllPointsX(double angle){ Rx.createRotationDataY(Math.toRadians(angle)); frontTopRight = Point3D.parseMatrix(Rx.multiply(frontTopRight)); frontTopLeft = Point3D.parseMatrix(Rx.multiply(frontTopLeft)); frontBottomLeft = Point3D.parseMatrix(Rx.multiply(frontBottomLeft)); frontBottomRight = Point3D.parseMatrix(Rx.multiply(frontBottomRight)); backTopLeft = Point3D.parseMatrix(Rx.multiply(backTopLeft)); backTopRight = Point3D.parseMatrix(Rx.multiply(backTopRight)); backBottomLeft = Point3D.parseMatrix(Rx.multiply(backBottomLeft)); backBottomRight = Point3D.parseMatrix(Rx.multiply(backBottomRight)); }[/spoiler]

lässt den würfel immer wieder um einen grad weiter drehen.
wie würde man das am besten lösen, immer wieder von neuem zu drehen?

*** Edit ***

  1. hat sich erledigt, war nur ein bisschen dummheit ansonsten klappt es so.

  2. Ich hab mal ein kleines testprojekt angelegt, [link], aber irgendwie scheint das ganze zu laggen.
    ich weiss nicht ob ich das nur so sehe weil ich es programmiert habe, aber vielleicht sind das ja auch diese
    „grenzen“ von java? Ich mein, ein 3d spiel… würde man das damit schreiben können?
    (jaja, es gibt minecraft - aber da wurde opengl benutzt… :slight_smile: )

  3. wie baue ich jetzt am besten die „kamera berechnungen“ ein, und kennt einer wieder lesestoff dafür?

  4. mein bisheriger code - was muss unbedingt geändert werden? Also nur von der grunstruktur und den berehcnungen usw. Zur erstellung der figuren bastel ich morgen ne klasse „mesh“

de.skysoldier.matrix3d
[spoiler][SPOILER=Matrix.java]```package de.skysoldier.matrix3d;

import java.io.PrintStream;

public class Matrix {

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

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

public Matrix(double 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 double[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++){
				double 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 double[][] getMatrixData(){
	return matrixData;
}

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

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

public Matrix createRotationDataX(double angle){
	if(!(getRows() == 4 && getCols() == 4)){
		throw new UnsupportedOperationException("rotation matrix should have size 4x4");
	}
	else{
		double cosAngle = Math.cos(angle);
		double sinAngle = Math.sin(angle);
		matrixData[0][0] = 1;
		matrixData[0][1] = 0;
		matrixData[0][2] = 0;
		matrixData[0][3] = 0;
		matrixData[1][0] = 0;
		matrixData[1][1] = cosAngle;
		matrixData[1][2] = -sinAngle;
		matrixData[1][3] = 0;
		matrixData[2][0] = 0;
		matrixData[2][1] = sinAngle;
		matrixData[2][2] = cosAngle;
		matrixData[2][3] = 0;
		matrixData[3][0] = 0;
		matrixData[3][1] = 0;
		matrixData[3][2] = 0;
		matrixData[3][3] = 0;
		return this;
	}
}

public Matrix createRotationDataY(double angle){
	if(!(getRows() == 4 && getCols() == 4)){
		throw new UnsupportedOperationException("rotation matrix should have size 4x4");
	}
	else{
		double cosAngle = Math.cos(angle);
		double sinAngle = Math.sin(angle);
		matrixData[0][0] = cosAngle;
		matrixData[0][1] = 0;
		matrixData[0][2] = sinAngle;
		matrixData[0][3] = 0;
		matrixData[1][0] = 0;
		matrixData[1][1] = 1;
		matrixData[1][2] = 0;
		matrixData[1][3] = 0;
		matrixData[2][0] = -sinAngle;
		matrixData[2][1] = 0;
		matrixData[2][2] = cosAngle;
		matrixData[2][3] = 0;
		matrixData[3][0] = 0;
		matrixData[3][1] = 0;
		matrixData[3][2] = 0;
		matrixData[3][3] = 1;
		return this;
	}
}

public Matrix createRotationDataZ(double angle){
	if(!(getRows() == 4 && getCols() == 4)){
		throw new UnsupportedOperationException("rotation matrix should have size 4x4");
	}
	else{
		double cosAngle = Math.cos(angle);
		double sinAngle = Math.sin(angle);
		matrixData[0][0] = cosAngle;
		matrixData[0][1] = -sinAngle;
		matrixData[0][2] = 0;
		matrixData[0][3] = 0;
		matrixData[1][0] = sinAngle;
		matrixData[1][1] = cosAngle;
		matrixData[1][2] = 0;
		matrixData[1][3] = 0;
		matrixData[2][0] = 0;
		matrixData[2][1] = 0;
		matrixData[2][2] = 1;
		matrixData[2][3] = 0;
		matrixData[3][0] = 0;
		matrixData[3][1] = 0;
		matrixData[3][2] = 0;
		matrixData[3][3] = 1;
		return this;
	}
}

}```[/spoiler]

Point3D.java
[spoiler]```package de.skysoldier.matrix3d;

import java.awt.Color;

public class Point3D extends Matrix {

private double x;
private double y;
private double z;
private Color c;

public Point3D(double x, double y, double z){
	super(new double[][]{{x},{y},{z},{1}});
	this.x = x;
	this.y = y;
	this.z = z;
	this.c = Color.WHITE;
}

public static Point3D parseMatrix(Matrix m){
	if(!(m.getRows() == 4 && m.getCols() == 1)){
		throw new IllegalArgumentException("Matrix cannot be converted to Point3D");
	}
	else{
		return new Point3D(m.getCellData(0, 0), m.getCellData(1, 0), m.getCellData(2, 0));
	}
}

public void update(){
	this.x = getCellData(0, 0);
	this.y = getCellData(1, 0);
	this.z = getCellData(2, 0);
	System.out.println(this.x);
}

public void setColor(Color c){
	this.c = c;
}

public double getX(){
	return x;
}

public double getY(){
	return y;
}

public double getZ(){
	return z;
}

public void setX(double x){
	this.x = x;
	setCellData(0, 0, x);
}

public void setY(double y){
	this.y = y;
	setCellData(1, 0, y);
}

public void setZ(double z){
	this.z = z;
	setCellData(2, 0, z);
}

public Color getColor(){
	return c;
}

}


MatrixTest.java
[spoiler]```package de.skysoldier.matrix3d;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class MatrixTest extends JPanel{
	
	Point3D frontTopRight = new Point3D(20, 20, 40);
	Point3D frontTopLeft = new Point3D(-20, 20, 20);
	Point3D frontBottomLeft = new Point3D(-20, -20, 40);
	Point3D frontBottomRight = new Point3D(20, -20, 20);
	Point3D backTopLeft = new Point3D(-20, 20, -40);
	Point3D backTopRight = new Point3D(20, 20, -40);
	Point3D backBottomLeft = new Point3D(-20, -20, -40);
	Point3D backBottomRight = new Point3D(20, -20, -20);
	Matrix Rx = new Matrix(4, 4);
	Matrix Ry = new Matrix(4, 4);
	
	public void rotateAllPointsX(double angle){
		Rx.createRotationDataX(Math.toRadians(angle));
		frontTopRight = Point3D.parseMatrix(Rx.multiply(frontTopRight));
		frontTopLeft = Point3D.parseMatrix(Rx.multiply(frontTopLeft));
		frontBottomLeft = Point3D.parseMatrix(Rx.multiply(frontBottomLeft));
		frontBottomRight = Point3D.parseMatrix(Rx.multiply(frontBottomRight));
		backTopLeft = Point3D.parseMatrix(Rx.multiply(backTopLeft));
		backTopRight = Point3D.parseMatrix(Rx.multiply(backTopRight));
		backBottomLeft = Point3D.parseMatrix(Rx.multiply(backBottomLeft));
		backBottomRight = Point3D.parseMatrix(Rx.multiply(backBottomRight));
	}
	
	public void rotateAllPointsY(double angle){
		Ry.createRotationDataY(Math.toRadians(angle));
		frontTopRight = Point3D.parseMatrix(Ry.multiply(frontTopRight));
		frontTopLeft = Point3D.parseMatrix(Ry.multiply(frontTopLeft));
		frontBottomLeft = Point3D.parseMatrix(Ry.multiply(frontBottomLeft));
		frontBottomRight = Point3D.parseMatrix(Ry.multiply(frontBottomRight));
		backTopLeft = Point3D.parseMatrix(Ry.multiply(backTopLeft));
		backTopRight = Point3D.parseMatrix(Ry.multiply(backTopRight));
		backBottomLeft = Point3D.parseMatrix(Ry.multiply(backBottomLeft));
		backBottomRight = Point3D.parseMatrix(Ry.multiply(backBottomRight));
	}
	
	public MatrixTest(){
		JFrame f = new JFrame();
		f.setSize(800, 600);
		f.setLocationRelativeTo(null);
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		f.setLayout(new BorderLayout());
		f.add(this);
		f.setVisible(true);
	
		new Thread(new Runnable() {
			public void run() {
				while(true){
					rotateAllPointsX(0.5);
					rotateAllPointsY(1);
					repaint();
					try{Thread.sleep(10);}catch(Exception e){}
				}
			}
		}).start();
	}
	
	public void paintComponent(Graphics g){
		super.paintComponent(g);
		g.setColor(Color.BLACK);
		g.fillRect(0, 0, getWidth(), getHeight());
		g.setColor(Color.WHITE);
		
		g.drawImage(getCubeMesh(), 100, 100, null);
	}
	
	public BufferedImage getCubeMesh(){
		BufferedImage drawPane = new BufferedImage(500, 100, BufferedImage.TYPE_INT_ARGB);
		Graphics2D g = (Graphics2D) drawPane.getGraphics();
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		g.setColor(Color.DARK_GRAY);
		g.fillRect(0, 0, drawPane.getWidth(), drawPane.getHeight());
		g.setColor(Color.WHITE);
		
		for(int i = 0; i < 5; i++){
			drawLine(backTopRight, backTopLeft, g, i * 90 + 50, 40);
			drawLine(backBottomRight, backBottomLeft, g, i * 90 + 50, 40);
			drawLine(backTopRight, backBottomRight, g, i * 90 + 50, 40);
			drawLine(backTopLeft, backBottomLeft, g, i * 90 + 50, 40);
			
			drawLine(frontTopRight, frontTopLeft, g, i * 90 + 50, 40);
			drawLine(frontBottomRight, frontBottomLeft, g, i * 90 + 50, 40);
			drawLine(frontTopRight, frontBottomRight, g, i * 90 + 50, 40);
			drawLine(frontTopLeft, frontBottomLeft, g, i * 90 + 50, 40);
			
			drawLine(backTopRight, frontTopRight, g, i * 90 + 50, 40);
			drawLine(backTopLeft, frontTopLeft, g, i * 90 + 50, 40);
			drawLine(backBottomLeft, frontBottomLeft, g, i * 90 + 50, 40);
			drawLine(backBottomRight, frontBottomRight, g, i * 90 + 50, 40);
		}
		
		return drawPane;
	}
	
	public void drawPixel(double x, double y, Graphics g){
		g.drawLine((int) x, (int) y, (int) x, (int) y);
	}
	
	public void drawLine(Point3D p1, Point3D p2, Graphics g, int translateX, int translateY){
		int x1 = (int) p1.getX() + translateX;
		int x2 = (int) p2.getX() + translateX;
		int y1 = (int) p1.getY() + translateY;
		int y2 = (int) p2.getY() + translateY;
		g.drawLine(x1, y1, x2, y2);
	}

	public static void main(String[] args){
		new MatrixTest();
	}
}```[/spoiler]
[/SPOILER]

Zu 1. … nicht ganz klar. Um zwei Rotationen nacheinander zu machen, sollte man die beiden Rotationsmatrizen miteinander multiplizieren, und die Vertices mit DIESER Matrix transformieren.

Bei Rotationen muss man immer aufpassen: Wenn man immer NUR um x, y oder z dreht (also die Rotation mit sogenannten “Eulerwinkeln” beschreibt - das sind die Drehwinkel um die jeweiligen Achsen), dann kann es zum http://de.wikipedia.org/wiki/Gimbal_Lock kommen. Allgemeiner ist es, wenn man eine Rotation beschreibt über eine Rotationsachse+Rotationswinkel. Die Matrix aus dieser Achse+Winkel zu erstellen ist etwas komplizierter, das ist nämlich die http://upload.wikimedia.org/math/b/5/a/b5aee9238d59bc53c5b798829bdfa583.png , aber zum Glück muss man das ja nur EINmal hinschreiben und gut ist. Die Alternative wären Quaternionen, die wurden schon an anderer Stelle angesprochen.

  1. Man sollte NICHT die Punkte immer um 1 Grad drehen. Überhaupt sollte man die Änderungen nicht auf die schon veränderten Punkte anwenden. Dabei entsteht IMMER ein kleiner Rundungsfehler, und das hätte zur Folge, dass dein anfangs so schöner Würfel während der Drehung erstaunlich schnell zu einem unansehnlichen, unförmigen Klumpen zusammenschnurrt. Man sollte üblicherweise IMMER die ursprünglichen Punkte beibehalten, und für’s Zeichnen immer neu die ursprünglichen Punkte mit der aktuellen Matrix multiplizieren, um die Punkte zu bekommen, die man zeichnen will. Also NICHT sowas wie
List<Point> points = ...

void animation()
{
    while (true) 
    {
        rotateByOneDegree();
        draw(points);
    }
}

void rotateByOneDegree()
{
    points = rotateByOneDegree(points);
}

sondern sowas wie

List<Point> points = ...
double currentAngle = 0;

void animation()
{
    while (true) 
    {
        List<Point> currentPoints = rotateBy(currentAngle, points);
        draw(currentPoints);
        currentAngle+=1;
    }
}

Gut vielen dank, das werde ich dann entsprechend ändern.
deine antwort zu 1. ist mir nicht ganz klar.
Was genau bringt mir jetzt die rotationsachse?
und zu 2: du meinst also das pro durchgang ein paar millionstel stellen “kaputt” gehen, und das irgendwann
sichtbar wird? Ja, jetzt ist es mir bewusst.
Quaternionen… gut ich schaus mir auch mal an.

aber was sagst du dazu, das das programm so laggt? liegt das etwa an den kleineren “rundungsfehlern”, also weil ich nicht die
original punkte behalte?

Ein echtes “Laggen” habe ich jetzt nicht bemerkt.

Das angedeutete Problem mit dem “Zusammenschnurren”, bzw. dem Fehler, der sich ansammelt, wenn man die transformierten Punkte immer wieder weiter transformiert, ist hier nicht so schlimm: Da mit double gerechnet wird, ist das kaum wahrnehmbar (aber dennoch vorhanden…)

In createRotationDataX muss matrixData[3][3] noch 1 sein (statt 0)

In getCubeMesh nicht immer ein neues Bild erstellen. Man könnte GROB sowas machen wie

    BufferedImage drawPane = null;
    public BufferedImage getCubeMesh(){
        if (drawPane == null)
        {
            drawPane = new BufferedImage(500, 100, BufferedImage.TYPE_INT_ARGB);
        }

aber… wozu wird das da überhaupt erstellt? Man könnte doch direkt ins Ziel-Graphics reinmalen…?!

Warum speichert Point3D seine x,y,z nochmal? Die Daten stehen schon drin, als Zellen der Matrix, die er ja extendet. Aus Programmiererischer Sicht sollte man sich genau überlegen, ob Point3D extends Matrix wirklich Sinn macht (ob damit das http://de.wikipedia.org/wiki/Liskovsches_Substitutionsprinzip noch erfüllt ist, wage ich auf die Schnelle nicht abzuschätzen…), auch wenn ein Mathematiker vermutlich sagen würde, dass das richtig ist.

Wenn das wirklich ein Würfel sein soll, müßten die Koordinaten wohl

    Point3D frontTopRight = new Point3D(20, 20, 20);
    Point3D frontTopLeft = new Point3D(-20, 20, 20);
    Point3D frontBottomLeft = new Point3D(-20, -20, 20);
    Point3D frontBottomRight = new Point3D(20, -20, 20);
    Point3D backTopLeft = new Point3D(-20, 20, -20);
    Point3D backTopRight = new Point3D(20, 20, -20);
    Point3D backBottomLeft = new Point3D(-20, -20, -20);
    Point3D backBottomRight = new Point3D(20, -20, -20);

sein.

Auch wenn das nur ein erster Test ist, und auch wenn die Punkte des Würfels in Zukunft ohnehin nicht mehr als benannte Variablen rumliegen (sondern z.B. in einer List, oder gleich in einer eigenen Klasse wie “Object3D”), überleg’ mal, ob man die Methoden rotateAllPointsX und rotateAllPointsY nicht “kompakter” und mit weniger Wiederholung schreiben könnte…

Wie auch schon an anderer Stelle gesagt: Bei der Methode
public Matrix multiply(Matrix multiplier){
könnte man in Betracht ziehen, sowas zu schreiben wie

public Matrix multiply(Matrix multiplier){
    return multiply(multiplier, new Matrix(getRows(), multiplier.getCols()));
}

public Matrix multiply(Matrix multiplier, Matrix product){
    // Wie vorher, aber verwende übergebenes 'product'
    // (und erstelle nur ein neues, wenn das übergebene 'null' ist)
}

Damit würde man sich den Krampf mit dem Point3D.parseMatrix sparen, sondern könnte direkt

Point3D originalPoint = ...
Point3D transformedPoint = ...
matrix.multiply(originalPoint, transformedPoint);

schreiben.

Ansonsten… “Details”. Ob es
public double[][] getMatrixData(){
wirklich geben muss, hatte ich ja schon gefragt. Und für eine Methode, die nichts erzeugt, sondern nur Werte setzt, ist ein Name wie createRotationDataX IMHO unpassend. Genauso wie ‘parseMatrix’ ja auch nichts parst. Aber das nur nebenbei.

OT: gut, da wären wir also bei „unterschied zwischen hobby und profi“ …

Das sollte jetzt nicht so aussehen als ob mir deine anweisungen egal waren, ich war gestern nur noch zu müde das alles umzusetzen :smiley:

1 nicht 0: oops stimmt. aber wieso sprüt man trotz fehler kaum eine veränderung?

jop ich mach das sowieso anders mit dem bild, aber stimmt das war ein dummer fehler

ja könnte man, aber ich dachte mir, wenn ich das ganze auf eine fläche male, kann ich diese dann noch weiterverwenden?.. zum beispiel wenn es nicht
gerade ein spiel ist sondern eine anwendung, in der ich die panels dann bewegen kann oder so…
aber hast recht - hier macht das keinen sinn.

Das mit Point3D ist so eine sache - ich hab das mit den doppelten variablen nur so gemacht weil ich für mich selber dachte ich könnte das dann besser auseinanderhalten…
sprich ob das jtezt ein punkt ist oder eben nur eine matrix… aber ich könnte in getX ja auch die zelle returnen… stimmt, ist schwachsinn.

Ist mir klar, das war vorher auch ein würfel, aber ich wollt halt mal so „krüppel“ figuren testen :smiley:

ja, das mit der kopaktheit - werde ich mal sehen müssen. aber eigentlich, muss ich doch nach wie vor jeden punkt mit jeder projektionsmatrix multiplizieren?..
und mit den unbenannten variablen wäre ich bei der nächsten frage - wie würde man das vom prinzip am besten anstellen, dem programm zu sagen welche punkte er verbinden soll,
bzw zwischen ewlchen er flächen malen soll? ich hab zwar schon eine idee - man könnte eine figur als textdatei speichern in der dann jede zeile einen neuen punkt enthält, (dafür einen grafischen editor mit vorschau schreiben), und anschliessend eine liste mit das und das soll verbunden werden… aber ist das sinnvoll?

Stimmt, danke, das mit dem Point3D muss ich wirklich nochmal komplett überarbeiten.

jaja get matrix data - wie gesagt es ist eine „allgemeine“ klasse, und ich dachte, naja, der vollständigkeit halber… aber eigentlich braucht mans ja wirklich nicht… naja ich pack mal ein „/* */“ drumherum :slight_smile:

stimmt, setMatrixData wäre passender, oder?

BTW: was heisst imho?

parse dachte ich im sinne von „übersetzen“ oder so

und noch eine sache: ich finde im internet einfach keine anleitung zum thema perspektivische projektionsmatrix… sprich objekt weiter weg = objekt kleiner, kamera ansicht…
hat einer vielleicht doch einen guten link?

Ob 0 oder 1 macht nicht nur “kaum” einen Unterschied, sondern … GAR keinen - bisher. Weil bisher keine Translationen verwendet werden. Bei einer Translation stehen in der letzten Spalte der Matrix die Verschiebungen


1 0 0 a   x    x+a
0 1 0 b   y    y+b
0 0 1 c * z =  z+c
0 0 0 1   1    1 

Wenn man in irgendeiner Matrix unten rechts eine 0 hat, kommt (wenn der Rest nur die Einheitsmatrix ist) das hier raus:


1 0 0 0   x    x
0 1 0 0   y    y
0 0 1 0 * z =  z
0 0 0 0   1    0 

D.h. die 1, die als letzte Stelle des Vektors stehen muss, wird zu einer 0. Wenn man diesen Punkt jetzt mit einer Translationsmatrix multipliziert, kommt


1 0 0 a   x    x
0 1 0 b   y    y
0 0 1 c * z =  z
0 0 0 1   0    0 

raus - d.h. die Verschiebung fehlt!

Die Frage, welche Punkte mit Kanten verbunden werden sollen, geht in Richtung einer allgemeinen Repräsentation von Meshes. Man hätte also neben einer Klasse “Point3D” auch noch eine Klasse “Face” (oder “Triangle” - meistens nimmt man nur Dreiecke). Und diese Klasse speichert eben die Point3Ds, aus denen die Fläche besteht - oder ggf. nur die indizes der Punkte, wenn sie in einer Liste liegen.

IMHO = In My Humble Opinion

Zu den Projektionen hatte ich schonmal
http://www.songho.ca/opengl/gl_projectionmatrix.html
gepostet. Nicht, dass ich diese Seite empfehlen würde (ist viel ziemlich langweiliger Formelkram, den man nicht unbedingt nachvollziehen muss). Aber das Endergebnis, nämlich die Matrix, ist ja nicht so kompliziert.

achso dafür ist diese 1… ?

okay also werde ich da selbst überlegen müssen wie ich meine meshes aufabaue.
aber MÜSSEN es unbedingt dreiecke sein; bzw was sind die vorteile?

achsoooo…

ach ja stimmt, das lese ich mir noch einmal durch

*** Edit ***

Okay was ich jetzt verstanden hab:

um das ganz perspektivisch zu projezieren (bei orthografisch gibts ja keinen grössenunterschied zwischen entfernung, oder?)
brauche ich…

aber was genau sind jetzt

n, r, l, t, b, und f ?

wenn das tatsächlich im text steht…

dann bitte zeilenangaben xD

[QUOTE=mymaksimus]aber was genau sind jetzt

n, r, l, t, b, und f ?[/QUOTE]Near, Right, Left, Top, Bottom und Far
Ein 3D-Raum ist ein Quader, der durch Vorder-, Rück-, linke, rechte, obere und untere Seite begrenzt ist. LRTBNF geben die Positionen dieser Seiten (Ebenen) an.

Zu dem parse matrix ersparen:
das problem ist das ich in eine Graphics3D klasse, die ich bald mal machen werde, gerne methoden hätte wie drawLine(Point3D, Point3D);
und deshalb kann ich aus dem Point3D nicht einfach eine Matrix machen, denn dann ist es eben kein punkt mehr.
Ich könnte zwar die multiply in Point3D überschreiben, aber ich multitpliziere ja Rx * Point3D und deshalb bringt mir das auch nichts.
Deshalb brauch ich halt irgendwie ne methode die das ganze dann wieder zu Point3D macht…

inner edit

Ich hab das jetzt so gelöst:

	public Point3D transform(Matrix transform){
		return transform.multiply(this).toPoint3D();
	}

*** Edit ***

[QUOTE=Spacerat]Near, Right, Left, Top, Bottom und Far
Ein 3D-Raum ist ein Quader, der durch Vorder-, Rück-, linke, rechte, obere und untere Seite begrenzt ist. LRTBNF geben die Positionen dieser Seiten (Ebenen) an.[/QUOTE]

Okay - das klingt einleuchtend. ^^

[QUOTE=mymaksimus]Zu dem parse matrix ersparen: …
und deshalb kann ich aus dem Point3D nicht einfach eine Matrix machen, denn dann ist es eben kein punkt mehr.
[/QUOTE]
Kapier’ ich nicht. Ein Point3D IST (bei dir) eine Matrix. Und deswegen könnte er bei einer Methode
public Matrix multiply(Matrix multiplier, Matrix product){
als zweites Argument übergeben und auch zurückgegeben werden.

aber ich erhalte als ergebniss eine Matrix.

public Matrix multiply(){…}

verstehst du jetzt?
Aber das ergebniss ist ja wieder ein punkt, nur dass er im programm halt wieder eine matrix ist…

Hmja. Das wäre nur relevant, wenn man

Point3D originalPoint = ...
Point3D transformedPoint = matrix.multiply(originalPoint, null);

aufrufen wollte. Man könnte aber einfach

Point3D originalPoint = ...
Point3D transformedPoint = ...
matrix.multiply(originalPoint, transformedPoint);

aufrufen (und dass der übergebene ‘transformedPoint’ dann zurückgegeben wird, aber dort nur noch als ‘Matrix’ bekannt ist, ist ja egal: Die Ergebnisse werden da ja reingeschrieben).

Aber nochmal zur Betonung: Das ist alles nicht so einfach, und das sollte man sich alles genau überlegen. Was ich schreibe sind keine “Empfehlungen für das nachhaltige, saubere und zukunftssichere eierlegende Wollmilch-Design einer 3D-Engine”, sondern nur verschiede Möglichkeiten, mit verschiedenen Vor- und Nachteilen…

achsoo so meinst du das. ja das geht natürlich auch, mal sehn :slight_smile:

Jaja das ist mir klar - aber trozdem habt ihr alle schon sehr viel erfahrung und könnt helfen.
mir ist klar das auch nicht alles was vom Herrn @Marco13 kommt perfekt ist :stuck_out_tongue:
Aber ihr Profis könnt ja bei lösungvorschlägen (generell) auch mitteilen welche nachteile so eine lösung denn hätte…
und mir ist auch klar das ich es (mit meiner geringen erfahrung) nicht in ein paar wochen schaffen werde eine 3D engine aufzubauen xD
Aber es geht mir halt ums prinzip, und mit dem was ich bis jetzt verstanden hab könnte ich schon ein kleines „in die wand rein lauf und ausweich spiel“ basteln.

Was man auf jeden Fall vergessen kann, ist View- und Projectionmatrix dauerhaft zu verknüpfen. Wenn man das tut, funktioniert anscheinend die Viewmatrix nicht mehr richtig, also da wird dann z.B. falsch verschoben, skaliert oder gedreht.

Was Marco mit “matrix.multilpy(toTransform, transfomed)” meint, ist im Übrigen genau das, worauf ich dein Augenmerk schon in einem anderen Thread gelenkt habe - die .transform()"-Methode in meinen Beispielen dort.

was meinst du mit dauerhaft verknüpfen?

Zwei Matrizen miteinander multiplizieren z.B. und das Ergebnis davon in einer Matrix speichern, die man dann statt der anderen beiden verwendet. Wenn man es so macht, wird nicht nur die View- sondern auch die Projectionmatrix verschoben, skaliert oder gedreht, statt nur die Viewmatrix.

und wie macht man es dann richtig?
abgesehen davon mach ich es ja so:

punkt matrix kopie * transformations matrix 1
punkt matrix kopie * transformations matrix 2
punkt matrix kopie * transformations matrix …
punkt matrix kopie * perspective projection matrix
punkt matrix zeichnen

matrix kopie load Identity
matrix kopie *= viewmatrix
matrix kopie *= projectionmatrix
matrix kopie transform(inPoints, outPoints)
draw outPoints

die Viewmatrix enthält dabei die Elemente Scale, Translate und Rotate.

Das Problem mit Matrizen ist, sag ich mal, dass sie für ein orthogonales Koordinatensystem im Grunde zu viel Information mit schleppt.
(Die Determinante von rechtwinkligen Koordinatensystemen hat stets den Betrag 1.)
Wenn ich mir ein Koordinatensystem als starres Gebilde von 3 Achsen gleicher Skalierung vorstelle, die jeweils rechtwinklig zu einander stehen und im Koordinaten-Ursprung drehbar gelagert ist,
dann wäre jede denkbare Rotation dieses Systems durch die Drehung eines im Ursprung angeordneten Knebels erreichbar. (nach Euler)
Zur Beschreibung jeder beliebigen Rotation würde die Richtung des Knebels und des zugehörigen Drehwinkels ausreichen.
Das ist in etwa das, was mit Hilfe von Quaternionen erreicht werden kann. Ein Quaternion besteht dann auch nur aus 4 Werten, statt der 9 einer Rotationsmatrix.
Quaternionen sind damit aber auch auf reine Rotation beschränkt.

Bei Projektionen verändert sich aber die Situation in aller Regel. Das Koordinatensystem bleibt nicht länger rechtwinklig und da muss man durchaus bei Berechnungen aufpassen.
Was für rechtwinklige Koordinatensysteme noch funktioniert, muss mit verzerrten Systemen NICHT mehr unbedingt funktionieren.

hä, ist das koordinatensystem dann gebogen oder wat :smiley:
@Spacerat : ist ja fast das selbe wie ich es hab, nur das du viewmatrix zusammengefasst hast, oder?