# Z-Buffering, ok, aber was ist mit X-Buffering und Y-Buffering?



## Developer_X (21. Jan 2010)

Hi, ich brauche mal wieder eure Hilfe. Ich habe mittlerweile, wenn es ein paar von euch bemerkt haben, eine erste kleine, eigene 3D-Engine geschrieben, ich habe auch einen Modelloader für ".modell" dateien geschrieben, diesen Datei format habe ich mir selbst geschaffen.

Nun aber, bei komplexeren Objekten tritt folgendes Problem auf:
Das einfache Z-Buffern ist nicht mehr genug, ich müsse auch X-Buffern und Y-Buffern, das sollte ungefähr so gehen:

0 sollte der zentral punkt von den x-koordianten sein, wer nun näher an der null ist, von der - Seite, sollte auch im Array um einen Eintrag vor geschoben werden, und von der + Seite, sollte im Array um einen Eintrag nach hinten geschoben werden, oder nicht?

Und wenn ich recht habe, wie sollte ich das jetzt in Java realisieren?
Ich hab wirklich keine Ahnung, wie ich das anstellen  sollte, 
kann mir einer Helfen?


```
public void drawObjects()
	{
		if(objects!=null)
		{
			Arrays.sort(objects, new PointComparator());
			for(int i = 0;i<objects.length;i++)
				objects[i].drawObject();
		}
	}
	
	class PointComparator implements Comparator<Object_3D> 
	{
	    @Override
	    public int compare(Object_3D o1, Object_3D o2) 
	    { 
	    	if (o2.z < o1.z) 
	    		return -1;
	    	if (o2.z > o1.z) 
	    		return 1;
	    	return 0;
	    }
	}
```
Dies ist ein ausschnitt aus der klasse Graphics_3D von mir, wie die Methode "draw_Objects" funktioniert, muss euch erstmal nicht interessieren, es geht mehr um den PointComparator, versteht ihr?

Also wie sollte ich dies nun anstellen?

Danke für eure Hilfen
Developer_x


----------



## Noctarius (21. Jan 2010)

Unabhängig vom Problem sehe ich mind. schon mal ein Performance-Problem, wenn du das später mal ernsthaft (...) einsetzen magst.

Du legst bei jedem Rendern eine neue Instanz von PaintComparator an. Wieso? Du hast keine Variablen welche nicht methodengebunden sind und damit kannst du die Instanz einmal erzeugen und statisch halten. Es wird niemals race conditions geben.

Ansonsten das mittlerweile echt nicht nur nervende, ätzende sondern auch leidige Thema "Code Conventions".


----------



## Developer_X (21. Jan 2010)

Gut kannste mir aber trotzdem helfen,
und übrigstens die Instanz ist nur einmal, von daher brauchst du dir keine sorgen zu machen.

Ich will zur Code Convention folgendes sagen:
Ok, es gibt regeln, dass zum beispiel package name klein geschrieben werden sollen, mach ich auch,
aber Klasse und methoden und dies und das, das mag ich lieber selbst entscheiden, weil es für mich einfacher ist durchzublicken.

Developer_X


----------



## Noctarius (21. Jan 2010)

Es bringt dir aber nichts irgendwas zu lernen, damit wirst du später, wenn du es richtig professionell machen willst nicht weiterkommen. Es gibt Regeln, damit Jeder sofort mit Code von Anderen klar kommt.

Das Problem ist, einmal angewöhnte "Fehler" wird man dann später nur schwer wieder los. Ergo ist es einfach schlecht sich "falsche" Dinge anzueignen.

Das haben dir Andere hier im Forum auch schon öfter versucht klarzumachen.

----------------------------

Nein du hast nicht nur eine Instanz, sondern du legst bei jedem Durchlauf eine neue Instanz an "new PaintComparator()"

-----------------------------

Nein sorry ich kann dir nicht helfen, da ich von 3D Programmierung in Java keinen Plan habe. Ich arbeite in einem anderen Softwarebereich.


----------



## Marco13 (21. Jan 2010)

Einen Z-Buffer verwendet man, damit Objekte, die weiter hinten liegen, nicht die Objekte übermalen, die weiter vorne liegen. Und dabei geht es genaugenommen nicht um ganze Objekte, sondern um _Pixel_. Man kann aber die Pixel kaum sortieren. Es wird erst unmittelbar vor dem Setzen des Pixels überprüft, ob schon ein anderer, näherer Pixel vorhanden ist. Bei einem Zeichenbereich der Größe 1024x768 braucht man einen Z-Buffer, der 1024x768 Einträge hat. Und was soll der x-Buffer dann genau machen?


----------



## Developer_X (21. Jan 2010)

Ich weiß, aber so perfekt will ich es nicht haben, ich will nur grob sagen könne, dass ein objekt vor einem anderen ist, verstehst du?
Wie mach ich das für x und y ?


----------



## Marco13 (21. Jan 2010)

:noe: Es macht beim besten Willen keinen Sinn.


----------



## SegFault (21. Jan 2010)

für solche Sachen werden nromalerweise Octtrees eingesetzt. Das beste ist dazu was im netz zu suchen. Prinzipiell ist es ein Baum bei dem jeder knoten 8 kinder hat. Jeder Knoten ist ein 3D Punkt die 8 Kinder sind Quadranten in den verschiedenen richtungen. 
Octree - Wikipedia, the free encyclopedia
Mit solchen system arbeiten dann echte 3D engines. Ich glaube das wirklich mathematisch zu berechnen was vor anderen dingen liegt ist sehr aufwendig.


----------



## Developer_X (21. Jan 2010)

Bei mir is es so:
Ein Rechteck wird gezeichnet, davon werden von den vier ecken der mittel wert aller z koordianten rausgefiltert verstehst du?


Genauso wie für x und y,

ich will doch einfach nur, dass mir jemand sagt, wie ich x und y noch ordnen kann, also buffern, für die 3D welt, die schwierigkeit dabei ist für mich nur folgende:
es gibt ja einen wert 0, alle die halt von der - seite näher an der null sind müssen irgendwie geordnet werden und dann irgendwie alle von der + seite, kann mir einer helfen?


----------



## Guest2 (21. Jan 2010)

Moin,

