# Eigene Renderengine ohne Java3D,etc.



## Matyr (1. Sep 2011)

Hi,
ich habe vor (nicht ganz freiwillig) eine komplett eigene Render-Engine in java zu bauen.
Diese soll (leider) nur mit Standert JDK Inhalte funktionieren.
Dabei zu sagen ist das ich relativ neu in Java bin, aber einige Jahre C++ Erfahrung habe.

Die Grundlagen  stehen schon, aber aktuell bricht mir leider die Performance massiv ein.

Grundlegender Aufbau des Renderns: 

- Sortiere eine Liste aller Objekte anhand der z (Entfernung) Koordinaten.
- Nun transformier Objekt-Space in World-Space
- Bilde das Objekt mit Perspektivischer Transformation auf die X-Y (Bildschirm) ebene ab
- Textur (die im RAM liegt) transformieren und auf ein Img "rendern" das hinterher angezeigt wird

Zeichnen von Quadratischen "einfachen" Objekten läuft über Graphics2D.draw,
das Transformierte zeichnen läuft über Pixelarrays (DataBufferInt).

Image Typ ist aktuell BufferedImage, alles läuft in einem Thread.

Der größte Geschwindigkeits verlust entsteht beim Zeichnen von Objekten (unteranderem weil Clipping fehlt)

Falls irgendjemand Tipps kennt wie man alles optiemieren kann würde ich mich freuen davon zu hören.

Einige Source Code beispiele:


Alle Objekte zeichnen und das fertige img zurückgeben, mit Caching wenn nichts geändert wurde.
BitmapBit arbeitet auf Pixelarrays

```
public BitmapJ2D tick(long frameCount)
	{		
				
		boolean up = false;
		frame.draw(old, 0, 0);
		frame.fill(0xff000000);
		
		BitmapBit BBit = null;
		
		java.util.Collections.sort(objects);			
			
		for(EntityBase itr : objects)
		{		
			itr.addAng(new Vector3(0,0.01,0));
			Vector3[] tmp = itr.toWorld();
			
			if(itr instanceof Plane)
			{
				//if(itr.getUpdate())
				{			
					if(BBit==null){BBit = new BitmapBit(WIDTH,HEIGHT,((DataBufferInt)((BufferedImage)frame.getImg()).getRaster().getDataBuffer()).getData());}
					
					for(int i=0;i<4;i++) { this.Project(tmp[i]);}				
				
					//System.out.println(tmp[0].toString()+ tmp[1].toString() + tmp[2].toString() + tmp[3].toString());
					//Transform draw 					
					BBit.drawTransform(((Plane)itr).getTopTexture(),tmp[0], tmp[1], tmp[2], tmp[3]);
					itr.cache(tmp);
					itr.setUpdate(false);
					up=true;
				}				
			}
		}
		if(up)
		{
			old.fill(0xff000000);
			old.draw(frame,0,0);
			up = false;
		}
		return frame;
	}
```

Falls weitere source Inhalte gebraucht werden bitte melden, ich wollt nicht alles vollposten...


----------



## Landei (1. Sep 2011)

Nur mit Java-Bordmitteln (also Java2D) ist harsch. Das wird nur für kleine Szenen klappen, ansonsten brauchst du irgendwie einen möglichst direkten Zugang zu OpenGL oder DirectX.


----------



## Matyr (1. Sep 2011)

Stimmt viele, Objekte parallel zeichnen geht nicht, aber durch einen Cache und begrentztem neuzeichnen von veränderten Objekten kann man was machen.

Ich habe heute was von VolatileImage gelesen lohnt es sich diese zu benutzen?


----------



## Landei (1. Sep 2011)

Soweit ich weiß kann das viel bringen, ist aber schweine-kompliziert, weil es eben jederzeit wieder freigegeben werden kann.


----------



## Matyr (1. Sep 2011)

Würde man dann alles ersetzen, also auch die Images die nur gelesen werden (texturen aus dateien),

oder nur das eine Image auf das geschrieben wird?

Mfg. Matyr


----------



## Cola_Colin (1. Sep 2011)

VolatileImage kann sehr sehr viel bringen, ist auch nur beim "malen" des Bildes wichtig, d.h. die Texturen können BufferedImage sein, das Lesen ist da trotzdem schon beschleunigt, schreiben hingegen läuft auf einem VolatileImage massiv schneller, wenn es den unterstützt wird.

