Although VB.NET is not the first programming language that comes to mind when you are thinking of writing applications that use the OpenGL API, it is possible (and not that difficult), if you are willing to dip your toes in the warm waters of open source software. OpenGL was originally developed by SGI, and is now available in commercial and free incarnations. In case you have never heard of it, OpenGL is an industry standard multi-platform API for 3D graphics supported by virtually all of the major players in the broadly defined field of computer graphics, computer animation, multimedia, and computer games. On Windows 2000/XP you should have a copy in your C:\WINNT\System\system32 directory (look for OPENGL32.DLL). In this article, I will show you how to quickly write a basic OpenGL application in VB.NET using clever open source glue to make it all work together.
|
Related Reading Programming Visual Basic .NET |
I assume that you are using VB.NET or a full version of the Visual Studio .NET and the .NET Framework SDK.
Next, you will need a copy of the CsGL library, written by the team of developers headed by Lloyd Dupont. CsGL is not another implementation of OpenGL, but a set of wrappers for the OpenGL DLL that glue your C# or VB.NET applications to OpenGL proper. It is written with C# programmers in mind, but it works rather well with VB.NET. The library has a BSD-like license, which means that you can use it in commercial applications, if you obey its (very liberal) terms.
CsGL installation is very simple. Just download the binary
distribution (I used version 1.4.1), unpack it with a tool like
WinZip, and run the
installation script that comes with CsGL.
That's it!
Start Visual Studio, and choose File->New->Project. In the New Project dialog, select Visual Basic Application and Windows Application. Click OK, and you should see the default rectangular form of your application. Right-click on the form and choose View Code. You should now see this snippet of code:
Public Class Form1 Inherits System.Windows.Forms.Form
Windows Form Designer generated code
End Class
Right-click on the References item in the Solution Explorer window and select Add Reference. In the Add Reference dialog, click on the Browse button and locate csgl.dll. Look for it in the same place where you found OPENGL32.DLL, possibly C:\WINNT\System\system32. Select it, click on the Open button in the file dialog, then click on the OK button in the Add Reference dialog. A reference to CsGL has now been added to your project.
Now let's return to the code of your application. Click on
the plus (+) sign next to the "Windows Form
Designer generated code" label, to expand the skeleton code.
You should see this:
Public Class Form1
Inherits System.Windows.Forms.Form
#Region " Windows Form Designer generated code "
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
'Add any initialization after the
'InitializeComponent() call
End Sub
'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose _
(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer
'NOTE: The following procedure is required by the
'Windows Form Designer It can be modified using the
'Windows Form Designer. Do not modify it using the
'code editor.
<System.Diagnostics.DebuggerStepThrough()>
Private Sub InitializeComponent()
components = New System.ComponentModel.Container()
Me.Text = "Form1"
End Sub
#End Region
End Class
You will now have to modify this skeleton in the following way:
view (viewport)
and thrOpenGL (an OpenGL thread).This is accomplished with:
Imports CsGL.OpenGL
Public Class Form1
Inherits System.Windows.Forms.Form
Private view As myOpenGL.myView
Private thrOpenGL
Next, you need to initialize view and thrOpenGL:
#Region " Windows Form Designer generated code "
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
'Add any initialization after the InitializeComponent() call
Me.view = New myOpenGL.myView()
Me.view.Parent = Me
Me.view.Dock = DockStyle.Fill
Me.thrOpenGL = New Threading.Thread(AddressOf OpenGL_Start)
Me.thrOpenGL.Start()
End Sub
#End Region
When thrOpenGL is initialized, it is given a reference
to the OpenGL_Start method, which takes care of refreshing the
viewport:
Private Sub OpenGL_Start()
While 1 = 1
Me.view.Refresh()
End While
End Sub
Once we have taken care of the start of the OpenGL thread,
the next step is adding code that terminates it. This is the
job of a modified version of the default Dispose()
method.
'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
Me.thrOpenGL.Abort()
End Sub
As you can see, all that was required was an addition of
a call to Me.thrOpenGL.Abort().
Next we need to add a large chunk of code that
does the real work. In the myOpenGL
namespace, define the class myView, which inherits
CsGL.OpenGL.OpenGLControl and overrides a few of
its methods, overloads some of them as needed, and defines a keyboard
handler.
Namespace and class definition, and inheritance of
OpenGLControl, are taken care of by these lines of code:
Namespace myOpenGL
Public Class myView
Inherits OpenGLControl
The next step is a bit unusual, because it defines
constants that are normally found in the gl.h
and glu.h header files. For some reason, I could
not access them in the same way as the methods and classes of CsGL,
but I found a way around it. I run these commands to create
code that can be put directly into the Private Enum
GLConstants ... End Enum block:
$ awk '/#define/ { print $2 " =
&H" substr($3, 3) }' gl.h > glconst
$ awk '/#define/ { print $2 " =
&H" substr($3, 3) }' glu.h > gluconst
For this example, the list of constants should look like this:
Private Enum GLConstants
GL_COLOR_BUFFER_BIT = &H4000
GL_DEPTH_BUFFER_BIT = &H100
GL_SMOOTH = &H1D01
GL_DEPTH_TEST = &HB71
GL_LEQUAL = &H203
GL_PERSPECTIVE_CORRECTION_HINT = &HC50
GL_NICEST = &H1102
GL_PROJECTION = &H1701
GL_MODELVIEW = &H1701
GL_POLYGON = &H9
End Enum
If you don't have an AWK interpreter installed
on your machine, you can find it in at least three
places: cygwin, unxutils, or
Windows
Services for UNIX. Some of these packages may
contain the gawk interpreter, which is
the GNU implementation of AWK that can be used instead of
awk. You will also need the gl.h
and glu.h header files, which you can get from
SGI
or from Mesa.
|
Next we override glDraw(). This is the place
where we add code that actually draws something. Readers of
this
book, will recognize Example 1-1 from the "Red Book."
Note how constants are used and when they're converted into
UInt32.
Public Overrides Sub glDraw()
GL.glClearColor(0.0, 0.0, 0.0, 0.0)
GL.glClear( _
Convert.ToUInt32( _
GLConstants.GL_COLOR_BUFFER_BIT Or _
GLConstants.GL_DEPTH_BUFFER_BIT))
GL.glLoadIdentity()
'
' Fun begins
'
GL.glColor3f(1.0, 1.0, 1.0)
GL.glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0)
GL.glBegin(Convert.ToUInt32(GLConstants.GL_POLYGON))
GL.glVertex3f(0.25, 0.25, 0.0)
GL.glVertex3f(0.75, 0.25, 0.0)
GL.glVertex3f(0.75, 0.75, 0.0)
GL.glVertex3f(0.25, 0.75, 0.0)
GL.glEnd()
GL.glFlush()
'
End Sub
Then we override InitGLContext().
Protected Overrides Sub InitGLContext()
GL.glShadeModel(Convert.ToUInt32(GLConstants.GL_SMOOTH))
GL.glClearColor(0.0, 0.0, 0.0, 0.5)
GL.glClearDepth(1.0)
GL.glEnable(Convert.ToUInt32(GLConstants.GL_DEPTH_TEST))
GL.glDepthFunc(Convert.ToUInt32(GLConstants.GL_LEQUAL))
GL.glHint( _
Convert.ToUInt32( _
GLConstants.GL_PERSPECTIVE_CORRECTION_HINT), _
Convert.ToUInt32(GLConstants.GL_NICEST))
End Sub
If we want to automatically resize the OpenGL viewport,
we'll need to overload OnSizeChanged().
Protected Overloads Sub OnSizeChanged(ByVal e)
Me.OnSizeChanged(e)
Dim s As Size
Dim aspect_ratio As Double
s = Me.Size
aspect_ratio = s.Width / s.Height
GL.glMatrixMode(Convert.ToUInt32(GLConstants.GL_PROJECTION))
GL.glLoadIdentity()
GL.gluPerspective(45.0, aspect_ratio, 0.1, 100.0)
GL.glMatrixMode(Convert.ToUInt32(GLConstants.GL_MODELVIEW))
GL.glLoadIdentity()
End Sub
Finally, we need to add a keyboard event handler. This one watches the Escape key, but you can easily extend it to handle other keystrokes.
Protected Sub myView_OnKeyDown(ByVal Sender As Object, _
ByVal kea As System.Windows.Forms.KeyEventArgs) _
Handles MyBase.KeyDown
If (kea.KeyCode = Keys.Escape) Then
Application.Exit()
End If
End Sub
Well, that's it! Your first VB.NET OpenGL application looks now like this:
Imports CsGL.OpenGL
Public Class Form1
Inherits System.Windows.Forms.Form
Private view As myOpenGL.myView
Private thrOpenGL
#Region " Windows Form Designer generated code "
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
'Add any initialization after the InitializeComponent() call
Me.view = New myOpenGL.myView()
Me.view.Parent = Me
Me.view.Dock = DockStyle.Fill
Me.thrOpenGL = New Threading.Thread(AddressOf OpenGL_Start)
Me.thrOpenGL.Start()
End Sub
Private Sub OpenGL_Start()
While 1 = 1
Me.view.Refresh()
End While
End Sub
'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose( _
ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
Me.thrOpenGL.Abort()
End Sub
'Required by the Windows Form Designer
Private components As _
System.ComponentModel.IContainer
'NOTE: The following procedure is required
'by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponent()
components = New System.ComponentModel.Container()
Me.Text = "Form1"
End Sub
#End Region
End Class
Namespace myOpenGL
Public Class myView
Inherits OpenGLControl
Private Enum GLConstants
GL_COLOR_BUFFER_BIT = &H4000
GL_DEPTH_BUFFER_BIT = &H100
GL_SMOOTH = &H1D01
GL_DEPTH_TEST = &HB71
GL_LEQUAL = &H203
GL_PERSPECTIVE_CORRECTION_HINT = &HC50
GL_NICEST = &H1102
GL_PROJECTION = &H1701
GL_MODELVIEW = &H1701
GL_POLYGON = &H9
End Enum
Public Overrides Sub glDraw()
GL.glClearColor(0.0, 0.0, 0.0, 0.0)
GL.glClear( _
Convert.ToUInt32( _
GLConstants.GL_COLOR_BUFFER_BIT Or _
GLConstants.GL_DEPTH_BUFFER_BIT))
GL.glLoadIdentity()
'
' Fun begins
'
GL.glColor3f(1.0, 1.0, 1.0)
GL.glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0)
GL.glBegin(Convert.ToUInt32(GLConstants.GL_POLYGON))
GL.glVertex3f(0.25, 0.25, 0.0)
GL.glVertex3f(0.75, 0.25, 0.0)
GL.glVertex3f(0.75, 0.75, 0.0)
GL.glVertex3f(0.25, 0.75, 0.0)
GL.glEnd()
GL.glFlush()
'
End Sub
Protected Overrides Sub InitGLContext()
GL.glShadeModel(Convert.ToUInt32(GLConstants.GL_SMOOTH))
GL.glClearColor(0.0, 0.0, 0.0, 0.5)
GL.glClearDepth(1.0)
GL.glEnable(Convert.ToUInt32(GLConstants.GL_DEPTH_TEST))
GL.glDepthFunc(Convert.ToUInt32(GLConstants.GL_LEQUAL))
GL.glHint( _
Convert.ToUInt32( _
GLConstants.GL_PERSPECTIVE_CORRECTION_HINT), _
Convert.ToUInt32(GLConstants.GL_NICEST))
End Sub
Protected Overloads Sub OnSizeChanged(ByVal e)
Me.OnSizeChanged(e)
Dim s As Size
Dim aspect_ratio As Double
s = Me.Size
aspect_ratio = s.Width / s.Height
GL.glMatrixMode(Convert.ToUInt32(GLConstants.GL_PROJECTION))
GL.glLoadIdentity()
GL.gluPerspective(45.0, aspect_ratio, 0.1, 100.0)
GL.glMatrixMode(Convert.ToUInt32(GLConstants.GL_MODELVIEW))
GL.glLoadIdentity()
End Sub
Protected Sub myView_OnKeyDown(ByVal Sender As Object, _
ByVal kea As System.Windows.Forms.KeyEventArgs) _
Handles MyBase.KeyDown
If (kea.KeyCode = Keys.Escape) Then
Application.Exit()
End If
End Sub
End Class
End Namespace
Now, all you have to do is build this code (Ctrl+Shift+B) and run it with F5. What you should see is a white rectangle on a black background, as the "Red Book" describes it. For more information about which CsGL method does what and how to override or overload them, see the source of CsGL, available from the project's site.
The best place to learn about CsGL is the project's site and mailing list. There you will find information about licensing, FAQs, and more.
As for OpenGL itself, you should not venture into writing OpenGL applications without the OpenGL Reference Manual (AKA the "Blue Book") and the OpenGL Programming Guide (AKA the "Red Book"). They are the ABCs of OpenGL. A few more titles are available, but these two should answer most of your questions. There are also quite a few online resources; I strongly recommend the famous NeHe tutorials. Links to more information for developers can be found on the official OpenGL site, in their developer section.
And if you are struggling with VB.NET or NET in general, have a look at O'Reilly's .NET section.
Best of luck hacking your OpenGL code in VB.NET!
Jacek Artymiak started his adventure with computers in 1986 with Sinclair ZX Spectrum. He's been using various commercial and Open Source Unix systems since 1991. Today, Jacek runs devGuide.net, writes and teaches about Open Source software and security, and tries to make things happen.
Return to ONDotnet.com
Copyright © 2009 O'Reilly Media, Inc.