Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

41

09.09.2015, 20:26

@GPU Ansatz
Ja das geht schon. Pixel in einer Textur kannst du mit Compute Shadern (zB. OpenCL/CUDA/DirectCompute) setzen. Oder du renderst eben nach wie vor Quads und nimmst Shader. Damit kann man die Interpolation der Texturkoordinaten anpassen.

Es besteht vermutlich bei der aber momentan auch noch viel Optimierungspotential auf der CPU.

@MitgliedXYZ
Tritt das Problem beim Lesen oder Schreiben von Daten auf?
Zeig doch mal den Code mit dem du das Bitmap lockst und darauf zugreifst.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Spiele Programmierer« (09.09.2015, 21:25)


MitgliedXYZ

Alter Hase

  • »MitgliedXYZ« ist der Autor dieses Themas

Beiträge: 1 369

Wohnort: Bayern

  • Private Nachricht senden

42

09.09.2015, 20:49

Gute Frage, ich Lese und Schreibe gleichzeitig in zwei Bitmaps:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
 public Bitmap quadDistort(Bitmap disortBitmap, Bitmap background, Vec2[] vectoren)
        {
            BitmapData dataNeu = background.LockBits(new Rectangle(0, 0, background.Width, background.Height), ImageLockMode.ReadWrite, ProjectionObject.PIXEL_FORMAT);
            int stride = dataNeu.Stride;

            BitmapData dataOriginal = disortBitmap.LockBits(new Rectangle(0, 0, disortBitmap.Width, disortBitmap.Height), ImageLockMode.ReadOnly, ProjectionObject.PIXEL_FORMAT); //PixelFormat.Format24bppRgb
            int strideOriginal = dataOriginal.Stride;

            int width = background.Width;
            int height = background.Height;

            Vec2 a = vectoren[0];
            Vec2 b = vectoren[1];
            Vec2 c = vectoren[2];
            Vec2 d = vectoren[3];

            unsafe
            {
                byte* ptr = (byte*)dataNeu.Scan0;
                byte* ptrOriginal = (byte*)dataOriginal.Scan0;

                for (int y = 0; y < height; y++)
                {
                    for (int x = 0; x < width; x++)
                    {

                        Vec2 vecOriginalBild = invBilinear(new Vec2((float)x, (float)y), a, b, c, d);
                        vecOriginalBild.x = vecOriginalBild.x * width;
                        vecOriginalBild.y = vecOriginalBild.y * height;
                        if (vecOriginalBild.x >= 0 && vecOriginalBild.y >= 0 &&
                            vecOriginalBild.x < width && vecOriginalBild.y < height)
                        {
                            //neuesBild.SetPixel(x, y,
                            //    original.GetPixel((int)vecOriginalBild.x, (int)vecOriginalBild.y));

                            int xO = (int)vecOriginalBild.x;
                            int yO = (int)vecOriginalBild.y;

                            ptr[(x * 3) + y * stride + 2] = ptrOriginal[(xO * 3) + yO * stride + 2]; //R
                            ptr[(x * 3) + y * stride + 1] = ptrOriginal[(xO * 3) + yO * stride + 1]; //G
                            ptr[(x * 3) + y * stride + 0] = ptrOriginal[(xO * 3) + yO * stride + 0]; //B
                        }
                        else
                        {
                            //neuesBild.SetPixel(x, y, Color.Black);

                            ptr[(x * 3) + y * stride + 2] = 1; //R
                            ptr[(x * 3) + y * stride + 1] = 1; //G
                            ptr[(x * 3) + y * stride + 0] = 1; //B
                        }
                    }
                }
            }
            background.UnlockBits(dataNeu);
            disortBitmap.UnlockBits(dataOriginal);

            return background;
        }


Zum Testen hab ich mal den LogBitmap Teil auskommentiert und wieder SetPixel(), getPixel() verwendet. Dann funktioniert es, ist aber langsamer:


Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »MitgliedXYZ« (09.09.2015, 21:12)


Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

43

09.09.2015, 22:06

Du verwendest bei ptrOriginal die Variable stride anstatt strideOriginal. ;)
Zum folgen des Codes wäre es übrigens auch sehr angenehm, wenn du bei einer Namenskonvention bleiben würdest. (Original vs background)

Ich würde außerdem stark davon abraten, dass PixelFormat bei LockBits variabel zu machen. Wenn PIXEL_FORMAT irgendeinen anderen Wert enthält, geht die Pixeladressierung total in die Hose und im schlimmsten Fall, hast du bei einem kleineren Format sogar einen BufferOverflow. (Unsafe!) Setz es am Besten einfach auf Format32bppArgb. Bei einem Bild, dass keinen Alphakanal enthält, werden die Daten dann einfach ignoriert. Ein weiterer Vorteil davon wäre, dass du dann statt mit einzelnen Bytes mit Uints arbeiten kannst. Das ist praktischer und schneller. Wenn du alle Pixel überschreibst, kannst du ImageLockMode.WriteOnly anstatt ImageLockMode.ReadWrite angeben, dass ist unter Umständen ebenfalls schneller.