Siehe http://java.sun.com/j2se/1.4/pdf/VolatileImage.pdf

So schwer ist das imho auch gar nicht zu nutzen.


----------



## Matyr (2. Sep 2011)

1)
Kann ich irgendwie auf das Pixel Array in VolatileImage zugreifen?

Ich muss für das Perspektivische Zeichnen ziemlich unregelmäßige Figuren in das Image bekommen, aktuell läuft das über ein BufferedImage und kopiert dies in das Volatile hinein.


2)
Ich suche auch noch nach einen schnellen Alghoritmus für die Texture-transformation ( Mapping) um die Rechteckigen Texturen auf die Figuren zu bekommen, kennt irgendwer einen?


----------



## Matyr (3. Sep 2011)

Drei weitere sachen, ich hab jetzt alles auf VolatileImage geändert, aber die Funktion 

```
public void draw()
	{
		
		BufferStrategy bs = getBufferStrategy();
		if (bs == null) {
			createBufferStrategy(2);
			return;
		}		
		
		Graphics g = bs.getDrawGraphics();	
		Graphics2D g2 = (Graphics2D)g;
		
		g2.drawImage(screen.getImg(),0,0,getWidth(),getHeight(),null);
		g2.dispose();
		bs.show();
	}
```

ist irgendwie seeeeehr langsam, sobald ich drawImage auskommentiere habe ich einen Geschwindigkeits gewinn von 4 auf >100fps.
Weiß einer was da falsch laufen kann? Da das Image mit 

```
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
		GraphicsConfiguration gc = ge.getDefaultScreenDevice().getDefaultConfiguration();
		VolatileImage image = null;

		image = gc.createCompatibleVolatileImage(width, height, transparency);
```

erzeug ist sollte es ja eigentlich keine Probleme geben.

2) Ich male aktuell in ein Canvas, das sollte von der geschwindigkeit im normallen bereich liegen, oder gibt es etwas drastisch schnelleres? Ich nutze keine repaint sondern einfach ein endlos loop der immer neu zeichnet.

3) Ist es effetkiver nicht immer image objekte zu übergeben sonder Graphics2D objecte?

Mfg. Matyr


----------



## Marco13 (3. Sep 2011)

Hab' nicht alles verfolgt, aber

```
g2.drawImage(screen.getImg(),0,0,getWidth(),getHeight(),null);
```
Ist das 'Img' dort genau getWidth*getHeight groß? Wenn nicht, wird es bei jedem Zeichnen skaliert. Ändere das mal zu

```
g2.drawImage(screen.getImg(),0,0,null);
```
und passe stattdessen falls nötig die Größe von "Img" an.


----------



## Matyr (3. Sep 2011)

Ok, das war das Problem auch wenn ich nicht verstehe wieso das vergrößern so eine laufzeit hat.Danke

Trotzdem habe ich noch mit BufferedImage eine bessere laufzeit als mit VolatileImage


----------



## EgonOlsen (6. Sep 2011)

Landei hat gesagt.:


> Nur mit Java-Bordmitteln (also Java2D) ist harsch. Das wird nur für kleine Szenen klappen, ansonsten brauchst du irgendwie einen möglichst direkten Zugang zu OpenGL oder DirectX.


Ich verstehe jetzt nicht so ganz, wie die Texturen hier gerendert werden (es wäre schön, wenn man das Ding mal als Ganzes zum selber ausführen sehen könnte), aber du kannst mit Software-Rendering in Java schon einiges machen. Du musst dich nur von dem ganzen Java2D-Geraffel verabschieden und die Pixel "von Hand" in ein BufferedImage malen. Ein kleines Beispiel dazu als Applet: http://www.jpct.net/quapplet/


----------



## twseitex (6. Sep 2011)

Hi,

ein Gegenbeispiel: Mein Java-Audioplayer ht eine GUI, die
die Fensterstruktur und Elemente als variable Bausteine nutzt. Also eine
Fensterklasse und deren Elemente. Es können mehrere Fenster - eigentlich
beliebig viele - gerendert werden. Alle Fensterlemente wie Buttons sind
optional, z.T. animiert (eigene Threads) und im Container gekapselt
und werden dynamisch erzeugt (daher optional). Graphic2D ist parallel
implementiert. Aduio sowieso parallel. Alles wird von einer anderen
Klasse synchronisert. Also 3 parallele Renderaktionen mit Synchron
(1x Audio, 1x Fenster, 1x Graphic2D in einem Fensterlemenent).