draußen ist kälter als dunkel hat genau denselben Sinn wie "X-Buffering und Y-Buffering", nämlich garkeinen. 

Wie Marco schon schrieb, ein Tiefentest (ZBuffer) muss Pixelbasiert sein. Das ist auch keine Frage des "so perfekt", sondern schlicht des "geht oder geht nicht".

Denk mal über folgendes Bild nach:







Gruß,
Fancy


----------



## Developer_X (21. Jan 2010)

Ich hab mir das Bild angeguckt, ...

Schaut euch mal das hier an:

```
@Override
	    public int compare(Object_3D o1, Object_3D o2) 
	    { 
	    	if (o2.z < o1.z) 
	    	{
	    		if(o1.x<0&&o2.x<0)
	    		{
	    			if(o2.x<o1.x)
	    			{
	    	    		return -1;
	    			}
	    			else if (o2.x>o1.x)
	    			{
	    	    		return +1;
	    			}
	    		}
	    		else if(o1.x>0&&o2.x>0)
	    		{
	    			if(o2.x>o1.x)
	    			{
	    	    		return -1;
	    			}
	    			else if (o2.x<o1.x)
	    			{
	    	    		return +1;
	    			}
	    		}
	    		return -1;
	    	}
	    	if (o2.z > o1.z) 
	    	{
	    		if(o1.x<0&&o2.x<0)
	    		{
	    			if(o2.x<o1.x)
	    			{
	    	    		return -1;
	    			}
	    			else if (o2.x>o1.x)
	    			{
	    	    		return +1;
	    			}
	    		}
	    		else if(o1.x>0&&o2.x>0)
	    		{
	    			if(o2.x>o1.x)
	    			{
	    	    		return -1;
	    			}
	    			else if (o2.x<o1.x)
	    			{
	    	    		return +1;
	    			}
	    		}
	    		return 1;
	    	}
	    	return 0;
	    }
```
Leider klappt das nicht so wie gewünscht, warum?
Developer_X

Ich weiß das das net der richtige weg ist, aber was ist der richtige weg denn dann? Zeigt mal code her bitte


----------



## Developer_X (21. Jan 2010)

