Dienstag, 13. Juni 2017

DIY: Emulator Teil 2.1_3: Zeichnen auf dem Emulatorbildschirm

Die Funktionen und Variablen hier (Teil 2.1_2) haben wir benötigt, damit die initialize-Funktion hier (Teil 2.1_1) benutzt werden kann.

Nun kommen wir zu den spassigen Funktionen: DrawPixel und Konsorten.

Ich muss dazu sagen, dass ich ein grafischer Typ bin. Ich brauch was fürs Auge. Vielleicht schliesse ich nach diesem Teil auch ab mit Emulator-ik und mache erstmal ein bisschen Demo-Code? Mal sehen...ich überlege jedenfalls schon, die ganze Engine hier auf Shader umzustellen. PixiJS kann das. Wenn ich nur den jeweils geshadeten Pixel mit einem Screen-Array Wert verknüpfen könnte (position rausfinden? JS array im shader?) ....

...

Diese Funktionen sind ziemlich einfach. Darum werde ich hier auch eine erste (sich bewegende) Grafikdemo(-Funktion) programmieren, damit es auch mal was für die Augen gibt. Das kann man dann für die Ladebildschirme oder andere Emulator-externe Dinge gebrauchen. Beim Sega Master System  habe ich ein Rauschen wie bei einem alten Fernseher programmiert: Einfacher Effekt mit grosser Wirkung (siehe Teil 1, das zweite Bild). Das kannst du ganz einfach machen, indem du das Array mit Zufallswerten füllst. Hier werden wir einen Plasma-Effekt programmieren. Der ist ganz wenig komplizierter als ein Rauschen, macht aber auch mehr her. :)

Wir benötigen nun erst mal die Funktionen um überhaupt mit dem Bildschirm zu arbeiten. Also fügen wir die folgenden Dinge zu EmuGraphicsAdapter.js hinzu:

Helferchen


// Ausserhalb der Klasse: Ein paar Helper-Funktionen
var BLUE = function(color) {return color  & 0xFF;}
var GREEN = function(color) {return (color >> 8) & 0xFF;}
var RED = function(color) {return (color >> 16) & 0xFF;}
var RGB = function(red, green, blue)
{
    return ((red & 0xFF) << 16) | ((green & 0xFF)<<8) | (blue & 0xFF);
}


Hier wird ein bisschen Bit-geshiftet und binär verknüpft.

Bei BLUE wird einfach die gegebene Farbe mit binärem UND mit 0xFF verknüpft.
0xFF sind 8 Einsen, also acht gesetzte Bits. Wenn man etwas binär mit Und verknüpft, ist es nur 1, wenn beide Bits 1 sind. Die Zahl 5 (binär 00000101) mit 4 (00000100) verknüpft ergibt somit 4, da nur das 4er Bit bei beiden Zahlen Eins ist. Mit 0xFF kriegt man also das letzte Byte, die letzten 8 Bit eines Wertes heraus.

Bei GREEN und RED wird erst der Wert an die die letzte Stelle verschoben (Bit-shifting): Rot ist 16 Bit, 2 Byte vom Ende "entfernt", Grün nur 8 Bit. Darum werden erst die Bits um 16 bzw. 8 Stellen nach rechts verschoben ( >> ) und dann wieder mit 0xFF "isoliert".

RGB macht genau dasselbe umgekehrt: Es nimmt die gegebene Zahl, isoliert das letzte Byte davon und schiebt diese Bits an die gewünschte Position. Mit dem binären ODER ( | ) können nun alle diese Bits aufeinander "addiert" werden. Da jede Farbe an die gewünschte Bit-Position verschoben wurde, und genau 8 Bit "breit" ist dank der Isolation durch 0xFF,  überschreibt auch keine Farbe eine andere.

Zeichnen

In der EmuGraphicsAdapter-Klasse fügen wir nun die wichtigsten Zeichen-Funktionen hinzu:


