[Solved] Bitmap in OpenGL - Code funktioniert aber extrem langsam



  • Hallo.

    Ich bin total enttäuscht. Ich habe es nach vielen Tagen endlich geschafft, einen beliebigen String in C# in ein Bitmap 256x256 zu verwandeln, dann zu trimmen (alle Zeilen vor, nach unter und oberhalb des Strings fliegen raus um das Bitmap klein zu halten) und nun in meinem OpenGL Fenster auszugeben. Ich möchte dabei Knotennummern bei einem Gitternetz (1000 < n < 10000) ausgeben. Für die Bewegung habe ich eine Maussteuerung implementiert.

    Wenn ich jetzt die Knotennummern für lediglich 60 Knoten anzeigen lasse, geht mein Rechner (Xeon 4x3.2GHz mit NVidia 7300 GTX Karte)total in die Knie.

    Meine Frage: Wie würde ihr so etwas machen? Einfach Text ausgeben scheint ja in OpenGL nicht zu gehen. Die Methode mit dem Bitmap, von der man öfters hört, scheint ja nicht all zu performat zu sein - oder mache ich evtl. etwas falsch beim Rendern?

    Es wäre schön, wenn einer von Euch mal draufschaut, denn ich habe mit OGL keine Erfahrung und vielleicht auch total falsche Einstellungen gesetzt oder bin falsch rangegangen.

    Anbei der Code, in dem meine Zeichenfläche neu gezeichnet wird und darunter dann der Code für die Bitmap übersetzung - und Darstellung. Übrigens: Die Bitmaps sind wunderbar. Ich habe sie als Datei rausschreiben lassen - also der Teil des Codes funktioniert.

    private void MainOGL_Paint(object sender, EventArgs e)
    {
     Struct_cs.TBoundingBox B = new Struct_cs.TBoundingBox();
                Struct_cs.TElement* P = null;
    
                Gl.glClear(Gl.GL_COLOR_BUFFER_BIT);
                Gl.glClearColor((float)0.0, (float)0.0, (float)0.0, (float)0.0); //Background color
                Gl.glViewport(0, 0, Width, Width);
    
                if (DisplaySolid) { Gl.glPolygonMode(Gl.GL_FRONT_AND_BACK, Gl.GL_FILL); } else { Gl.glPolygonMode(Gl.GL_FRONT_AND_BACK, Gl.GL_LINE); }
    
                foreach (CMesh M in _pMesh)
                {
                    if (M.Visible == true)
                    {
                        B.xmin = System.Math.Min(M.MeshBox.xmin, B.xmin);
                        B.xmax = System.Math.Max(M.MeshBox.xmax, B.xmax);
    
                        B.ymin = System.Math.Min(M.MeshBox.ymin, B.ymin);
                        B.ymax = System.Math.Max(M.MeshBox.ymax, B.ymax);
    
                        B.zmin = System.Math.Min(M.MeshBox.zmin, B.zmin);
                        B.zmax = System.Math.Max(M.MeshBox.zmax, B.zmax);
    
                        if (DisplayLine == true)
                        {
                            for (int fi = 0; fi < M.ExtFamily.Count; fi++)
                            {
                                if (M.ExtFamily[fi].Visible == true && DisplayLine == true)
                                {
                                    P = M.ExtFamily[fi].P_First;
                                    for (ulong fj = 0; fj < M.ExtFamily[fi].nElement; fj++)
                                    {
                                        DrawElement(P);
                                        P++;
                                    }
                                    /*Jetzt wird der Text dargestellt*/
                                    if (DisplayExternalNodeNumber)
                                    {
                                        P = M.ExtFamily[fi].P_First;
                                        Gl.glPolygonMode(Gl.GL_FRONT_AND_BACK, Gl.GL_FILL);
                                        for (ulong fj = 0; fj < M.ExtFamily[fi].nElement; fj++)
                                        {
                                            Ogl_Test.OGL_Text.AddText(P->Node[0]->ExternalNumber.ToString(), Color.Blue, Color.Transparent, 0.0, 0.0, 1.0, 0.02);
                                        }
                                        Ogl_Test.OGL_Text.AddText(P->Node[1]->ExternalNumber.ToString(), Color.Blue, Color.Transparent, P->Node[1]->P.x, P->Node[1]->P.y, 1.0, 0.02);
                                        if (!DisplaySolid) { Gl.glPolygonMode(Gl.GL_FRONT_AND_BACK, Gl.GL_LINE); }
                                    }
                                }
                            }
                        }
    
                        if (DisplayElement == true)
                        {
                            for (int fi = 0; fi < M.IntFamily.Count; fi++)
                            {
                                if (M.IntFamily[fi].Visible == true)
                                {
                                    P = M.IntFamily[fi].P_First;
                                    for (ulong fj = 0; fj < M.IntFamily[fi].nElement; fj++)
                                    {
                                        DrawElement(P);
                                        P++;
                                    }
                                }
                            }
                        }
                    }
               }
    

    Textur erstellen in C#

    public static void AddText(string text, Color color, Color backcolor, double x, double y, double scale, double BitScale)
            {
                const int side = 256;
                Bitmap texture = new Bitmap(side, side, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                Bitmap textureTrimmed;
    
                Graphics g = Graphics.FromImage(texture);
    
                using (Brush brush = new SolidBrush(backcolor))
                {
                    g.FillRectangle(brush, new Rectangle(Point.Empty, texture.Size));
                }
    
                using (Font font = new Font(SystemFonts.DialogFont.FontFamily, 12f))
                {
                    SizeF sz = g.MeasureString(text, font);
                    double f = side / Math.Max(sz.Width, sz.Height) * scale;
                    g.TranslateTransform(side / 2 + (float)f * sz.Width / 2, side / 2 - (float)f * sz.Height / 2);
                    g.ScaleTransform(-(float)f, (float)f);
                    using (Brush brush = new SolidBrush(color)){g.DrawString(text, font, brush, 0, 0);}
                }
    
                textureTrimmed = TrimBitmap(texture, 20, 95, 98, true, System.Drawing.Color.Transparent);
                textureTrimmed.RotateFlip(RotateFlipType.RotateNoneFlipX);
                textureTrimmed.RotateFlip(RotateFlipType.RotateNoneFlipY);
    
                texture.Save("C:\\HomeC\\Test.bmp");
                textureTrimmed.Save("C:\\HomeC\\TestTrimmed.bmp");
    
                LoadTexture(textureTrimmed, false, x, y, BitScale);
    
            }
    

    Textur skalieren mit C#

    private static Bitmap TrimBitmap(Bitmap texture, int left, int bottom, int top, bool TrimRight, System.Drawing.Color BackGround)
            {
                int right = 0, fi = 0, fj = 0;
    
                if (TrimRight)
                {
                    bool Found = false;
                    for (fi = texture.Width - 1; fi > 0; --fi)
                    {
                        for (fj = top; fj < texture.Height - bottom; fj++)
                        {
                            if (texture.GetPixel(fi, fj) != System.Drawing.Color.FromArgb(0))
                            {
                                Console.WriteLine(texture.GetPixel(fi, fj).ToString());
                                Found = true;
                                break;
                            }
                        }
                        if (Found) { break; }
                    }
    
                    right = texture.Width - fi;
                }
                else
                {
                    right = 0;
                }
    
                Bitmap B = new Bitmap(texture.Width - left - right + 2, texture.Height - top - bottom);
    
                for (fi = left; fi <= texture.Width - right + 1; fi++)
                {
                    for (fj = top; fj < texture.Height - bottom; fj++)
                    {
                        B.SetPixel(fi - left, fj - top, texture.GetPixel(fi, fj));
                    }
                }
                return B;
            }
    

    Und die Darstellung in C#

    private static int LoadTexture(Bitmap texture, bool mipmaped, double x, double y, double BitScale)
            {
    
                int id;
                Gl.glGenTextures(1, out id);
                Gl.glBindTexture(Gl.GL_TEXTURE_2D, id);
                int wt = texture.Width;
                int ht = texture.Height;
    
                Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER,Gl.GL_LINEAR);
                Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER,Gl.GL_LINEAR);
    
                System.Drawing.Imaging.BitmapData data = texture.LockBits(
                    new Rectangle(0, 0, wt, ht),
                    System.Drawing.Imaging.ImageLockMode.ReadOnly,
                    System.Drawing.Imaging.PixelFormat.Format32bppRgb);
    
                Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA, wt,ht, 0, Gl.GL_BGRA, Gl.GL_UNSIGNED_BYTE, data.Scan0);
    
                texture.UnlockBits(data);
    
                wt = (int)((double)wt * BitScale);
                ht = (int)((double)ht * BitScale);
    
                Gl.glMatrixMode(Gl.GL_MODELVIEW);
                Gl.glEnable(Gl.GL_TEXTURE_2D);
                Gl.glColor3f(1.0f, 1.0f, 1.0f);
                Gl.glBegin(Gl.GL_QUADS);
                Gl.glTexCoord2d(0, 0); Gl.glVertex2d(x, y);
                Gl.glTexCoord2d(1, 0); Gl.glVertex2d(x+wt, y);
                Gl.glTexCoord2d(1, 1); Gl.glVertex2d(x+wt, y+ht);
                Gl.glTexCoord2d(0, 1); Gl.glVertex2d(x, y+ht);
                Gl.glEnd();
                Gl.glDisable(Gl.GL_TEXTURE_2D);       
    
                return id;
            }
    


  • texture.Save("C:\\HomeC\\Test.bmp");
                textureTrimmed.Save("C:\\HomeC\\TestTrimmed.bmp");
    
                LoadTexture(textureTrimmed, false, x, y, BitScale);
    

    Rechner (Xeon 4x3.2GHz mit NVidia 7300 GTX Karte)total in die Knie.

    Und wie schnell ist deine Festplatte die du hier die ganze Zeit beschreibst und liest?

    CJens schrieb:

    Ich bin total enttäuscht.

    Kopf hoch, du wirst besser mit der Zeit.



  • Generier' beim Programmstart 1x eine Textur wo alle nötigen Zeichen drin sind (sollte wenn es nur um Nummern geht ja easy sein).
    Wenn du's dir einfach machen willst nimmst du dazu nen Fixed-Font, dann musst du keine unterschiedlichen Zeichenbreiten berücksichtigen.

    In der OpenGL Zeichenschleife verwendest du diese Textur dann einfach nur mehr.
    Also 1x Textur auswählen und dann für jeden Buchstaben den du zeichnen willst einen Quad mit den passenden Texturkoordinaten rendern.

    Und Falls du in anderen Programmteilen noch andere Texturen verwendest, dann solltest du FBOs verwenden. Sonst musst du nämlich erst wieder dauernd glTexImage2D aufrufen, was auch halbwegs bremst. (Oder die Textur in eine Display-List packen, aber keine Ahnung wie gut das aktuellen OpenGL Implementierungen "verstehen".)



  • Erstmal vielen Dank für die Antworten.

    Das mit dem Speichern der Bitmaps auf dem Rechner war nur für den Debug gedacht um zu sehen, was er da zeichnet. Ich hatte vergessen, das rauszunehmen.

    Das mit der Fixed Font gefällt mir, ich denke, ich werde es so machen.
    Also, für Nummern 0 bis 9 das ganze mit Quads oder Tris (ist es wirklich so, dass die Grafikkarte mit Dreiecken viel besser klar kommt?) nachbilden und wenn ich wirklich mal was schreiben muss mit Buchstaben, was nur vereinzelt der Fall sein wird, dann mache ich das über Texturen, die ich außerhalb des Renderns erstelle.

    Vielen Dank für die Tipps.

    Eine Frage hätte ich aber noch: Wie machen "die Profis" so etwas?



  • Vielen Dank für den Tip, funktioniert mit der FixedFont super.

    80 000 Knoten mit Nummern und dynamischer Maussteuerung - ruckelfrei 🙂


Log in to reply