Ich bitte euch wirklich außerordentlich sehr darum mir zu helfen, da ich bemüht bin mein Problem zu lösen.
Bitte kann mir keiner einen produktiven programmatischen Ansatz machen`?


----------



## Tobias (21. Jan 2010)

Ne, weil wir dein Problem nicht verstehen, bzw dein Vorhaben keinen Sinn ergibt. Siehe die Posts weiter oben.


----------



## Noctarius (21. Jan 2010)

Besonders Letzteres. Sieh doch ein, wenn 3 Leute sagen das macht keinen Sinn, dass es so ist.


----------



## EgonOlsen (21. Jan 2010)

Noctarius hat gesagt.:


> Sieh doch ein, wenn 3 Leute sagen das macht keinen Sinn, dass es so ist.


Ich mache auch noch mit: Das ergibt keinen Sinn! Ich vermute mal, du willst damit die Unzulänglichkeiten einer flächenbasierten Tiefensortierung umgehen? Das wird so nichts. Wenn du den Maleralgorithmus (Wikipedia) verwendest, hast du immer Situationen, die er nicht eindeutig auflösen kann (Sieht man z.B. auch bei der Away3D Flash Engine, die das ebenfalls so macht). Das müssen gar nicht diese zyklisch überlappenden Fälle sein, die in der Beschreibung des Algorithmus immer aufgeführt werden. Es reicht z.B. ein Objekt unter einer gekippten Fläsche zu haben, dessen mittlerer Z-Wert näher am Betrachter ist, als der der Fläche selber. Auf einmal erscheint das Objekt vor der Fläsche, was falsch ist.
Um das zu lösen, gibt es mehrere Ansätze:

- Es gibt Erweiterungsansätze zum Maleralgo...die zu verfolgen aber nicht wirklich sinnvoll ist. Letztendlich läuft es dabei auf ein Teilen von Flächen hinaus, was sehr aufwändig ist und nicht lohnt.
- Du kannst auch einfach deine Modelle mit mehr Polygonen versehen. Das behebt nicht das Problem, vermindert aber extreme Fehlerfälle zugunsten mehrerer kleiner. Schneller wird das Zeichnen dadurch aber natürlich nicht.
- Wenn es ein Software-Renderer ist, der seinen eigenen Rasterizer nutzt, kann man einen S-Buffer nutzen. Das machte z.B. Quake so. Heutzutage braucht das eigentlicht niemand mehr.
- Du kannst einen echten Z- bzw. W-Buffer verwenden (was du da hast, ist keiner...). Der speichert, wie schon von anderen hier erwähnt, den Tiefenwert/Pixel. Dafür musst du aber, wie schon beim S-Buffer, deinen eigenen Rasterizer verwenden...oder eben Hardware nutzen, die das automatisch macht.

Von den genannten Möglichkeiten ist eigentlich nur der Z-Buffer heutzutage wirklich sinnvoll. Wenn du das nicht kannst, weil du z.B. Polygone mit Java2D-Funktionen malst und somit keinen Tiefenwert/Pixel erzeugen kannst, musst du wohl mit den Unzulänglichkeiten deiner Sortiermethode leben. Irgendwelche X- und Y-Werte einzurechnen ist jedenfalls wirklich und wahrhaftig unsinnig.


----------



## Soulfly (21. Jan 2010)

Ich stimme auch zu, es geht so nicht.

Aber ich gebe dir mal einen anderen Denkanstoß. Und zwar Mathematik.

Eine eher unperformante Herangehenweise (bei vielen Bildschirmpixeln) aber sehr sehr sehr genau.

1. Gehe jeden Pixel der Anzeigefläche durch.
3. Berechne dessen Raumkoordinate
2. Schicke von dort einen Strahl im Raumkoordinatensystem Richtung Z
3. Prüfe auf Schnitte mit eventuellen Polygonen (Optimierung: vorher entscheiden was getroffen werden könnte)
4. Polygon des näher gelegenen Schnittpunkts wählen
5. Farbe des Punktes bestimmen
6. Pixelfarbe setzten
7. Beginne mit nächsten Pixel

Damit hast du an sich immer ein korrektes Ergebnis. Wie gesagt, es kann langsam sein und wird eigentlich nur beim Rendern von komplexen aber vorher optimierten Szenen genutzt.


----------



## Noctarius (21. Jan 2010)

Soulfly hat gesagt.:


> Ich stimme auch zu, es geht so nicht.
> 
> Aber ich gebe dir mal einen anderen Denkanstoß. Und zwar Mathematik.
> 
> ...



Dir ist schon klar, dass der Jung 13 vielleicht mittlerweile 14 ist?


----------



## Soulfly (21. Jan 2010)

Noctarius hat gesagt.:


> Dir ist schon klar, dass der Jung 13 vielleicht mittlerweile 14 ist?



Und warum will er dann eine 3D-Engine schreiben. Er will sich mit 3D beschäftigen, dann soll er das doch. Auch wenn es sich halt, wie bei meinem Beitrag oben, um einen Raytracing-Ansatz handelt. Dann muss er halt ein bißchen Wikipedia *abrat* oder besser ein Buch lesen.


Edit: Tutorials oder andere Arbeiten im Netz tun es auch


----------



## Marco13 (21. Jan 2010)

An sich ist das ja nicht schlimm. Isoliert betrachtet könnte man es sogar als lobenswert bezeichnen. Sich mit 14 in seiner Freizeit nicht mit GTA4 auf der Playstation3 zu beschäftigen, sondern mit der Frage, was Homogene Koordinaten, der Bresehnham-Algorithmus oder eben ein Z-Buffer ist, könnte eigentlich bewirken, dass derjenige ein potentieller zweiter John Carmack wird. 

Aber...

(sorry, den Vergleich find ich jetzt selbst ein bißchen lustig  )

... das ganze hat inzwischen schon System. Wie läuft's eigentlich mit diesem RayMan-Spiel?


----------



## Evil-Devil (22. Jan 2010)

Developer_X hat gesagt.:


> Ich weiß, aber so perfekt will ich es nicht haben, ich will nur grob sagen könne, dass ein objekt vor einem anderen ist, verstehst du?
> Wie mach ich das für x und y ?


Ähm, wieso gibst du dem Objekt nicht einfach eine Bounding Box mit und fragst dann die jeweiligen Objekte, ob sich ihre Bounding Box überschneidet oder wie weit sie von einander entfernt stehen. Das sollte die Aufgabe eigentlich auch lösen.


----------



## Developer_X (22. Jan 2010)

Marco13 hat gesagt.:


> An sich ist das ja nicht schlimm. Isoliert betrachtet könnte man es sogar als lobenswert bezeichnen. Sich mit 14 in seiner Freizeit nicht mit GTA4 auf der Playstation3 zu beschäftigen, sondern mit der Frage, was Homogene Koordinaten, der Bresehnham-Algorithmus oder eben ein Z-Buffer ist, könnte eigentlich bewirken, dass derjenige ein potentieller zweiter John Carmack wird.
> 
> Aber...
> 
> ...



Ich habs abgebrochen, weil ich eine eigene 3D-Egine nur für die Zwecke die ich brauche, machen will.

So viel nur mal dazu


----------



## Developer_X (22. Jan 2010)

OK ich sehe nun ein, es ergibt keinen sinn, entweder perfekt oder gar nicht. Ich will euch ein Bild von meiner eigenen ISS-Station zeigen, die is noch nicht fertig, nur die sonnen kollektoren und der Japaner Kasten vorne, deren Farb- und Koordinaten- Werte von einem eigenen Modellloader eingelesen werden.

Wie ihr sehne könnt sind die 3 Bilder aus unterschiedlichen Perspektiven geschossen worden.

Schaut nun auf das 2te und das 3te bild.

Ich habe auf dem 3ten bild aus der selben Perspektive geschossen wie aus der, des 2ten bildes, nun aber, habe ich die figur translatiert, und nun kann man sehen, dass dieses "Röhren Kreuz" seltsamer und unnatürlicher weise vor den Solar Panels zu sehen ist, das ist mein Problem.


----------



## Developer_X (22. Jan 2010)

Zu meinem Graphik system.
Es ist wie folgt aufgebaut:
Aus einem Graphics3D objekt
und aus einem Object_3D.

Das ganze funktioniert so:
Ich habe in Graphics3D Methoden wie
drawTriangle, drawPoint, drawLine, ...
Bei jeder dieser "draw" Methoden wird das selbe getan:
Ein Object_3D zu dem Array hinzu gefügt.

Jedes Object_3D ist wie folgt aufgebaut:

```
package graphics;

import java.awt.*;
import java.awt.geom.GeneralPath;
import java.util.Arrays;

import vector_math.Point3D;

public class Object_3D 
{
//	Attributes
	Point3D[] points;
	
	int state = 0;
	public double x = 0;
	public double y = 0;
	public double z = 0;
	
	public static final int FILL  = 2;
	public static final int LINE  = 1;
	public static final int POINT = 0;

	Color color;
	
	Graphics2D gr;
	