// color one specific pixel by array index.
this.pixelIndex = function(arrIndex, color)
{
if(arrIndex >= 0 && arrIndex < screenArray[doubleBufferIndex].length)
screenArray[doubleBufferIndex][arrIndex].tint = color;
}

// color one specific pixel.
this.pixel = function(x,y,color)
{
var z = y * emuScreenWidth + x;
this.pixelIndex(z, color);
}


Mit diesen zwei Funktionen wird ein spezifischer Pixel im aktuellen Backbuffer mit einer Farbe "gefüllt".


// copy a color array to the screen.
this.arrayToScreen = function(arr)
{
var maxZ = arr.length;
if(screenArray[doubleBufferIndex].length<maxZ)
maxZ = screenArray[doubleBufferIndex].length;
for(var z=0;z < maxZ;z++)
{
this.pixelIndex(z,arr[z]);
}
}


Diese Funktion kopiert den Inhalt eines Arrays auf den Backbuffer.

// uses the array values as palette index.
this.arrayFromPaletteToScreen = function(arr, paletteArray)
{
var maxZ = arr.length;
if(screenArray[doubleBufferIndex].length<maxZ)
maxZ = screenArray[doubleBufferIndex].length;
var paletteSize = paletteArray.length;
for(var z=0;z < maxZ;z++)
{
this.pixelIndex(z, emuBackgroundColor);
var a = arr[z] % paletteSize; // modulo the value through paletteSize
this.pixelIndex(z,paletteArray[a]);
}
}


Diese Funktion kopiert wie die obere Funktion den Inhalt eines Arrays auf den Backbuffer. Doch anstatt die Werte des Arrays direkt zu applizieren, werden diese als Index auf der mitgegebenen Palette benutzt. Die Palette ist einfach ein Array beliebiger Grösse mit allen benutzten Farben darin. Der Wert im arr-Array wird mit Modulo an die Palettengrösse angepasst. Wir benötigen diese Funktion unbedingt für den Plasma-Effekt. ;)


// returns an array in the size of the screen.
// filled with the backbuffer content.
this.screenToArray = function()
{
var arr = [];
for(z=0;z < screenArray[doubleBufferIndex].length; z++)
{
arr.push(screenArray[doubleBufferIndex][z]);
}
return arr;
}


Mit dieser Funktion kann man sich ein Array in der Grösse des Bildschirms "ziehen". Auf diesem kann man dann herumzeichnen und es dann wieder mit den obigen zwei Funktionen auf dem Bildschirm anzeigen. Wir benötigen dies, um das Basis-Bild des Plasma-Effektes zu erstellen.

Zusätzlich wird hier der Inhalt des Backbuffers auf das Array kopiert. Somit kriegen wir eine Kopie des aktuell im Hintergrund gezeichneten Bildschirms.

Da ich vielleicht die Engine auf Shader umstelle, werde ich für den Plasma-Effekt einen eigenen, neutralen Artikel schreiben. Ansonsten hätte ich ihn jetzt hier in diesem Artikel gepostet.

Für den Klassen-externen Plasmaeffekt brauchen wir schliesslich dann noch die Grösse des EmulatorBildschirms:


// In der EmuGraphicsAdapter-Klasse:
this.screenWidth = function() {return emuScreenWidth;}
this.screenHeight = function() {return emuScreenHeight;}


Hier ist der Source-Code für diesen Teil. Der Plasmaeffekt ist hier auch schon drin als eigene Datei, wird aber in einem anderen Artikel behandelt und bis dahin auch noch verbessert. Die index.html wurde leicht daran angepasst.

https://github.com/ben0bi/EmulatroniX/releases/tag/Blog_Series_Part_2.1_3

Du solltest nun in der Lage sein, deinen Emulatorbildschirm zu benutzen.

In Demo 1: Plasma wird der erwähnte Plasmaeffekt erklärt.

Es gibt noch Anpassungen zum Code nach Teil 2.1_2.

Viel Spass damit.

Keine Kommentare:

Kommentar veröffentlichen