MitgliedXYZ

Alter Hase

  • »MitgliedXYZ« ist der Autor dieses Themas

Beiträge: 1 369

Wohnort: Bayern

  • Private Nachricht senden

44

09.09.2015, 23:08

Oh, danke das hab ich übersehen.
Auf die Namenskonvention werde ich mehr achten, in dem aktuellen Programm hab ich Englisch und Deutsche Variablen Namen stark gemischt :S

Das PixelFormat für neue Bitmaps habe ich mir bereits als Konstante angelegt:

C#-Quelltext

1
public static System.Drawing.Imaging.PixelFormat PIXEL_FORMAT = System.Drawing.Imaging.PixelFormat.Format32bppArgb;

Aber das kann ich nur benutzen, wenn ich neue Bitmaps (die dann der Methode als "background" übergeben werden) erstelle, das "disortBitmap" lade ich aus einer .Jpg Datei, die kann vermutlich unterschiedliche Pixel_Formate enthalten.

Wenn ich jetzt aber im geänderten Quellcode "(x * 3)" durch "(x * 4)" ersetze, bleibt das Bild schwarz. Lasse ich "(x * 3)" stehen, sieht es so aus:




Das Bild wird trotz gleichen Koordinaten kleiner Dargestellt und die Farben sind komisch. Morgen teste ich noch ob es dann wohl doch an dem evtl. falschen Format von dem "distortBitmap" liegt.

Hab gerade noch deinen Tipp getestet, ImageLockMode.ReadOnly ist am schnellsten, der Geschwindigkeitsunterschied ist deutlich. WriteOnly ist langsamer, und schreiben der Pixelwerte funktioniert auch bei ReadOnly. Kann da was beschädigt werden, oder ist das ein Bug der Bitmap Klasse?

SlinDev

Treue Seele

Beiträge: 142

Wohnort: Lübeck

Beruf: Programmierer

  • Private Nachricht senden

45

10.09.2015, 00:17

Für OpenGL meinte ich einen einzelnen Quad in der Größe des Rendertargets rendern und dann deine Funktion im Shader implementieren. Alternative in einem Computeshader mit OpenCL, dann hast du eventuell weniger von dem Overhead von OpenGL, musst aber direkter mit den Texturdaten hantieren. Außerdem könnte die Ausführung des Codes mit OpenGL eventuell etwas schneller sein als mit OpenCL, sollte sich aber eigentlich nicht viel tun.
Die Shadertoy Demo von dem Link aus dem die Funktion stammt tut genau das was ich als Shaderumsetzung mit OpenGL meine.

Dein erstes Bild zeigt wie es aussehen soll und wie es mit SetPixel aussieht? Und das Zweite ist dein aktuelles Ergebnis wenn du die Daten direkt veränderst?
Dass es kleiner wird und dass die Farben kaputt gehen liegt beides beides noch daran dass da irgendwas mit den Strides oder so nicht stimmt. Vielleicht solltest du das Verzerren erstmal weglassen und stattdessen in deiner Schleife das Bild Pixel für Pixel in das neue kopieren. Sobald das funktioniert weißt du dass alles stimmt und kannst dann die Verzerrung einbauen.

ReadOnly klingt wirklich nicht als ob man da reinschreiben sollte, dass es trotzdem funktioniert muss nicht unbedingt ein Bug sein, sondern kann auch ein Nebeneffekt der Implementierung sein. Ich würde es eher nicht machen. Aber wenn es nur bei dir funktionieren muss und es das auch fehlerfrei tut und der Geschwindigkeitsvorteil merkbar ist, könntest du es natürlich trotzdem machen.

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

46

10.09.2015, 01:12

Überprüfe, dass du wirklich an allen Stellen auf * 4 umgestellt hast und gleichzeitig überall Format32bppArgb zu verwendest.
Die Streckung in X Richtung um den Faktor 3/4 und die Farbverschiebung spricht für mich sehr dafür, dass du *4 das beim Schreiben vergessen hast. Auf der rechten Hälfte scheint es fehlerhafte Daten zu übergeben, die nicht überschrieben wurden.
Das als Konstante außerhalb der Funktion auszulagern, halte ich in dem Fall tatsächlich für eine schlechte Idee.

Und in ReadOnly solltest du nicht und unter keinen Umständen hineinschreiben. Es ist nicht dokumentiert was das genau auslöst und vielleicht hat es unangenehme Nebeneffekte von denen du nichts weißt oder welche die nur bei bestimmten Bildern oder Hardware existieren. Und wer weiß, vielleicht ändert Microsoft zukünftig seine Implementierung und dann funktioniert es nicht mehr.

