Learn OpenGL and C#

Introduction

I’m glad to be the first one writing about OpenGL at this site. What I want to show you in this article is that it is fairly easy to setup a form that is capable of showing 3D. We will accomplish this with the OpenGLControl. So before we can start we (that means you) have to download CsGL here, or if it isn’t there anymore, from its project page. For this article I used the CsGL version 1.4.0 I think it will run on later versions though.

Ok let we start. First thing to do is make a new Windows Application. Which I called SimpelOpenGL but its not important. Ok when that done popup the “Solution explorer” and “add reference” the reference for CsGL must be in "D:\WINDOWS\system32\" which are called csgl.dll and csgl-base.dllnotice that there is .dll called csgl-native.dll but you don’t need it.

Ok import the following namespaces

An OpenGL Control

What we need to do now is add a class named OurView. Make it a derived class from OpenGLControl. I will show you the code for the class now.

using System;
using System.Drawing;
using System.Windows.Forms;
using CsGL.OpenGL;
namespace SimpleOpenGL
{
    public class OurView : OpenGLControl
    {
        public OurView(): base()
        {
            this.KeyDown += new KeyEventHandler(OurView_OnKeyDown);
        }
        public override void glDraw()
        {    
            GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer    
            GL.glLoadIdentity();                 // Reset The Current Modelview Matrix        
            // TODO: Draw a box or something
//             this.SwapBuffer(); // swap Buffers
        }
        protected override void InitGLContext()
        {
            GL.glShadeModel(GL.GL_SMOOTH);         // Set Smooth Shading                
            GL.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);     // BackGround Color        
            GL.glClearDepth(1.0f);                 // Depth buffer setup            
            GL.glEnable(GL.GL_DEPTH_TEST);         // Enables Depth Testing            
            GL.glDepthFunc(GL.GL_LEQUAL);             // The Type Of Depth Test To Do    
            GL.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);     /* Really Nice Perspective Calculations */    
        }
        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);
       
            Size s = Size;
            double aspect_ratio = (double)s.Width /(double) s.Height;
            GL.glMatrixMode(GL.GL_PROJECTION); // Select The Projection Matrix
            GL.glLoadIdentity(); // Reset The Projection Matrix
           
            // Calculate The Aspect Ratio Of The Window
            GL.gluPerspective(45.0f, aspect_ratio, 0.1f, 100.0f);
   
            GL.glMatrixMode(GL.GL_MODELVIEW); // Select The Modelview Matrix
            GL.glLoadIdentity();// Reset The Modelview Matrix
        }
        protected void OurView_OnKeyDown(object Sender, KeyEventArgs kea)
        {
            if (kea.KeyCode == Keys.Q && kea.Modifiers == Keys.Shift)
            {
                Application.Exit();
            }
        }
    }
}

You may notice that this is almost the base code from NeHe. And yes it is I created it with the NeHe base code in mind. I won’t go for every gl function in detail you can read here, lesson 1 & 2 for that.

Now we have the control we can add it to our form. Add this in the class

private SimpleOpenGL.OurView view;

Ok add this after the InitializeComponent function.

this.view = new SimpleOpenGL.OurView();
this.view.Parent = this;
this.view.Dock = DockStyle.Fill; // Will fill whole form

You can now try to run your form. Everything is running perfect. But there is one more thing our form is only render once. And when you resize it, it will render another time. To solve this problem we’re going to add a thread which will render the scene over and over again (how it should).

Ok first thing to do is add a thread. Put this underneath the other declarations.

private static Thread thrOpenGL;

Then underneath the view properties.

thrOpenGL = new Thread(new ThreadStart(OpenGL_Start));
thrOpenGL.Start();
Then the function OpenGL_Start.
private void OpenGL_Start()
{
    for (;;) // infinity loop for rendering
    {
        this.view.Refresh();
    }
}

If you would run the application now you might notice that when you close the form you program isn’t really exit cause the thread is still running to solve this we add this code at the end of the Dispose function. But for people who don’t use the Visual Studio .Net I will show you the whole function.

protected override void Dispose( bool disposing )
{
    if( disposing )
    {
        if (components != null)
        {
            components.Dispose();
        }
    }
    base.Dispose( disposing );
    // Abort the OpenGL Thread
    // otherwise it will last rendering forever
    thrOpenGL.Abort();
}