	public Object_3D(Point3D[] points,Color color, int state, Graphics2D gr)
	{
		this.points = points;
		this.color  = color;
		this.state  = state;
		this.gr     = gr;
		
		generateX();
		generateY();
		generateZ();
	}
	public void generateX()
	{
		double[] back = new double[points.length];
		for(int i = 0;i<back.length;i++)
			back[i] = points[i].x;
		Arrays.sort(back);
		
		x = back[back.length-1];
	}
	public void generateY()
	{
		double[] back = new double[points.length];
		for(int i = 0;i<back.length;i++)
			back[i] = points[i].y;
		Arrays.sort(back);
		
		y = back[back.length-1];
	}
	public void generateZ()
	{
		double[] back = new double[points.length];
		for(int i = 0;i<back.length;i++)
			back[i] = points[i].z;
		Arrays.sort(back);
		
		z = back[back.length-1];
	}
	public void drawObject()
	{
		gr.setColor(color);
		
		if(state == 0)
		{
			for(int i = 0;i<points.length;i++)
				gr.fillOval(points[i].get_X_Point_2D(),points[i].get_Y_Point_2D(),4,4);
		}
		else if(state == 1)
		{
			if(points.length == 2)
			{
				gr.drawLine(points[0].get_X_Point_2D(),points[0].get_Y_Point_2D(),
							points[1].get_X_Point_2D(),points[1].get_Y_Point_2D());
			}
			if(points.length == 3)
			{
				gr.drawLine(points[0].get_X_Point_2D(),points[0].get_Y_Point_2D(),
							points[1].get_X_Point_2D(),points[1].get_Y_Point_2D());
				gr.drawLine(points[0].get_X_Point_2D(),points[0].get_Y_Point_2D(),
							points[2].get_X_Point_2D(),points[2].get_Y_Point_2D());
				gr.drawLine(points[1].get_X_Point_2D(),points[1].get_Y_Point_2D(),
							points[2].get_X_Point_2D(),points[2].get_Y_Point_2D());
			}
			if(points.length == 4)
			{
				gr.drawLine(points[0].get_X_Point_2D(),points[0].get_Y_Point_2D(),
							points[1].get_X_Point_2D(),points[1].get_Y_Point_2D());
				gr.drawLine(points[1].get_X_Point_2D(),points[1].get_Y_Point_2D(),
							points[2].get_X_Point_2D(),points[2].get_Y_Point_2D());
				gr.drawLine(points[2].get_X_Point_2D(),points[2].get_Y_Point_2D(),
							points[3].get_X_Point_2D(),points[3].get_Y_Point_2D());
				gr.drawLine(points[3].get_X_Point_2D(),points[3].get_Y_Point_2D(),
							points[0].get_X_Point_2D(),points[0].get_Y_Point_2D());
			}	
		}
		else if(state == 2)
		{
			if(points.length == 3)
			{
				GeneralPath gp = new GeneralPath();
				gp.moveTo(points[0].get_X_Point_2D(),points[0].get_Y_Point_2D());
				for(int i = 1;i<points.length;i++)
					gp.lineTo(points[i].get_X_Point_2D(),points[i].get_Y_Point_2D());
				gp.closePath();
				gr.fill(gp);
				gr.draw(gp);
			}
			if(points.length == 4)
			{
				GeneralPath gp = new GeneralPath();
				gp.moveTo(points[0].get_X_Point_2D(),points[0].get_Y_Point_2D());
				for(int i = 1;i<points.length;i++)
					gp.lineTo(points[i].get_X_Point_2D(),points[i].get_Y_Point_2D());
				gp.closePath();
				gr.fill(gp);
				gr.draw(gp);
			}
		}
	}
}
```
Es wird dort farbe, punkte, und substanz gespeichert, und eine methode, mit der man dieses Objekt zeichnen kann, *drawObject*.

Nun dann, in der Klasse Graphics3D gibt es die Methode, *drawGraphics*, in der die Methode *drawObject* von allen Object_3Ds aufgerufen wird, die im Array drin sind, versteht ihr?

Nun dann, was könnte ich nun tun, um meine erste kleine eigene "3D-to-2D-Engine"(Ich nenne das so, weil man im grunde 3D Coordinates zu 2D Coordinates verarbeitet) zu verbessern, mit dem "Buffern". 

Developer_X

Braucht ihr noch mehr Material an code um zu sehen, was ich mache, versteht ihr auch wie das system funktioniert?
Wenn ihr es verstanden habt, wisst ihr sicher auch wie ihr mir helfen könntet.


----------



## Noctarius (22. Jan 2010)

Die Frage ist eher: Will sich Jemand mit deinen eigenen Code Conventions, die für dich ja so viel leichter sind, auseinander setzen? Du willst Hilfe, siehst es aber nicht ein den Leuten möglichst wenig Arbeit mit dem Code zu machen.

Ich finde das nach wie vor ätzend. Mir egal ob ich der Buhmann bin. Das ist unverschämt den Leuten gegenüber die dir echt lange lange versucht haben zu helfen.


----------



## Evil-Devil (22. Jan 2010)

Irgendwie sieht das was du dir da zusammengeschrieben hast unnötig kompliziert aus. Hast du dich schon mit einschlägiger Lektüre befasst? Hab daheim folgendes Buch: Computer Graphics for Java Programmers: Amazon.de: Leen Ammeraal, Kang Zhang: Englische Bücher
und da steht eine Menge hilfreicher Krams drin, wenn man Grafikprogrammierung betreiben will, da man auch genügend Hintergrundwissen vermittelt bekommt das man mit entsprechender weiterführender Lektüre vertiefen sollte.


----------



## Developer_X (22. Jan 2010)

OK, das sieht interessant aus, am Besten ich hole mir dieses Buch mal, lese es durch, ändere oder "update" meine kleine 3D-Graphik Engine einfach etwas, und wenn ich dann noch fragen 
habe wende ich mich an euch.

Aber erstmal lese ich dieses Buch hier:
Introduction to Computer Graphics: Using Java 2D and 3D Undergraduate Topics in Computer Science: Amazon.de: Frank Klawonn: Englische Bücher

Das ist erstens billiger, und fängt wirklich ganz von vorne an.

Und noch was, auch wenns mir keiner glauben will, aber ich habe schon vor langer zeit, anfang letzen Jahres habe ich 5 Java Bücher gelesen, alle für anfänger bis fortgeschrittene.

Developer_X


----------



## Developer_X (22. Jan 2010)

OK, ich hab jetzt die 2 Bücher zusammen in Bestellung gegeben, und werde sie aufmerksam "durch studieren" und hoffentlich eine Menge davon lernen.

Seit Jahres Beginn lese ich Bücher über Mathematik, um auf den Stoff der 12. Klasse zu kommen, weil das was ich hab, reicht vorne und hinten nicht, könnt ihr sicher verstehn, ein bisschen vom ersten Semester des Mathe Studiums, laut Buch, kommen auch vor.

Developer-X


----------



## tuxedo (22. Jan 2010)

Developer_X hat gesagt.:


> Und noch was, auch wenns mir keiner glauben will, aber ich habe schon vor langer zeit, anfang letzen Jahres habe ich 5 Java Bücher gelesen, alle für anfänger bis fortgeschrittene.



Du wirst es nicht glauben, aber ich glaub das nicht so wirklich. Nimms mir bitte nicht übel, aber entweder hast du sie gelesen, aber nicht verstanden, oder du bist nicht über den Buchrücken hinaus gekommen.


----------



## Noctarius (22. Jan 2010)

Developer_X hat gesagt.:


> Seit Jahres Beginn lese ich Bücher über Mathematik, um auf den Stoff der 12. Klasse zu kommen, weil das was ich hab, reicht vorne und hinten nicht, könnt ihr sicher verstehn, ein bisschen vom ersten Semester des Mathe Studiums, laut Buch, kommen auch vor.



Was glaubst du warum das Stoff der 12 und nicht der 7 ist?


----------



## Marco13 (22. Jan 2010)

Developer_X hat gesagt.:


> Ich habe auf dem 3ten bild aus der selben Perspektive geschossen wie aus der, des 2ten bildes, nun aber, habe ich die figur translatiert, und nun kann man sehen, dass dieses "Röhren Kreuz" seltsamer und unnatürlicher weise vor den Solar Panels zu sehen ist, das ist mein Problem.



Ich hab' mich ... mehrmals ... durch die Bilder geklickt, um nachzuvollziehen, was du meinst, glaube aber nun zu wissen, dass das genau der schon Anfangs angesprochene Punkt ist: Es bringt nichts, die Objekte zu sortieren. Es müssen nur Pixel, die weiter hinten liegen, verworfen werden. 

In diesem Bild ist das "Objekt" näher am Auge, und würde bei DIR dann VOR der Wand gezeichnet werden. Der mit XXX markierte Teil des Objektes ist aber HINTER der Wand - also nicht sichtbar.


```
/
              /
            / ___________
          /  |_________  | Objekt 
        /         __   | |
