OpenGL Library - Eure Vorschläge!

Muepe32

Erfahrenes Mitglied
Hallo zusammen,

Hier im Forum gab es in der letzten Zeit erfreulicherweise vermehrt Postings in meinem Lieblingsthema, der 3D-Programmierung. Ich selbst bin da hauptsätzlich in DirectX aktiv aber habe natürlich auch Erfahrung mit OpenGL. Da ich jemand bin, der sehr gerne Libraries für andere schreibt und das auch präferiert mit Unterstützung von .NET habe ich mir gedachte ich entwickle auch mal eine kleine Bibliothek um OpenGL in .NET verwenden zu können. Das Verwenden an sich war ja eine leichte Aufgabe (P-Invoke) und bisschen abtippen, daher habe ich mich entschlossen das ganze etwas auszuweiten.

So gliedert sich das ganze momentan eigentlich in 3 Level:
1. Einfach alle Funktionen importiert, die zu OpenGL gehören sowie dynamisches Laden diverser sehr oft verwendeter Extensions (sofern sie vorhanden sind).

2. Diverse Wrapperklassen, die es gerade für Einsteiger leichter machen, da viele Kontrollen vorhanden sind und so Fehler gleich erkannt werden können. Von den meisten Klassen gibt es verschiedene Versionen mit verschiedenen "Sicherheitsstufen". So verlässt sich ein UncheckedVertexBuffer völlig darauf, dass der User weiss, welches die richtigen Eingaben sind, ein VertexBuffer jedoch prüft alles (natürlich mit der entsprechenden Performanceverbrennung) noch nach.

3. Ein UserControl, das einem die Erstellung des Kontexts und andere nützliche Dinge in einfacher Weise zur Verfügung stellt.

So kann eigentlich dann jeder für sich bestimmen, bis zu welchem Level er gerne Unterstützung haben möchte.

Nun auch noch ein paar Spezifikationen, was denn bisher so alles vorhanden ist (ich bin erst seit 3 Tagen dran, ist daher noch nicht alle Welt ;)):
Texturen
- Die Klasse Texture2D kann Texturen laden aus Dateien, dem Speicher, einer Resource in der Exe oder allgemein einem Stream (Netzwerkstream, ...). Unterstützt werden dabei die Formate PNG, JPG, GIF, TIFF, EXIF und BMP. Für DDS (DirectDraw Surface) sind auch die gängigsten Umwandlungen bereits vorhanden (inklusive der komprimierten Formate).
- Die Klasse SpriteList bietet die Möglichkeit einfach 2D-Objekte zu cachen und dann in einem Batch zu zeichnen. Dabei werden Modi wie AlphaBlend und ColorBlend unterstützt. Ein Beispiel wäre folgendermassen:
C#:
            texture = Texture2D.FromImageFile(@"C:\Users\Admin\Desktop\Beatle-Icon.png");
            list = new SpriteList();
 
            list.CaptureBatch(SpriteFlags.ColorBlend);
            list.DrawTexture(Vector2.Zero, texture);
            list.EndBatch();

Ein Aufruf der Form list.BatchDraw zeichnet nun all das, was zwischen CaptureBatch und EndBatch aufgenommen wurde.

Primitive
Für das Zeichnen von Primitiven habe ich Teile der Konzepte von DirectX als Inspiration genommen, da ich diese eigentlich äusserst komfortabel finde.

Wesentlich für das Zeichnen sind grundsätzlich dabei 2 Klassen:
IndexBuffer
VertexBuffer

Der VertexBuffer hält Positionsdaten, Farbdaten, Texturkoordinaten und Normalen. Da es jedoch mühsam (und teilweise auch einfach unpassend) ist diese immer alle anzugeben ist das natürlich auch nicht nötig. Es wird dabei ein Konzept ähnlich dem FVF in DirectX verwendet. Man definiert structs, welche durch Attribute angeben, welche Daten wo und wie vorhanden sind. Beispielsweise folgende struct definiert einen Vertex der Position und Farbe hat:
C#:
    [StructLayout(LayoutKind.Sequential)]
    [VertexElementPosition(ElementType.Vector3, 0)]
    [VertexElementColor(ElementType.Vector3, 12)]
    public struct VertexPositionColor
    {
        public float X, Y, Z;
        public float R, G, B;

        public VertexPositionColor(float x, float y, float z, GLColor color)
        {
            X = x;
            Y = y;
            Z = z;
            R = color.R / 255.0f;
            G = color.G / 255.0f;
            B = color.B / 255.0f;
        }
    }

Durch die Attribute kann man angeben, um wie viele Komponenten es sich handelt und wie das Offset vom Anfang der struct zum ersten Element dieser Kategorie ist. Dadurch kann man theoretisch auch unzählige andere Dinge reinpacken, man muss nur sagen, wo die richtigen Daten zu finden sind. Interessant ist es zum Beispiel auch, wenn man in obigem Beispiel für die Farbe ein Offset von 0 angibt, dann wird die Position als Farbe verwendet, was durchaus lustige (und schöne) Ergebnisse bringt.