We are done with our OpenGL control now but it is hard to show off to your friends when you only have a black window. Like this.BackColor = Color.Black;in the main form constructor will do the same thing.

Our Cube Class

What we going to do now is making a class which will draw a cube. This cube can rotate, change color and even shrink or grow.I will just show you the code and I’m going to explain it to you later on.

using System;
using System.Drawing;
using CsGL.OpenGL;
namespace SimpleOpenGL
{
    public class Cube
    {
        protected float[] fColor = new float[3];
        /// <summary>
        /// How much Red between 0.00 and 1.00
        /// </summary>
        public float CubeColorRed
        {
            get { return fColor[0]; }
            set { fColor[0] = value ; }
        }
        /// <summary>
        /// How much Green between 0.00 and 1.00
        /// </summary>
        public float CubeColorGreen
        {
            get { return fColor[1]; }
            set { fColor[1] = value ; }
        }
        /// <summary>
        /// How much Blue between 0.00 and 1.00
        /// </summary>
        public float CubeColorBlue
        {
            get { return fColor[2]; }
            set { fColor[2] = value ; }
        }
        protected float fPlusPosition;
        protected float fMinusPosition;
        /// <summary>
        /// How big ??
        /// </summary>
        public float Scale
        {
            set
            {
                fPlusPosition = value;
                fMinusPosition = (0 - value);
            }
        }
        public float fRotate;
        protected bool bRotateCheck;
        /// <summary>
        /// Wanna rotate???
        /// default is false
        /// </summary>
        public bool Rotate
        {
            get { return bRotateCheck; }
            set { bRotateCheck = value; }
        }
        protected float fViewPort;
        /// <summary>
        /// With this you can zoom
        /// </summary>
        public float ViewPort
        {
            get { return fViewPort; }
            set { fViewPort = (-6 + value); }
        }
        /// <summary>
        /// Default Contructor
        /// </summary>
        public Cube()
        {
            for (int i = 0; i < 3; i++)
            {
                fColor[i] = 1.0f;
            }
            bRotateCheck = false;
            this.ViewPort = 0;
            this.fMinusPosition = 0f;
            this.Position = 2f;
        }
        /// <summary>
        /// Do the Draws
        /// </summary>
        public void glDrawCube()// To be in OpenGL style it I gave it a gl prefix
        {
            GL.glTranslatef(0.0f,0.0f, fViewPort);     // viewport = 0 0 0 and this is 6 deep
            if (bRotateCheck == true)
            {
                GL.glRotatef(fRotate, 1.0f, 1.0f, 1.0f);
            }
            GL.glBegin(GL.GL_QUADS);// start drawin the cube
                    GL.glColor3fv(fColor); // Color with an array of floats
                // Draw Top of the Cube
                GL.glVertex3f( fPlusPosition, fPlusPosition, fMinusPosition);                    
                GL.glVertex3f(fMinusPosition, fPlusPosition, fPlusPosition);                
                GL.glVertex3f(fMinusPosition, fPlusPosition, fPlusPosition);                
                GL.glVertex3f( fPlusPosition, fPlusPosition, fPlusPosition);                    
                // Draw Bottom of the Cube
                GL.glVertex3f( fPlusPosition, fMinusPosition, fPlusPosition);                    
                GL.glVertex3f(fMinusPosition,fMinusPosition, fPlusPosition);                    
                GL.glVertex3f(fMinusPosition,fMinusPosition, fMinusPosition);                    
                GL.glVertex3f( fPlusPosition,fMinusPosition, fMinusPosition);                
                // Draw Front of the Cube
                GL.glVertex3f( fPlusPosition, fPlusPosition, fPlusPosition);                
                GL.glVertex3f(fMinusPosition, fPlusPosition, fPlusPosition);                
                GL.glVertex3f(fMinusPosition,fMinusPosition, fPlusPosition);                
                GL.glVertex3f( fPlusPosition,fMinusPosition, fPlusPosition);        
                // Draw Back of the Cube
                GL.glVertex3f( fPlusPosition,fMinusPosition, fMinusPosition);    
                GL.glVertex3f(fMinusPosition,fMinusPosition, fMinusPosition);                
                GL.glVertex3f(fMinusPosition, fPlusPosition, fMinusPosition);                    
                GL.glVertex3f( fPlusPosition, fPlusPosition, fMinusPosition);                    
                // Draw Left of the Cube
                GL.glVertex3f(fMinusPosition, fPlusPosition, fPlusPosition);                
                GL.glVertex3f(fMinusPosition, fPlusPosition, fMinusPosition);                    
                GL.glVertex3f(fMinusPosition,fMinusPosition, fMinusPosition);            
                GL.glVertex3f(fMinusPosition,fMinusPosition, fPlusPosition);            
                // Draw Right of the Cube
                GL.glVertex3f( fPlusPosition, fPlusPosition, fMinusPosition);            
                GL.glVertex3f( fPlusPosition, fPlusPosition, fPlusPosition);                    
                GL.glVertex3f( fPlusPosition,fMinusPosition, fPlusPosition);                
                GL.glVertex3f( fPlusPosition,fMinusPosition, fMinusPosition);    
            GL.glEnd();// end drawing the cube    
           
            if (bRotateCheck == true)
            {
                fRotate += 2.5f;
            }
        }
    }
}