MitgliedXYZ

Alter Hase

  • »MitgliedXYZ« ist der Autor dieses Themas

Beiträge: 1 369

Wohnort: Bayern

  • Private Nachricht senden

47

10.09.2015, 13:01

Wenn ich "width" durch "widthDistort" in Zeile 28 und für height das selbe in Zeil 29 austausche funktioniert es jetzt. Aber ich hab immer noch "(x * 3)", statt "(x * 4)", trotz des PixelFormats.

Allerdings hab ich jetzt gemerkt, dass der Algorithmus nur funktioniert, wenn höchstens zwei Innenwinkel des Vierecks 90° haben. Bei einem Rechteck bleibt wieder alles schwarz. Wenn ich aber einen Vektor, z.b. d um 1px verschiebe, wird wieder etwas dargestellt.
Das kann ich zwar beheben, indem ich alle Innenwinkel vorher berechne und falls alle gleich 90° sind (oder ich teste nur drei Winkel auf 90°, der vierte muss es ja dann auch sein, selbst optimiert :rolleyes: ), zeichne ich das Bild direkt mit der GDI Funktion, ohne die quadDistort() Methode.

ReadOnly ersetze ich dann wieder bei dem einem Bild durch WriteOnly, das zweite wird nur gelesen. Nicht das es doch noch Nebeneffekte gibt, die erst später sichtbar werden.

Wieso ist die globale Konstante mit dem PixelFormat eine schlechte Idee, wirkt sich das auf die Performance aus? Der Vorteil dadurch ist, dass ich mich so mit dem PixelFormat nicht mehr vertippen kann, da ich jetzt die Konstante benutzen kann.

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

48

10.09.2015, 13:44

Also du solltest auf jeden Fall * 4 bei RGBA benutzen. Wenn du * 3 verwendest, dann stimmt irgendetwas ganz und gar nicht. Ich habe auch schon mit LockBits gearbeitet habe, und das hat bei mir mit uint und RGBA immer gut funktioniert. Wenn du weiter nur mit * 3 zurechtkommst, solltest du vielleicht doch nochmal den aktuellen Code zeigen.

Das Problem mit dem Innenwinkel kommt vermutlich daher, dass in der invBlinear-Methode dann Zahlen wie Unendlich oder NaN auftreten und daher die Berechnung scheitert. Die beste Lösung wäre es wahrscheinlich, wenn man mal überprüfen würde, wo genau das auftritt.

MitgliedXYZ

Alter Hase

  • »MitgliedXYZ« ist der Autor dieses Themas

Beiträge: 1 369

Wohnort: Bayern

  • Private Nachricht senden

49

10.09.2015, 14:28

Das mit dem Rechteck ist kein Problem, die Innenwinkel kann ich selbst überprüfen und zum zeichnen ohne Verzerrung ist die GDI Funktion schneller.

Aber das mit dem *4 funktioniert nicht. Dieser Code hier generiert nur komplett schwarze Bilder:
http://pastebin.com/z6jL3YZY

Dieser Code mit *3 und "PixelFormat.Format24bppRgb" bei "BitmapData" funktioniert, obwohl die Bitmaps als "Format32bppArgb" vorliegen:
http://pastebin.com/vgt8EY9S

Den Fehler warum das Bild oben schwarz bleibt, kann ich nicht finden. Hab das PixelFormat der Bitmaps kontrolliert, bei beiden ist es "Format32bppArgb", trotzdem funktioniert es nur mit *3 und BitmapData PixelFormat.Format24bppRgb, obwohl das eigentliche Bitmap ein anderes Format hat. Ändere ich das Bitmap Format und den Faktor auf *4, bleibt trotzdem alles schwarz.

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

50

10.09.2015, 16:28

Du schreibst auch nur Rot, Grün und Blau. Du solltest auch auch Alpha (4. Byte) schreiben. Es ist möglich, dass in deinem Bild ursprünglich die Deckkraft 0 ist. Formate die keine Transparenz unterstützen (wie JPEG), werden wahrscheinlich mit schwarz als Hintergrundfarbe gespeichert. Schwarz mit einem Bild ohne Deckkraft darüber bleibt dann schwarz.

Wenn du mit *3 arbeitest verschieben sich die Farbdaten und landen auch in Alpha. Eigentlich müsste das aber zu einem verzerrten Bild und Fehlfarben führen.

Um das kompakter und schneller zu gestalten, schlage ich uints vor:
((uint*)ptr)[x + y * (stride / 4)] = ((uint*)ptrOriginal)[xO + yO * (strideOriginal / 4)]; //ABGR: alles aufeinmal

Werbeanzeige