Auge  <)         |  |  |X|
        \        |  |  |X| 
          \      |  |  |X|
            \    |  |  |X|
              \  |  |
                \|  | 
                 Wand
```


----------



## Pfaeff (22. Jan 2010)

Der Trick beim Z-Buffer ist der folgende:

Du ermittelst den Abstand zwischen dem zu rendernden Punkt im Raum und dem Betrachter. 
Diesen Wert speicherst du in den Z-Buffer an die Position, wo sich dein Pixel auf dem Schirm befinden soll.
Angenommen du betrachtest den Punkt, der auf den Pixel (42, 266) abgebildet wird, dann machst du: 
	
	
	
	





```
zBuffer[42][266] = distance;
```
Wenn du jetzt ein weiteres Objekt rendern möchtest, berechnest du wieder den Abstand und vergleichst ihn mit dem, der im Z-Buffer gespeichert ist. 
Ist der Abstand kleiner, wird der Pixel überzeichnet. Ist er größer, wird der Punkt verworfen. Er hat den Tiefentest (Depth-Test) also nicht bestanden.

Was du bisher tust, ist die Objekte nach ihrer Z-Koordinate zu sortieren. Dies funktioniert aber, wie du bereits festgestellt hast, nur wenn man die Szene direkt von vorn betrachtet. Was du also tun musst, ist dies relativ zum Betrachter zu tun, wie ich es oben erklärt habe.


----------



## Developer_X (23. Jan 2010)

@Pfaeff
Danke für den Tipp, aber schau dir mal an, wie meine Engine funktioniert sag wie ich deinen tipp dort anwenden soll.

Developer-X


----------



## Guest2 (23. Jan 2010)

Der ZBuffer an sich ist nicht das Problem, sondern der Transformationsschritt davor. 

Angenommen Du hast eine Linie die von (-1, -1, -1) nach (+1, +1, +1) geht, dann liegen auf dieser Linie erstmal potentiell unendlich viele Punkte. Z.B. der Punkt (0.5, 0.5, 0.5) oder der Punkt (0.6, 0.6, 0.6) aber eben auch der Punkt (0.6001, 0.6001,  0.6001) usw.. Der ZBuffer arbeitet aber jetzt auf Pixel. Das heißt Du musst Deine Line (die eben aus unendlich vielen Punkten besteht) (von denen Du aber auch erstmal nur Start- und Endpunkt kennst) irgendwie auf ein Raster mit einer endlichen Auflösung abbilden (z.B. 1024x768). Eben rastern. Das passende Stichwort wurde ja schon erwähnt: Bresenham-Algorithmus ? Wikipedia

Für Linien ist das fast noch trivial, aber wenn ich Deinen Code oben richtig deute, willst Du auch beliebige Vielecke darstellen (diese müsstest Du dann wahrscheinlich erstmal in eine Menge von Dreiecken umwandeln  und hättest damit die nächste Baustelle).

Letztendlich müsstest Du folgendes abarbeiten (vereinfacht):

1.	3D Objekte einlesen
2.	3D Objekte in Dreiecke umwandeln
3.	Die 3 Eckpunkte pro Dreieck perspektivisch transformieren 
4.	Die Pixel zwischen den Eckpunkten berechnen (rastern)
5.	Den zum Pixel (x,y) gehörenden z Wert mit dem im ZBuffer vergleichen (bzw. setzen wenn er kleiner ist, sonnst Abbruch)
6.	Pixel (x,y) zeichnen

In der Gesamtheit ist das leider nicht einfach und auch nichts was sich mal eben in ein KSKB pressen lassen würde (zumindest käme es deutlich auf die Definition des ersten K an ).

Gruß,
Fancy


----------



## Marco13 (23. Jan 2010)

Man könnte (so als weiteren Dämpfer ) noch explizit sagen: Wenn man einen eigenen Z-Buffer verwenden will, kann die Funktionen, die vom Graphics angeboten werden (z.B. drawLine, fill* usw) natürlich vergessen. Man muss einzelne Pixel setzen. Mit Java würde man das am ehesten direkt in einem BufferedImage machen. 

Von den ersten Anfängen meiner Programmierzeit (damals alles mit C) weiß ich, dass man den Bresenham noch hinkriegt. Ein gefülltes Viereck zu zeichnen geht auch noch. Das Viereck dann mit einem Farbverlauf zu füllen, so dass die Farben der Pixel zwischen den Farben der Vertices interpoliert werden ("Gouraud-Shading", sozusagen  ) kann schon RICHTIG aufwändig werden, wenn man das auch im "schnellen Bresenham-Stil" machen will. Irgendwann hatte ich das zwar ansatzweise hingekriegt, aber das hat dann auch erstmal nur für EIN Viereck funktioniert. Ein zweites dazu? Neee.... Ich hab' mich dann doch lieber darauf verlassen, dass die Leutz das bei OpenGL auf der Grafikkarte _etwas_ flotter hinkriegen, und man statt 3000 Zeilen Code dann doch nur 3 Zeilen braucht, um ein Dreieck zu zeichnen


----------



## Pfaeff (23. Jan 2010)

Einfacher wird es wohl sein, einen Raytracer zu schreiben. 
Mit Echtzeit sieht es dann aber auch eher schlecht aus, aber ich glaube auch nicht, 
dass so ein Software-Renderer sonderlich schnell ist.


----------



## Marco13 (23. Jan 2010)

Naja... was heißt schon "Echtzeit". Die Tendenzen gehen Teilweise schon weg von "normalen" Rasterisierern hin zu Raytracern (und mit CUDA, OpenCL und Co ist sowas sogar handhabbar). Der Grund dafür ist recht einleuchtend: Inzwischen werden für viele Objekte und Szenen SO viele Dreiecke verwendet, dass es im sichtbaren Bereich tatsächlich mehr Dreiecke als Pixel gibt..........


----------



## Developer_X (23. Jan 2010)

Hey, wie wäre es, wenn ich einfach ein Array, aus sagen wir mal einer selbst erstellten Klasse namens Pixel mache, die Pixel klasse könnte so aussehen:

```
public class Pixel
{
public Color color;
public double z;
publix Pixel(Color color, double z)
{
this.color = color;
this.z = z;
}

}
```

Danach gehe ich die Arrays durch und sortiere sie, usw... und dann zeichne ich einfach kleine fillrects oder drawrects mit den farben aus der Klasse Pixel, wäre doch ein ansatzt oder?

Die einzige schwierigkeit für mich als Programmierer wäre dann nur noch auszurechnen, wo die Pixel halt sind, und die richtig zu erstellen, sowie das ganze zu überprüfen auf die z koordinaten.

Naja ich les jetzt erstmal mein Buch, Developer_x


----------



## Nobbs (23. Jan 2010)

Du brauchst eigentlich nur ein int[x][y] / float[x][y] array in dem du den aktuellen z-Wert des entsprechenden Pixels speicherst. Außerdem würde ich dir vorschlagen nicht mit fillRect zu arbeiten wenn du nur ein Pixel setzen willst, sondern z.B. über ein WritableRaster eines BufferedImages mit raster.setPixel(...) dein Bild zu zeichnen. Am Ende lässt du dann dein BufferedImage auf den Bildschirm zeichnen.


----------



## Developer_X (23. Jan 2010)

Das ist aber noch weit weit entfernt, erstmal brauche ich von euch Hilfe, für mich erscheint das schwierigste folgendes:
Das Rastern.

Wir Rastere ich?

OK: Stellen wir uns vor wir zeichnen eine Linie
sagen wir mal von 2 folgenden Punkten:
*
Point A (-1|0|0)
Point B (+1|+1|+1)
*
Nun habe ich ja eine Klasse Point3D die mir die 3D Coordinates in die 2D Coordinates parst.
Jetzt kommt der entscheidende Punkt : Soll ich das Mit dem Rastern vorher machen, also bevor ich die 3D Coordinates in die 2D Coordinates parse, oder danach.

Ich würde sagen danach, da ich danach ja die 2D Coodinaten kenne, und nun bequem x und y rastern kann, nur das z nicht.

Also ich weiß nicht wie ich das angehen soll.
Lasst mir mal Zeit, ich versuche mich daran, und wenn ich nicht mehr kann zeige ich euch einfach mal was ich so habe ok?

Developer_x


----------



## Developer_X (24. Jan 2010)

OK, ich hab jetzt nun eine Blockade, ich weiß net wie's weiter gehen soll.
Wirklich nicht.

Was soll ich jetzt machen?

Ich habe mal im Anhang den ganzen Code bereit gestellt.

Wäre einer von euch vielleicht bereit mal kurz drüber zu schauen?

Es geht darum, ich habe schon mal bisschen was zu den Pixeln gemacht, und auch eine Methode geschrieben, mit der ich die einzelnen Pixel zeichne.

Wie solls aber jetzt weiter gehen?

Ich habe die Methode nicht aufgerufen, sondern extra zu einem Kommentar gemacht, damit überhaupt so mal das ganze lenken könnt.

Ihr müsst nur die Main Class starten und könnt die ISS Station, die noch nicht fertig ist, etwas steuern.

Probierts mal aus, und danke wenn ihr mir helfen könntet.

Den Zip im Anhang müsst ihr nur in eure IDE kopieren, ich verwende Eclipse.

Danke
Developer_X


----------



## Marco13 (24. Jan 2010)

X3D ist schon belegt X3D - Wikipedia, the free encyclopedia Aber das interessiert dich wahrscheinlich ebensowenig wie naming conventions oder die clean-code-developer - Prinzipien.

Geh' davon aus, dass du NUR einzelne Pixel setzen kannst. Also graphics2D.drawLine, fillOval usw. fallen WEG. Nicht verwenden. Auch nicht ein bißchen. GAR nicht. Dann geh' davon aus, dass das ganze unerträglich langsam wird, wenn ein "Pixel" ein Objekt einer Klasse ist, die ein "Color"-Objekt enthält. Ein Pixel ist praktisch immer ein int, nicht mehr und nicht weniger. 

Wenn du bedenkst, dass auf Computer Graphics and Visualisation mal schlapp ein 1200seitiges und ein 500seitiges Buch auf 40 Bildschirmseiten gepresst wurden, und das (an sich schon fast "triviale" Konzept der) Z-Buffer dort nicht mal EINE Seite einnimmt, wirst du hoffentlich erkennen, dass es da noch einiges zu Berücksichtigen gibt....


----------



## Landei (24. Jan 2010)

Ich finde es schon etwas amüsant, wenn jemand versucht, ein Auto zu bauen, bevor er eines fahren kann. Brauchen wir noch eine 3D Engine? Nein. Wird sie jemals so gut sein wie vorhandene (Java3D, JME)? Nein. Also kann das Ziel nur darin bestehen, zu _lernen_. Aber lernen tut man erst einmal dadurch, dass man mit einer 3D-Engine halbwegs umgehen kann, um die Ideen zu begreifen und die Terminologie zu beherrschen, und nicht indem man versucht, selber eine zusammenzustümpern.


----------



## Developer_X (24. Jan 2010)

Marco13 hat gesagt.:


> X3D ist schon belegt X3D - Wikipedia, the free encyclopedia Aber das interessiert dich wahrscheinlich ebensowenig wie naming conventions oder die clean-code-developer - Prinzipien.
> 
> Geh' davon aus, dass du NUR einzelne Pixel setzen kannst. Also graphics2D.drawLine, fillOval usw. fallen WEG. Nicht verwenden. Auch nicht ein bißchen. GAR nicht. Dann geh' davon aus, dass das ganze unerträglich langsam wird, wenn ein "Pixel" ein Objekt einer Klasse ist, die ein "Color"-Objekt enthält. Ein Pixel ist praktisch immer ein int, nicht mehr und nicht weniger.
> 
> Wenn du bedenkst, dass auf Computer Graphics and Visualisation mal schlapp ein 1200seitiges und ein 500seitiges Buch auf 40 Bildschirmseiten gepresst wurden, und das (an sich schon fast "triviale" Konzept der) Z-Buffer dort nicht mal EINE Seite einnimmt, wirst du hoffentlich erkennen, dass es da noch einiges zu Berücksichtigen gibt....



Dann geb mir doch mal einen Tipp wie ich das Ganze umsetzen kann.



Landei hat gesagt.:


> Ich finde es schon etwas amüsant, wenn jemand versucht, ein Auto zu bauen, bevor er eines fahren kann. Brauchen wir noch eine 3D Engine? Nein. Wird sie jemals so gut sein wie vorhandene (Java3D, JME)? Nein. Also kann das Ziel nur darin bestehen, zu lernen. Aber lernen tut man erst einmal dadurch, dass man mit einer 3D-Engine halbwegs umgehen kann, um die Ideen zu begreifen und die Terminologie zu beherrschen, und nicht indem man versucht, selber eine zusammenzustümpern.



Ach ja?
Ich kann mit Java3D umgehen, wollte aber das Ganze selbst mal programmieren, nur um überhaupt zu verstehen wie die das gemacht haben.


----------



## Marco13 (24. Jan 2010)

Developer_X hat gesagt.:


> Dann geb mir doch mal einen Tipp wie ich das Ganze umsetzen kann.



Vollziehe die (für dich) (relevanten) Schritte der "Klassischen Rendering-Pipeline" nach. Überlege dann, wie man den ersten Schritt umsetzen kann (wissend, wie die übrigen Schritte aussehen werden).


----------



## Guest2 (25. Jan 2010)

Und nachdem Du Marcos Tipp befolgt hast, kannst Du Dir diese Tutorialserie durchlesen:

DevMaster.net - Software Rendering School: Part I
DevMaster.net - Software Rendering School, Part II: Projection
DevMaster.net - Software Rendering School, Part III: Triangle Rasterization
DevMaster.net - Software Rendering School, Part IV: Shading and Filling

Das Rasterisieren wäre dort Teil 3 (Du solltest aber alle Teile durchlesen!).

Anschließend kannst Du versuchen diesen Quelltext zu verstehen (bezieht sich nur auf Teil 3!):


```
package late.raster;