What I’m going to explain the following functions.

glTranslatef

Which is defined like this glTranslatef(float x, float y, float z) or like glTranslated(double x, double y, double z) which uses doubles instead of floats.

This function will set the drawing point. In this application we want to draw the cube right in the center of the screen so we set x and y to zero. So to make the cube visible we have to draw it a bit deeper then that we are looking so to go deeper we set the z variable in a minus position like mine apps default -6.

glRotatef

Which is defined like this glRotatef(float angle, float x, float y, float z) or like glRotated(double angle, double x, double y, double z).

To let it rotate we have to make the angle every time a bit larger like we did. For the “x y z” you just have to play with it to really get to know it.

glBegin & glEnd

Which is defined like this glBegin(uint mode) and the glEnd( void ). Those functions always come in pairs.

This function is to say against OpenGL start drawing mode or something like that. The glBegin function we used has GL_QUADS but can be much more than only quads. I will go on more detail for glBegin in the next article.

glColor3fv

This function has many others and here they are.

glColor3b(sbyte red, sbyte green, sbyte blue)
glColor3bv(sbyte[] v)
glColor3d(double red, double green, double blue)
glColor3dv(double[] v)
glColor3f(float red, float green, float blue)
glColor3fv(float[] v)
glColor3i(int red, int green, int blue)
glColor3iv(int[] v)
glColor3s(short red, short green, short blue)
glColor3sv(short[] v)

And the unsigned versions of them which are defined with a U before the type prefix. There’s also an alpha version of all this functions and have a 4 instead of a 3 where the last type variable is the alpha. This will give the quad a color where red, green and blue can be between 0.0 and 1.0

gllVertex3f

this function has also many others and here they are.

glVertex3d(double x, double y, double z)
glVertex3dv(double[] v)
glVertex3f(float x, float y, float z)
glVertex3fv(float[] v)
glVertex3i(int x, int y, int z)
glVertex3iv(int[] v)
glVertex3s(short x, short y, short z)
glVertex3sv(short[] v)

And then you can change 3 to 2 or 4. At the 2nd version of this row of functions there is no z axis. And with the 4th version of these functions there is also a w axis. This function will point a place where to draw. I will go deeper into the OpenGL coordinate at the next article so try to play with it for now.

Summary

You now know how to set up a OpenGL form where you can draw. But for now play with this and I will come soon with my next article about OpenGL.

BTW: there is one thing I want to tell you and that is that you can put as many glColor3f() functions between the glVertex3f() so that all points get different colors really cool!!! As you may notice we didn’t covered any basics for coordinates and this will be my next article to really draw some figures and so.

You might also like...

Comments

About the author

Johnny Netherlands

I'm doing first year Informatica at academy of Amsterdam

Interested in writing for us? Find out more.

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“A computer lets you make more mistakes faster than any other invention in human history, with the possible exceptions of handguns and tequila” - Mitch Ratcliffe