War eine Schweine Arbeit, läuft, aber Java frisst Ressourcen endlos.
Ich würde es nie wieder machen, das zu programmieren.

Mit anderen Worten: Überzeuge denjenigen, der die Rendermaschine in
Java verlangt, Fertiges zu verwenden, das optimiert ist. Java3D ist leider
uralt. Und möglichst nur hoch optimierte Fertigbausteine, wenn
mit Bytecode realisiert.


Mit createVolatileImage() habe ich folgende reizende Erfahrung gemacht:

    #
    # An unexpected error has been detected by Java Runtime Environment:
    #
    #  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x02e7920c, 
                                                       pid=3584, tid=3492
    #
    # Java VM: Java HotSpot(TM) Client VM (11.3-b02 mixed mode, 
                                                      sharing windows-x86)
    # Problematic frame:
    # C  [awt.dll+0x3920c]
    #
    # If you would like to submit a bug report, please visit:
    #   HotSpot Virtual Machine Error Reporting Page
    # The crash happened outside the Java Virtual Machine in native code.
    # See problematic frame for where to report the bug.
    #
    */


----------



## Matyr (7. Sep 2011)

Selber ausführen wird aktuell schwierig. Ich hab aktuell aufgrund der Laufzeit auf isometrische Perspektive gesetzt, daher keine Drehung in die Tiefe. 

Intern werkelt noch an allem die selbe engine.

Texturen werden wie folgt gezeichnet: es werden alle Pixel der Textur Datei durchlaufen und mit entsprechenden Matrix Operationen auf den Monitor projiziert. Wenn die Textur zu groß ist gibt es massive Performance Einbrüche, wenn sie zu klein ist löcher auf dem Objekt, daher nicht gerade ideal.

Aber wie gesagt ich werde wahrscheinlich jetzt isometrische Perspektive nehmen


----------



## 0x7F800000 (7. Sep 2011)

1) Wieso wird irgendwas nach der Tiefe sortiert? GraKa's verwenden Z-Stencil-Buffer, und arbeiten auf Pixel-Ebene. Ansonsten kannst du nicht mal zwei sich schneidende Dreiecke malen
2) Java2D-Kram ist wohl nicht nötig, wenn du eh alles selbst zeichnest: mal also alles direkt in ein int-array rein
3) Bei jedem halbwegs sinnvoll implementierten Programm dieser Art dürfte es _keinerlei_ Unterschiede zwischen der isometrischen Perspektive und der normalen Perspektive geben: dafür muss doch nur ein einzelner eintrag in der Projektionsmatrix geflippt werden
4) Texturen werden nicht aus Dateien gezeichnet. Wenn du das schon nicht alles auf der grafikkarte ausführst, dann müssen die Texturen wenigstens im Speicher liegen.
5) es wird nicht von der Textur auf Bildschirm sondern vom Bildschirm auf die Textur gemappt, mit der inversen Homographie und zusätzlich einem bilinearen filter & MIP-Maps.


----------



## Matyr (7. Sep 2011)

- Java2D kram ist meiner ansicht nach deutlich schneller als das beschreiben eines int-Arrays.
(Meines wissens sollte das ja direkt optimiert als maschinencode vorliegen)

- Nach tiefe sortieren tue ich, weil dies für meine problemstellung ausreicht und deutlich weniger performance zieht als aufwendige methoden ( und ich mich ehrlich gesagt nicht gut mit solchen clipping alghoritmen auskenne)

- Den begriff inverse Homographie habe ich bis jetzt noch nicht gehört werde aber mal suchen ob ich was entsprechendes finde.

- Meine Texturen liegen im RAM, wahrscheinlich habe ich micht dabei nicht klar genug ausgedrückt.


----------



## 0x7F800000 (7. Sep 2011)

Matyr hat gesagt.:


> - Java2D kram ist meiner ansicht nach deutlich schneller als das beschreiben eines int-Arrays.


Okay... kommt darauf an, was du davon benutzst, und wie du es benutzst

