Bruk av OpenGL i .Net/C#
glut/minimum
Glut [2] er et enkelt vindussystem som kort sagt lar oss sette opp et enkelt vindu og programmere grafikk i det. Glut ble opprinnelig skrevet som et verktøy for c-programmerere i "vindusløse" omgivelser. Vi fraskriver oss alle de mulighetene vi har for å integrere grafikken i mer rike GUI-omgivelser. Det er enkelt å programmere i glut, men det er begrenset hva slags applikasjoner vi kan lage. Glut distribueres med TAO. Vi går fram som følger:
- Lager et console-program
- Legger til referanser: Tao.FreeGlut og Tao.OpenGL. Disse finner vi der vi plassere dem ved installasjonen
- Kopierer fila freeglut.dll til prosjektkatalogen, legger den til prosjektet og merker den slik at den kopieres til samme katalog som .exe-fila når prosjektet bygges.
Det enkle console programmet ser i sin helhet slik ut:
using System; using System.Collections.Generic; using System.Text; using Tao.FreeGlut; using Tao.OpenGl; namespace taoglutsimple { class Program { static float rotx; static float rotz; static void Main(string[] args) { Glut.glutInit(); Glut.glutInitDisplayMode(Glut.GLUT_RGB | Glut.GLUT_SINGLE); Glut.glutInitWindowSize(320, 320); Glut.glutInitWindowPosition(50, 50); Glut.glutCreateWindow("Sphere"); // once in a lifetime Init(); // define callbacks Glut.glutDisplayFunc(new Glut.DisplayCallback(Display)); Glut.glutReshapeFunc(new Glut.ReshapeCallback(Reshape)); // start the glut main loop Glut.glutMainLoop(); } private static void Init() { // set up material float[] mat_ambient = { 0.3f, 0.3f, 0.0f, 1.0f }; float[] mat_diffuse = { 1.0f, 1.0f, 0.5f, 1.0f }; float[] spec = { 0.6f, 0.6f, 0.5f, 1.0f }; Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_AMBIENT, mat_ambient); Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_DIFFUSE, mat_diffuse); // set up light float[] ambient = { 0.2f, 0.2f, 0.2f, 1.0f }; float[] diffuse = { 1.0f, 1.0f, 1.0f, 1.0f }; float[] position = { 200.0f, 300.0f, 100.0f, 0.0f }; Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_AMBIENT, ambient); Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_DIFFUSE, diffuse); Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_POSITION, position); Gl.glEnable(Gl.GL_LIGHT0); Gl.glEnable(Gl.GL_LIGHTING); // enable light Gl.glEnable(Gl.GL_LIGHTING); Gl.glEnable(Gl.GL_LIGHT0); // depth sorting Gl.glEnable(Gl.GL_DEPTH_TEST); Gl.glDepthFunc(Gl.GL_LESS); // background Gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); } #region callbacks // callbacks private static void Display() { Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT | Gl.GL_ACCUM_BUFFER_BIT); Gl.glMatrixMode(Gl.GL_MODELVIEW); Gl.glLoadIdentity(); Glu.gluLookAt(0, 0, 5, // eyepos 0, 0, 0, // look at 0, 1, 0);// up-vector Glu.GLUquadric quad = Glu.gluNewQuadric(); Glu.gluQuadricDrawStyle(quad, Glu.GLU_FILL); Glu.gluQuadricNormals(quad, Glu.GLU_SMOOTH); Glu.gluQuadricOrientation(quad, Glu.GLU_OUTSIDE); Glu.gluQuadricTexture(quad, Glu.GLU_TRUE); Glu.gluSphere(quad, 1.0, 20, 20); Glu.gluDeleteQuadric(quad); Gl.glFlush(); Glut.glutSwapBuffers(); } private static void Reshape(int width, int height) { Gl.glMatrixMode(Gl.GL_PROJECTION); Gl.glLoadIdentity(); Gl.glViewport(0, 0, width, height); Glu.gluPerspective(30.0f, (double)width / height, 0.1f, 70.0f); Gl.glMatrixMode(Gl.GL_MODELVIEW); } #endregion callbacks } }
Som vanlig er det 3 nødvendige funksjoner som skal ivaretas: init, display og reshape. De to siste er callbacks, siden de skal utføres som konsekvens av (bruker)handlinger. Det er glutMainLoop() som har kontroll helt til vi avslutter programmet ved å lukke vinduet. Vi har en rekke flere muligheter for å plukke opp begivenheter ved callbacks.
glut/teapot
glut-pakka har en egen rutine for å tegne den klassiske tekanna [3] . Koden er i sin helhet slik:
Merk at vi her har lagt inn en timer-callback.
form
Når vi skal integrere OpenGL i vindusstrukturen til .Net blir det noe mer konfigurering å ta hensyn til. Vi må sette opp det aktuelle vinduet, Form, slik at det kan vise OpenGL, og vi må finne en OpenGL konfigurasjon som er kompatibel med de grafikkmulighetene Formen tilbyr. Dette kan gjøres på flere måter, men det enkelste synes å være å bruke en GUI-komponent som distribures som del av TAO Framework. Forutsatt at TAO Framework er installert, kan vi gå fram slik:
- Lag en Windows applikasjon
- Importer referanser: Tao.OpenGL og Tao.Platform.Windows
- Set up Tools fra Tao.Platform.Windows
- Plasser den genererte komponenten på formen
Vi legger i tillegg til en vertikal og en horisontal slider og vi tegner ut en sylinder i opengl-componenten. Vi får følgende kode i Form.cs:
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 Tao.OpenGl; using Tao.Platform.Windows; namespace taoform1 { public partial class Form1 : Form { float rotx = 0.0f; float roty = 0.0f; public Form1() { InitializeComponent(); simpleOpenGlControl1.InitializeContexts(); } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { if (components != null) { simpleOpenGlControl1.DestroyContexts(); } } private void simpleOpenGlControl1_Paint(object sender, PaintEventArgs e) { Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT | Gl.GL_ACCUM_BUFFER_BIT); Gl.glMatrixMode(Gl.GL_MODELVIEW); Gl.glLoadIdentity(); Glu.gluLookAt(0, 0, 5, // eyepos 0, 0, 0, // look at 0, 1, 0);// up-vector Gl.glRotatef(rotx, 1.0f, 0.0f, 0.0f); Gl.glRotatef(roty, 0.0f, 1.0f, 0.0f); Glu.GLUquadric quad = Glu.gluNewQuadric(); Glu.gluQuadricDrawStyle(quad, Glu.GLU_FILL); Glu.gluQuadricNormals(quad, Glu.GLU_SMOOTH); Glu.gluQuadricOrientation(quad, Glu.GLU_OUTSIDE); Glu.gluCylinder(quad, 1.0f, 1.0f, 1.0f, 20, 20); Gl.glFlush(); Glu.gluDeleteQuadric(quad); } private void simpleOpenGlControl1_Resize(object sender, EventArgs e) { setPerspective(); } private void setPerspective() { int height = simpleOpenGlControl1.Height; int width = simpleOpenGlControl1.Width; Gl.glViewport(0, 0, width, height); Gl.glMatrixMode(Gl.GL_PROJECTION); Gl.glLoadIdentity(); Glu.gluPerspective(35, (double)width / (double)height, 1.0, 100); //Gl.glOrtho(-2.0, 2.0, 2.0, -2.0, -10.0, 100.0); Gl.glMatrixMode(Gl.GL_MODELVIEW); } private void simpleOpenGlControl1_Load(object sender, EventArgs e) { // set up material float[] mat_ambient = { 0.3f, 0.3f, 0.0f, 1.0f }; float[] mat_diffuse = { 1.0f, 1.0f, 0.5f, 1.0f }; float[] spec = { 0.6f, 0.6f, 0.5f, 1.0f }; Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_AMBIENT, mat_ambient); Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_DIFFUSE, mat_diffuse); // set up light float[] ambient = { 0.2f, 0.2f, 0.2f, 1.0f }; float[] diffuse = { 1.0f, 1.0f, 1.0f, 1.0f }; float[] position = { 200.0f, 300.0f, 100.0f, 0.0f }; Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_AMBIENT, ambient); Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_DIFFUSE, diffuse); Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_POSITION, position); Gl.glEnable(Gl.GL_LIGHT0); Gl.glEnable(Gl.GL_LIGHTING); // enable light Gl.glEnable(Gl.GL_LIGHTING); Gl.glEnable(Gl.GL_LIGHT0); // depth sorting Gl.glEnable(Gl.GL_DEPTH_TEST); Gl.glDepthFunc(Gl.GL_LESS); // background Gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); setPerspective(); } private void trackBarX_Scroll(object sender, EventArgs e) { rotx = trackBarX.Value; simpleOpenGlControl1.Invalidate(); } private void trackBarY_Scroll(object sender, EventArgs e) { roty = trackBarY.Value; simpleOpenGlControl1.Invalidate(); } } }