import java.awt.Graphics;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.util.Arrays;

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

@SuppressWarnings("serial")
public class Raster extends JPanel implements ComponentListener {

    public static final float[][] triangles = {

        // first triangle (x, y, z, r, g, b) 
        { 100,  50, 0.5f, 250,   0,   0 }, 
        { 500, 300, 0.7f,   0, 250,   0 }, 
        { 200, 500, 0.5f,   0,   0, 250 },

        //second triangle (x, y, z, r, g, b)
        { 500,  90, 0.1f,   0, 250,   0 }, 
        { 800, 150, 0.1f,   0,   0, 250 }, 
        { 100, 400, 0.7f, 250,   0,   0 }};

    
    private BufferedImage         image;
    
    private int                   width;
    private int                   height;

    int[]                         pixels;
    float[]                       zbuffer;


    public Raster() {

        super();
        image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
        addComponentListener(this);

    }


    @Override
    protected void paintComponent(final Graphics g) {

        super.paintComponent(g);
        synchronized (image) {

            g.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), null);

        }
    }


    @Override
    public void componentResized(final ComponentEvent e) {

        width = getWidth();
        height = getHeight();

        if (width != image.getWidth() || height != image.getHeight()) {
            synchronized (image) {

                image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
                pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
                zbuffer = new float[pixels.length];

                Arrays.fill(zbuffer, 1);

            }
        }

        draw();       

    }

    
    /*
     * draw our scene
     */
    public void draw() {

        for (int i = 0; i < triangles.length; i += 3)
            drawTriangel(triangles[i + 0], triangles[i + 1], triangles[i + 2]);

    }

    
    /*
     * draw a triangle (v1, v2, v3) with v = (x, y, z, r, g, b) 
     * x, y must be in screen space coordinates
     * z must be normalized (0 - 1)
     * z, r, g, b will be linear interpolated in screen space 
     */
    private void drawTriangel(float[] v1, float[] v2, float[] v3) {

        //sort v1.y < v2.y < v3.y, because we need a upper and lower triangle part
        if (v1[1] > v2[1]) {

            final float[] v = v1;
            v1 = v2;
            v2 = v;

        }

        if (v2[1] > v3[1]) {

            final float[] v = v2;
            v2 = v3;
            v3 = v;

        }

        if (v1[1] > v2[1]) {

            final float[] v = v1;
            v1 = v2;
            v2 = v;

        }

        // calculate delta x from a to b
        final float dx12 = (v2[0] - v1[0]) / (v2[1] - v1[1]);
        final float dx13 = (v3[0] - v1[0]) / (v3[1] - v1[1]);
        final float dx32 = (v2[0] - v3[0]) / (v2[1] - v3[1]);

        //calculate delta z from a to b
        final float dz12 = (v2[2] - v1[2]) / (v2[1] - v1[1]);
        final float dz13 = (v3[2] - v1[2]) / (v3[1] - v1[1]);
        final float dz32 = (v2[2] - v3[2]) / (v2[1] - v3[1]);

        //calculate delta r from a to b
        final float dr12 = (v2[3] - v1[3]) / (v2[1] - v1[1]);
        final float dr13 = (v3[3] - v1[3]) / (v3[1] - v1[1]);
        final float dr32 = (v2[3] - v3[3]) / (v2[1] - v3[1]);

        //calculate delta g from a to b
        final float dg12 = (v2[4] - v1[4]) / (v2[1] - v1[1]);
        final float dg13 = (v3[4] - v1[4]) / (v3[1] - v1[1]);
        final float dg32 = (v2[4] - v3[4]) / (v2[1] - v3[1]);

        //calculate delta b from a to b
        final float db12 = (v2[5] - v1[5]) / (v2[1] - v1[1]);
        final float db13 = (v3[5] - v1[5]) / (v3[1] - v1[1]);
        final float db32 = (v2[5] - v3[5]) / (v2[1] - v3[1]);

        
        // draw upper triangle part from v1 to v2
        final int signum12 = (int) Math.signum(v2[1] - v1[1]);
        for (int y = (int) v1[1]; y != (v2[1] + signum12); y += signum12) {

            final float d = y - v1[1];

            final int x1 = (int) (v1[0] + dx13 * d);
            final float z1 = (v1[2] + dz13 * d);
            final int r1 = (int) (v1[3] + dr13 * d);
            final int g1 = (int) (v1[4] + dg13 * d);
            final int b1 = (int) (v1[5] + db13 * d);

            final int x2 = (int) (v1[0] + dx12 * d);
            final float z2 = (v1[2] + dz12 * d);
            final int r2 = (int) (v1[3] + dr12 * d);
            final int g2 = (int) (v1[4] + dg12 * d);
            final int b2 = (int) (v1[5] + db12 * d);

            drawLine(y, x1, z1, r1, g1, b1, x2, z2, r2, g2, b2);

        }

        //draw lower triangle part from v3 to v2
        final int signum32 = (int) Math.signum(v2[1] - v3[1]);
        for (int y = (int) v3[1]; y != (v2[1] + signum32); y += signum32) {

            final float d = y - v3[1];

            final int x1 = (int) (v3[0] + dx13 * d);
            final float z1 = (v3[2] + dz13 * d);
            final int r1 = (int) (v3[3] + dr13 * d);
            final int g1 = (int) (v3[4] + dg13 * d);
            final int b1 = (int) (v3[5] + db13 * d);

            final int x2 = (int) (v3[0] + dx32 * d);
            final float z2 = (v3[2] + dz32 * d);
            final int r2 = (int) (v3[3] + dr32 * d);
            final int g2 = (int) (v3[4] + dg32 * d);
            final int b2 = (int) (v3[5] + db32 * d);

            drawLine(y, x1, z1, r1, g1, b1, x2, z2, r2, g2, b2);

        }

    }


    /*
     * draw a horizontal scan line from (x1, y, z1) to (x2, y, z2) with color (r1, g1, b1) to (r2, g2, b2)
     * x, y must be in screen space coordinates
     * z must be normalized (0 - 1)
     * z, r, g, b will be linear interpolated in screen space 
     */
    private void drawLine(final int y, final int x1, final float z1, final int r1, final int g1, final int b1, final int x2, final float z2, final int r2, final int g2, final int b2) {

        final float d = x2 - x1;
        final int signum = (int) Math.signum(d);

        final float dz = (z2 - z1) / d;
        final float dr = (r2 - r1) / d;
        final float dg = (g2 - g1) / d;
        final float db = (b2 - b1) / d;

        for (int x = x1; x != (x2 + signum); x += signum) {

            final float l = x - x1;
            final float z = z1 + dz * l;
            final int r = (int) (r1 + dr * l);
            final int g = (int) (g1 + dg * l);
            final int b = (int) (b1 + db * l);

            drawPixel(x, y, z, r, g, b);

        }

    }


    /*
     * draw a pixel (x, y, z) with color (r, g, b) with respect to zbuffer 
     * x, y must be in screen space coordinates 
     * z must be normalized (0 - 1)
     */
    private void drawPixel(final int x, final int y, final float z, final int r, final int g, final int b) {

        if (0 <= x && x < width && 0 <= y && y < height) {

            final int c = (r << 16) | (g << 8) | (b << 0);
            final int pixel = y * width + x;

            synchronized (image) {

                if (0 <= z && z < zbuffer[pixel]) {

                    pixels[pixel] = c;
                    zbuffer[pixel] = z;

                }
            }
        }
    }


    public static void main(final String[] args) {

        final Raster raster = new Raster();
        final JFrame frame = new JFrame();

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {

                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(raster);
                frame.setSize(800, 500);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

            }
        });
    }


    @Override public void componentHidden(final ComponentEvent e) { }
    @Override public void componentMoved(final ComponentEvent e) { }
    @Override public void componentShown(final ComponentEvent e) { }

}
```

Das ist imho die einfachste Art Dein Vorhaben zu realisieren.


@Pfaeff: Ja, ein Raytracer sollte einfacher sein, allerdings, damit das sinnvoll ist, braucht man ein paar Grundlagen aus der linearen Algebra. Und die fehlen ihm imho.

Gruß,
Fancy


----------



## Developer_X (25. Jan 2010)

Okay, danke euch allen. Ich werde nun folgendes tun:

Die Bücher sind heute angekommen, ich werde sie mir zunächst durchlesen.
Als nächstes werde ich die Tutorials durchlesen, die mir "guest2" gezeigt hat.
Dann werde ich mir hier noch einmal alle Posts ab der Seite 3 hier lesen,
und dann werde ich probieren das ganze noch mal zu realisieren.

Developer_X


----------



## Noctarius (25. Jan 2010)

So das heißt 2 Bücher und 3 Tutorials. Damit will ich dich hier mindestens 2 Wochen nicht sehen weil du liest. Du probierst nichts, postet kein neues Game, nichts. Dann glaube ich dir vielleicht, dass du die Dinger tatsächlich gelesen und nicht auf dem Klo überblättert hast.


----------