Wenn du irgendetwas pixel-für-pixel zeichnest, dann passiert das ganze nicht auf der graka, sondern im normalen ram, mit normalen Prozessor. Da macht es wenig sinn, durch drei wrapper-ebenen hindurch die gleichen einträge eines stinknormalen arrays anzusprechen.



> - Den begriff inverse Homographie habe ich bis jetzt noch nicht gehört werde aber mal suchen ob ich was entsprechendes finde.


Naja, die perspektivische Verzerrung einer Textur bildet geraden auf geraden ab, ist also eine Homographie, und kann dementsprechend durch eine 3x3-Matrix dargestellt werden. Diese kann man invertieren, um von Bildschirmkoordinaten zu Texturkoordinaten zu kommen. Dann läuft man alle Pixel durch, findet ihre textur-Koordinaten mithilfe der inversen Matrix raus, und liest die Farbe der Textur an dieser Stelle ab, beispielsweise mithilfe der bilinearen Interpolation.



> - Meine Texturen liegen im RAM, wahrscheinlich habe ich micht dabei nicht klar genug ausgedrückt.


Okay, das war voll korinthenkackig, sorry


----------



## EgonOlsen (7. Sep 2011)

0x7F800000 hat gesagt.:


> Naja, die perspektivische Verzerrung einer Textur bildet geraden auf geraden ab, ist also eine Homographie, und kann dementsprechend durch eine 3x3-Matrix dargestellt werden. Diese kann man invertieren, um von Bildschirmkoordinaten zu Texturkoordinaten zu kommen. Dann läuft man alle Pixel durch, findet ihre textur-Koordinaten mithilfe der inversen Matrix raus, und liest die Farbe der Textur an dieser Stelle ab, beispielsweise mithilfe der bilinearen Interpolation.


Ja, kann man so machen...muss man aber zu Glück nicht, denn es wird viel zu langsam für jedes Pixel mit einer Matrix rumzufummeln. Man braucht keine Matrizen an dieser Stelle. Mit den Texturkoordinaten u/v und der Tiefe z an den Eckpunkten eines Dreiecks hat man alles, was man für perspektivisch korrekte Texturen braucht. Ich habe das vor Jahren mal kurz in diesem Thread beschrieben: http://www.java-forum.org/softwareentwicklung/14211-funktioniert-eigentlich-texture-mapping.html#post88556


----------



## 0x7F800000 (7. Sep 2011)

Wieder was dazugelernt... Okay, aus performancegründen schreibt man das nicht als matrix hin, sondern ein wenig "direkter", samt den ganzen Bruchstrichen, und macht es nicht für alle Pixel, sondern für wenige punkte, die über das dreieck verstreut sind: gut. Mir ging es eigentlich weniger um Matrizen, sondern um die Richtung Bild -> Textur, statt andersherum.


----------



## Matyr (8. Sep 2011)

Ok, ich guck mir das ganze mal an und probier dann mal drauf los  




> Okay... kommt darauf an, was du davon benutzst, und wie du es benutzst
> 
> Wenn du irgendetwas pixel-für-pixel zeichnest, dann passiert das ganze nicht auf der graka, sondern im normalen ram, mit normalen Prozessor. Da macht es wenig sinn, durch drei wrapper-ebenen hindurch die gleichen einträge eines stinknormalen arrays anzusprechen.



Zum malen der Hintergrundbilder und Interfaces/Menüs benutze ich vorallem Java2D, aufgrund der großen fläche (Vollbild) ist dies schneller als wenn ich ein Array mit z.B. 1680*1050 Elementen durchlaufe und für jeden Pixel eine Funktion aufrufe die die Farbe richtig berechnet (aufgrund von RGBA).


----------



## 0x7F800000 (8. Sep 2011)

Nja, man muss ja die 3D-Grafik nicht _sofort_ in dasselbe Bild wie die ganzen restlichen knöpfe reinquetschen, man kann's vielleicht auch erstmal auf einem separaten bild mit transparenten Hintergrund malen, und dann zum rest hinzufügen... Wobei ich ehrlich gesagt keine Ahnung habe, wozu du das ganze eigentlich machst, ich kenne 3D-Grafik eigentlich nur von 3D-Spielen und komplizierten Funktionsplottern: nichts davon enthält normalerweise irgendwelche Swing-Knöpfe in der selben komponente. Da ich das Ziel nicht kenne, halte ich mich ab jetzt ein wenig zurück.


----------