Solche Vertices kann man nun in den VertexBuffer einspeisen. Auch das geht denkbar einfach:
C#:
            buffer = new VertexBuffer();
            VertexPositionColor[] vertices = new VertexPositionColor[]
            {
                new VertexPositionColor(-1, 1, 0, Color.Green),
                new VertexPositionColor(-1, -1, 0, Color.Red),
                new VertexPositionColor(1, -1, 0, Color.Blue),
                new VertexPositionColor(1, 1, 0, Color.Chartreuse),
            };

            buffer.SetData(vertices);

Wer nun keine Indices verwenden möchte kann den Inhalt des Vertexbuffers einfach über DrawElements zeichnen lassen (mit entsprechendem Offset und Anzahl Vertices bzw Primitivetype).

Wer aber Indices verwenden möchte zum Zeichnen kann nun noch einen IndexBuffer definieren. Da ist nicht wahnsinnig viel zu sagen, ein Beispiel hier:
C#:
            indexBuffer = new IndexBuffer();
            byte[] indices = new byte[6]
            {
                0, 1, 2,
                0, 2, 3
            };

            indexBuffer.SetIndices(indices);

VertexBuffer bietet dann die Methode DrawIndexedElements welche einen IndexBuffer erwartet.

Alles zusammen sähe dann beispielsweise so aus:
C#:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using OpenSL.GL.Wrappers.Drawing;
using OpenSL.GL.Wrappers.Textures;
using OpenSL.GL.Wrappers.Objects;
using OpenSL.GL.Wrappers;

namespace TestApplication
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            glControl1.RenderMode = OpenSL.UserControls.RenderMode.Timer;
            glControl1.OnRender += new Action(glControl1_OnRender);
        }

        Texture2D texture;
        VertexBuffer buffer;
        IndexBuffer indexBuffer;
        SpriteList list;

        void glControl1_OnRender()
        {
            buffer.DrawIndexedElements(indexBuffer, PrimitiveType.TriangleList);
            list.BatchDraw();
        }

        private void Form1_Shown(object sender, EventArgs e)
        {
            texture = Texture2D.FromImageFile(@"C:\Users\Admin\Desktop\Beatle-Icon.png");
            list = new SpriteList();
 
            list.CaptureBatch(SpriteFlags.ColorBlend);
            list.DrawTexture(Vector2.Zero, texture);
            list.EndBatch();

            buffer = new VertexBuffer();
            VertexPositionColor[] vertices = new VertexPositionColor[]
            {
                new VertexPositionColor(-1, 1, 0, Color.Green),
                new VertexPositionColor(-1, -1, 0, Color.Red),
                new VertexPositionColor(1, -1, 0, Color.Blue),
                new VertexPositionColor(1, 1, 0, Color.Chartreuse),
            };

            indexBuffer = new IndexBuffer();
            byte[] indices = new byte[6]
            {
                0, 1, 2,
                0, 2, 3
            };

            indexBuffer.SetIndices(indices);

            buffer.SetData(vertices);
        }
    }
}

Mit folgendem Ergebnis:
img1vb.jpg


(Oder mit dem Offset der Farbe auf 0 gestellt:
img2su.jpg
)

Nun meine Frage an euch:
Habt ihr Vorschläge, spontane Ideen, Kommentare oder Meinungen zu dem gezeigten (oder auch anderem)? Wenn ja, bitte postet diese doch, denn ich merke, dass mir das Projekt sehr viel Spass macht und habe daher auch vor da noch einiges zu verwirklichen :)

Viele Grüsse und besten Dank schon jetzt
Muepe
 

Adrian_Broher

Erfahrenes Mitglied
Wieso sollte man deiner Library denn den Vorzug geben im Gegensatz zum Tao Framework oder zum Open Toolkit? Ansonsten fällt mir die harte Verdrahtung mit der FFP auf, die mit dem OpenGL 3.2 Core Profile schon tot ist.
 
Zuletzt bearbeitet:

Muepe32

Erfahrenes Mitglied
Hi Adrian,

Zum momentanen Zeitpunkt gibt es wohl kaum wirklich relevante Vorzüge, allerdings ist es auch nicht so, dass ich als Ziel habe andere Bibliotheken "auszustechen", ich setze viel mehr einfach meine Sicht der Dinge und meinen Approach um und wer da ähnlich denkt wird wohl auch Freude daran haben.

Du hast recht, momentan sind die Wrapper noch sehr stark an der FFP orientiert, das liegt aber einfach daran, dass Klassen für Shaderprogramme momentan auf der To-Do-Liste und nicht auf der Implemented-Liste sind, jedoch sind die Utilities dafür das was ich wohl als nächstes umsetzen werde. Schritt für Schritt ;).

Gruss
Muepe