The Open Toolkit Manual

The manual is not complete. You can find (and add) experimental pages here. Also, check the available translations. Welcome and thanks for using the Open Toolkit library! This manual will guide you through the necessary steps to develop a project with OpenTK. You will learn how to setup a new project, how to successfully use the tools provided by OpenTK and, finally, how to distribute your project to your end-users. You will also find information on writing performing code and maintaining crossplatform compatibility. This manual is written in a way that allows skipping from section to section as needed, so feel free to do that; or you can read it sequentially from start to end, if you prefer. Keep in mind that you may add a comment at any point - we always try to improve the manual and high-quality feedback will help not only you but future OpenTK users, too. You can also browse the OpenTK API reference. It is our hope that the time invested reading this book will be paid back in full. So let's get started!

Table of Contents
  

 

Table of Contents Chapter 0: Learn OpenTK in 15' Chapter 1: Installation o Linux o Windows o Troubleshooting Chapter 2: Introduction to OpenTK o The DisplayDevice class o The GameWindow class o The NativeWindow class o Building a Windows.Forms + GLControl based application o Avoiding pitfalls in the managed world Chapter 3: OpenTK.Math o Half-Type Chapter 4: OpenTK.Graphics (OpenGL and ES) o The GraphicsContext class  Using an external OpenGL context with OpenTK o Textures  Loading a texture from disk  2D Texture differences  S3 Texture Compression o Frame Buffer Objects (FBO) o Geometry  1. The Vertex

  

  

2. Geometric Primitive Types 3.a Vertex Buffer Objects 3.b Attribute Offsets and Strides 3.c Vertex Arrays 4. Vertex Array Objects 5. Drawing 5.b Drawing Optimizations 6. OpenTK's procedural objects o OpenGL rendering pipeline o Fragment Operations  01. Pixel Ownership Test  02. Scissor Test  03. Multisample Fragment Operations (WIP)  04. Stencil Test  05. Depth Test  06. Occlusion Query  Conditional Render  07. Blending  08. sRGB Conversion  09. Dithering  10. Logical Operations o How to save an OpenGL rendering to disk o How to render text using OpenGL Chapter 5: OpenTK.Audio (OpenAL) o 1. Devices, Buffers and X-Ram o 2. Sources and EFX o 3. Context and Listener Chapter 6: OpenTK.Compute (OpenCL) Chapter 7: OpenTK.Input Chapter 8: Advanced Topics o Vertex Cache Optimizations o Garbage Collection Performance o GC & OpenGL (work in progress) Chapter 9: Hacking OpenTK o Project Structure o OpenTK Structure o Wrapper Design Appendix 1: Frequently asked questions Appendix 2: Function Reference Appendix 3: The project database o Creating a project o Creating a project release Appendix 4: Links o Models and Textures o OpenGL Books and Tutorials o Programming links o Tools & Utilities o Tutorials Appendix 5: Translations

       

Chapter 0: Learn OpenTK in 15'
So, you have downloaded the latest version of OpenTK - what now? This is a short tutorial that will help you get started with OpenTK in 3 simple steps. [Step 1: Installation] Open the zip you downloaded and extract it to a folder of your choosing. I usually create a 'Projects' folder on my desktop or in my documents but any folder will do. [Step 2: Use OpenTK] Open the folder you just extracted. Inside, you will find three solutions: OpenTK.sln, Generator.sln and QuickStart.sln. The first two contain the OpenTK source code - no need to worry about them right now. The QuickStart solution is what we are interested in. Double-click QuickStart.sln. This will launch your .Net IDE (don't have a .Net IDE? Check out MonoDevelop or Visual Studio Express). Take a few moments to take in the contents of the QuickStart project:
 

Game.cs: this contains the code for the game. Scroll to the bottom: the Main() method is where everything begins. References: click on the '+' sign to view the project references. The 'OpenTK' reference is the only one you need in order to use OpenTK.

Now, press F5 to run the project. A window with a colored triangle will show up - not very interesting, is it? Press escape to close it. [Step 3: Play] Now it's time to start playing with the code. This is a great way to learn OpenGL and OpenTK at the same time. Every OpenTK game will contain 4 basic methods: 1. OnLoad: this is the place to load resources from disk, like images or music. 2. OnUpdateFrame: this is a suitable place to handle input, update object positions, run physics or AI calculations. 3. OnRenderFrame: this contains the code that renders your graphics. It typically begins with a call to GL.Clear() and ends with a call to SwapBuffers. 4. OnResize: this method is called automatically whenever your game window changes size. Fullscreen applications will typically call it only once. Windowed applications may call it more often. In most circumstances, you can simply copy & paste the code from Game.cs. Why don't you try modifying a few things? Here are a few suggestions:

display devices. Make the triangle move across the screen. of course.even if something breaks. Rotate the camera so that the plane above acts as ground (OnRenderFrame method).Net runtime (Windows) or the Mono runtime (Linux/Mac OS X/Windows). which could cover a whole book by itself. Vector3. Use the keyboard and mouse to walk on the ground. Make the triangle change colors when you press a key (OnUpdateFrame and OnRenderFrame methods). which means OpenTK is typically usable out of the box. Hopefully.0 framework. . advanced opengl. In a few cases. 4.Net 2.0 will work fine. plus device drivers for OpenGL (graphics). Then there's the subject of proper engine and game design. support for GUIs through GLControl. but are no longer supported. respectively. [Step: next] There's a lot of functionality that is not visible at first glance: audio.1.Net will not work. To use it.Net or Mono runtime.. Joysticks property for interaction with joystick devices.0 / Mono 2.Net runtime preinstalled. the library will help you pinpoint the cause of the error.. don't hesitate to post at the forums if you hit any roadblocks! Chapter 1: Installation [Prerequisites] OpenTK is a managed library that targets the .Net 2. 3. you might need to install manually a version of . Use a for-loop to render many triangles arranged on a plane (OnRenderFrame method). Change the colors of the triangle or the window background (OnLoad and OnRenderFrame methods). Don't be afraid to try things and see the results. Earlier versions of Mono may also work. OpenAL (audio) and OpenCL (compute). Use the arrow keys or the mouse to control its position (OnUpdateFrame and OnRenderFrame methods). 2. Earlier versions of . depending on the parts of OpenTK you wish to use. you'll have gained a feel of the library by now and you'll be able to accomplish more complex tasks. you will need either the . OpenTK lends itself to explorative programming . Most operating systems come with a version of the . Make sure you can't fall through it! (OnUpdateFrame and OnRenderFrame methods). Any version equal to or newer than . 6. Some things you might find useful: Vector2. Mouse and Keyboard properties for interaction with the mouse and keyboard. 5. Vector4 and Matrix4 classes for camera manipulations. You might wish to consult the complete documentation for the more advanced aspects of OpenTK and.

Linux Installing Mono If you are using a recent Linux distribution. all prerequisites for OpenTK projects should be readily available: the Mono runtime and the Mono compilers.monoproject.2. you'll have to install Mono.0 If one or both of these commands fail. Mono packages should be readily available through your package manager: # Ubuntu and other .Additionally.dll as a reference to your project.dll under the Binaries/OpenTK folder of the OpenTK archive. Execute "mono --version" and "gmcs --version" and check if the output looks like this: $ mono --version Mono JIT compiler version 1. Additionally.deb-based distributions sudo apt-get install mono mono-gmcs # or su -c "apt-get install mono mono-gmcs" # Fedora Core and . TLS: __thread GC: Included Boehm (with typed GC) SIGSEGV: altstack Notifications: epoll Architecture: amd64 Disabled: none $ gmcs --version Mono C# compiler version 1. [todo: add links to common hardware vendors] Last. Simply extract the archive contents to a location on your disk and add OpenTK.config to your project and instruct your IDE to copy this file to the output directory. most recent operating systems come with OpenGL drivers preinstalled.rpm-based distributions su -c "yum install mono mono-gmcs" .dll. [Installation] OpenTK releases are simple compressed archives.6. For OpenAL and OpenCL drivers. you should refer to the website of your hardware vendors. you should add OpenTK.2. You can find OpenTK. The following pages contain specific instructions for using or building OpenTK on different platforms. Inc and Contributors.6 (tarball) Copyright (C) 2002-2007 Novell. but not least. you will need to download the latest OpenTK release. This is necessary for your project to function under Linux and Mac OS X.

exe A new window will hopefully show up. or they are outdated (mono --version returns something less than 1.3. Examples.dll" target="/usr/lib/libgdiplus. Troubleshooting The following error has been reported on Fedora Core 8. you can find use one of the Mono binary packages on the Mono download page.System.y.Drawing. and add this line: <dllmap dll="gdiplus.TypeInitializationException: An exception was thrown by the type initializer for Now. check the troubleshooting section below. when running Examples.. The "Libraries" folder contains the main OpenTK assembly (OpenTK. If not.gz release from Sourceforge and untar it: tar -xvf opentk-0.System. check the "QuickStart" folder for a ready-to-use project.exe should work.dll) and the OpenTK.GdiplusSta rtupOutput&) at System. To correct this issue. listing all available examples.Reflection.y.6).these are all you need to run OpenTK projects. "Examples".config file .Forms.DllNotFoundException: gdiplus.z will be created with four subfolders: "Documentation".exe: Unhandled Exception: System.tar.3.GDIPlus. you should build Mono from source.If no Mono packages are available.gz A new opentk-x.13/Examples mono Examples. Building OpenTK from source . Alternatively.dll. "Libraries" and "QuickStart".Windows. If you are using MonoDevelop. don't forget to take a look at the release notes contained in the "Documentation" folder. ---> System.Form ---> System. open the aforementioned file (you must be root!).GDIPlus ---> System.Drawing.cctor () [0x00000] --.tar.Drawing.Drawing.TypeInitializationException: An exception was thrown by the type initializer for System. Using a binary release Download the latest opentk-x. Try running the examples contained in the second folder to make sure everything works alright: cd opentk-0.2.dll at (wrapper managed-to-native) System.End of inner exception stack trace --- This is caused by a missing entry in "/etc/mono/config".TargetInvocationException: Exception has been thrown by the target of an invocation.0" />.GdiplusStartupInput&.z-mono.13-mono. Last.GDIPlus:GdiplusStartup (ulong&.Drawing. There is a message in the support forum describing the process of building mono from source here.

OpenTK's build system currently uses NAnt. then download a compiler/IDE for . in case there is no package available] Windows OpenTK does not come with any installer or setup. Here are some popular choices: 1. untar the source release and cd to the Build folder: unzip opentk-0.13.3. MonoDevelop (bundled in the mono installer) 3.2. the following software is required: 1. Instead. . SharpDevelop 2.NET/mono. Note that OpenAL is not strictly required if the application does not use any sound. OpenTK development If you want to start developing applications using OpenTK. append "debug" so that the last command looks like: mono Build.13/Build mono Build. first make sure the items under "OpenTK demo" are installed.3.0 or Mono 1. (Unzip the binaries first!) OpenTK demo To run all of the OpenTK builtin examples.dll" in your Visual Studio/SharpDevelop/MonoDevelop project. and check the "Binaries" folder that just appeared in the base OpenTK directory.exe mono debug [Add an appendix that describes how to build Mono from cd opentk-0. you download the OpenTK binaries and add a reference to "OpenTK.6 2.exe mono Wait a few seconds for the compilation to end. OpenAL 2. To build the debug version.0.3 This is also the software required for an end-user running an OpenTK application. so you'll need to install that: # Ubuntu sudo apt-get install nant # Debian su -c "apt-get install nant" # Fedora su -c "yum install nant" Once that is out of the way.NET2. Visual Studio Express .

Visual explanation: Troubleshooting Most problems with running OpenTK-based Applications are related to the target platform missing the proper Creative Labs (Mac & Windows) http://www.config" in your project.html .aspx?displaylang=en&FamilyID=. Mac & Windows) http://www.html Direct link to download page for Windows: http://connect.go-mono. and make sure the "Copy To Output Folder" (not "compile"!) is set to "Copy Always". The application will run without this on Windows. OpenTK requires these components installed:    Either Mono or . Below are links for your convenience. Strangesoft (Linux) http://kcat..html OpenGL .microsoft. make sure the "Copy Local" property is set to true for the OpenTK reference. An OpenGL driver for your graphics card. but not but not on Linux or Mac OS The OpenAL driver for your Operating System. Mono Novell (Linux..Net Microsoft (Windows) http://www.Net (not both). Setting up an OpenTK application in SharpDevelop Include the "OpenTK. OpenAL Note: It doesn't matter what brand your soundcard is.creativelabs.dll.Setting up an OpenTK application in Visual Studio Express It is a good idea to add " to simplify the distribution of your application. Last.config" to your Note: Many of those sites require Javascript enabled to function. if you want it to run under Linux Mac OS X.dll. just chose the proper Operating System.strangesoft.

OpenGL|ES. Unlike similar libraries. with iPhone port in progress.nvidia.html Mesa 3D (software rendering) Cross-platform binaries that are portable on . SDL or GLFW. OpenTK attempts provide a consistent interface that utilizes the superior managed runtime. As such. OpenTK uses strongly-typed enumerations. VB. Instead of untyped pointers. Instead of plain constants. .amd. but it quickly grew in focus: right now. the Open Toolkit is a free project that allows you to use OpenGL. suitable for RAD development. Linux. Features:      Written in cross-platform C# and usable by all managed languages (F#. C++/CLI). OpenTK started life as an experimental fork of the Tao framework before during the summer of 2006.Forms. It's original intention was to provide a cleaner wrapper than Tao. A common math library is integrated and directly usable by each Mac OS Intel (Linux) http://intellinuxgraphics. what is OpenTK? Simply Last edit of the links: March 2008 Tags for searches: help problem error outdated trouble crash fail failure exception abort opengl openal driver ati intel nvidia Chapter 2: Introduction to OpenTK First of all. it provides access to various Khronos and Creative APIs and handles the necessary initialization logic for each API. OpenTK provides generics.Net. the Open Toolkit is most similar to projects like Tao. you can obtain updated drivers through: http://www. Instead of plain function lists. OpenCL and OpenAL APIs from managed Consistent. SlimDX.aspx?lang=enus Intel (Windows) http://downloadcenter.Net and Mono without recompilation. Wide platform support: Windows. Boo. WPF. Usable stand-alone or integrated with Windows. GTK#. OpenTK separates functions per extension category.html NVIDIA (Linux & Windows) http://www.ATi (Linux.php?group_id=3 If you have a laptop with an Nvidia card. Mac & Windows) http://ati.OpenGL. strongly-typed bindings.

AvailableDevices) { device. OpenTK exposes all of them through the same interface: OpenTK. 600.AvailableResolutions) { Console. 32. You can use OpenTK.Bounds).DisplayDevice. foreach (DisplayDevice device in DisplayDevice. the . If a specific bit depth or refresh rate is not supported. The DisplayDevice class There are three main types of display devices: monitors.RefreshRate). -1). scientific visualizations and all kinds of software that requires advanced graphics. } OpenTK will try to match your custom resolution to the closest supported resolution. foreach(DisplayResolution res in device. the current bit depth or refresh rate will be used. Console. audio or compute capabilities.ChangeResolution(800. Console.WriteLine(device. If no custom resolution matches the specified parameters.AvailableDisplays) { Console. Example 1: discover available devices using OpenTK.WriteLine(device.WriteLine(device.ChangeResolution(800. It's license makes it suitable for both free and commercial applications. Example 3: set the resolution of the second device to 800x600x32 using the preferred refresh rate: using OpenTK.WriteLine(res). 32.BitsPerPixel). 600.The Open Toolkit is suitable for games. devices[1]. discover and modify their properties. Example 4: restore all devices to their default settings using OpenTK.60).DisplayDevice to query available display devices. Console. foreach (DisplayDevice device in DisplayDevice. devices[0].WriteLine(device.RestoreResolution(). projectors and TV screens. } } Example 2: set the resolution of the first device to 800x600x32@60Hz: using OpenTK.IsPrimary).

and at least basic knowledge of OpenGL. In GameWindow. you get more for free! Just as in the GameWindow case.. Apart from those (minor) issues. it is quite a different approach one has to take when designing a game/application using the GLControl in a Windows. you'll have to rethink that fundamentally. no "garbage" is seen anymore in the design view of Visual Studio. For example. It also assumes a top-to-bottom readthrough. If you come from a "main-loop-background" (C/SDL/Allegro etc.Forms + GLControl based application Note 1: This tutorial is somewhat dated. [Todo: add information about hotplugging support] The GameWindow class [Describe the functionality of the GameWindow class] The NativeWindow class [Describe the functionality of the NativeWindow class] Building a Windows.Forms. because of how the underlying windowing system works [someone with more detailed knowledge than me may want to elaborate on this. not black. so with the right drivers installed it will be hardware accelerated. please update the below page (by clicking the "Edit" link above when logged in) to reflect the current state of OpenTK. with large windows it will be slower than the corresponding fullscreen GameWindow. You can specify a negative number or zero to indicate that a specific parameter is of no interest: for example. this tutorial will get you started using OpenTK+Windows. specifying a refresh rate of 0 will result in the default refresh rate being used.) when it comes to coding games.Form compared to using the GameWindow.current DisplayResolution will be used. it is a guide and not a reference. Failing to call this method will result in undefined behavior. And the default color is beige. GLControl is more low-level than GameWindow so you'll have to pull the strings on for example time measurements by yourself. GLControl uses the default OpenGL driver of the system. Note that it is your responsibility to call RestoreResolution() prior to exiting your application. However.Forms application development in Visual Studio 2005/C#. You'll have to change into a .]. To begin with. Note 2: If you have some spare time and want to contribute to the OpenTK project. This tutorial assumes familiarity with Windows.

Forms+GLControl based application. pick "Choose Items.. You want to have an embedded OpenGL rendering panel inside an already existing Windows. . You want to be able to do drag-and-drop into the rendering panel. Make sure you can find the "GLControl" listed in the ".mindset of "what event should I hook into. 2. You want to build a GUI-rich/RAD kind of application using the Windows.Forms application. Adding the GLControl to your Windows.. Why use Windows.dll." and browse for OpenTK. Your mileage may vary if using VS2008 or Monodevelop -. gotchas and whys for you.Form (I am assuming you are using Visual Studio 2005 Express Edition. Right click in some empty space of the Toolbox. here's the steps. 3. for example dropping a model file into a model viewer.Forms controls.I don't know the details for them -. and what events should I trigger. create a Form on which you will place your GLControl.but the follow sections should be the same no matter how you add the GLControl) To begin with. Eg.NET Framework Components".Forms + embedded GLControl compared to a windowed GameWindow?" Here are some reasons why you would like to add that complexity: 1. and when?" instead. Assuming you've got at least one of those reasons to build a Windows.Forms+GLControl instead of GameWindow? The first thing you'll have to decide is: "Do I really need the added complexity of Windows. level editors or model viewers/editors may be in this category while a windowed game leans more towards a GameWindow kind of application. as in the image below.

Under the hood. Don't touch glControl/GL .* commands (or Glu for that matter!). Order of creation The fact that glControl1's GLContext is created in runtime is important to remember however. The conceptual order is this: 1. The same is true for any GL. First the Windows. since you cannot access or change glControl1's properties reliably until the GLContext has been created. Don't touch glControl/GL 2. The first thing you'll notice is the "junk graphics" inside glControl1. and this context is only created in runtime.Form constructor runs. So no worries. A GLControl named glControl1 will be added to your Form. Then the Load event of the form is triggered. not in design time. GLControl uses a so called GLContext to do the actual GL-rendering and so on.NET control.Then you can add the GLControl to your form as any .

EventArgs e) { loaded = true. Then in any event handler where you are going to access glControl1/GL you can put a guard first of all: private void glControl1_Resize(object sender. } A popular way of adding a Load event handler to a Form is via the Properties window of Visual Studio. using OpenTK.Load event instead until this issue of OpenTK is fixed. any event handler may touch So one approach to address this problem is having a bool loaded=false. public partial class Form1 : Form { bool loaded = false.please use the Form. Then the Load event of the GLControl is triggered. EventArgs e) { if (!loaded) return. 2.Graphics.3.Graphics. 3. } private void glControl1_Load(object sender. OK to touch 4.OpenGL. handler has run. 4. } } Note: the GLControl. which is set to true in the Load event handler of the GLControl: using OpenTK. member variable in your Form. PaintEventArgs e) { . Click anywhere on the GLControl in the Designer Make sure glControl1 is listed in the Properties window Click the "Events" button to list all events of glControl1 Double-click the empty cell right of the Load event to create and hook an event handler for the Load event Hello World! The absolutely minimal code you can add at this stage to see something is adding an event handler to the Paint event of glControl1 and filling it with this: private void glControl1_Paint(object sender. glControl/GL After the Load event glControl/GL. public Form1() { InitializeComponent().Load event is never fired -. something like this: 1.

NET Colors can be used directly! } . glControl1. which we have to clear using GL. Notice that the GLControl provides a color.ClearColor(Color.DepthBufferBit). GL.SwapBuffers(). // Yey! .ColorBufferBit | ClearBufferMask. EventArgs e) { loaded = true. [TODO: how to control which buffers and formats the GLControl has? Possible at all?] Next thing would be setting the clear color.and a depth buffer.Clear().Clear(ClearBufferMask. } Yes! A black viewport. GL.SkyBlue). An appropriate place to do GL initialization is in the form's Load event handler: private void glControl1_Load(object sender.if (!loaded) // Play nice return.

GL. I put the viewport initialization in a separate method to make it a little more readable.ClearColor(Color. w. h. 1). GL. SetupViewport(). 0) GL.Height. // Use all of the glControl painting area } And between Clear() and SwapBuffers() our yellow triangle: private void glControl1_Paint(object sender. PaintEventArgs e) { if (!loaded) .Viewport initialization Next thing we want to do is draw a single yellow triangle. 0. GL. // Bottom-left corner pixel has coordinate (0.Ortho(). First we need to be good OpenGL citizen and setup an orthographic projection matrix using GL. private void glControl1_Load(object sender.Projection). We need to call GL. -1. } private void SetupViewport() { int w = glControl1.Ortho(0. int h = glControl1. For now we'll add this in the Load event handler by the other initialization code -ignoring the fact that we may want to allow the user to resize the window/GLControl.SkyBlue).Viewport() also.MatrixMode(MatrixMode. 0.Width. GL. w.LoadIdentity(). h). We'll look into window resizing later.Viewport(0. EventArgs e) { loaded = true.

Begin(BeginMode.Forms key events and using the OpenTK KeyboardDevice. GL.Vertex2(100. Since the rest of our program resides in the Windows. We'll have an int x=0. } Voila! Keyboard input Next thing we want to do is animate the triangle via user interaction. means the glControl has to be focused.Vertex2(100.Space) . GL.Yellow). GL. GL.Forms world (our window might be a very small part of a large GUI) we'll play nice and use Windows.MatrixMode(MatrixMode. The two general approaches to keyboard input in a GLControl scenario is using Windows. int x = 0.LoadIdentity(). GL.Clear(ClearBufferMask. GL. GL. we want the triangle to move one pixel right. 50).DepthBufferBit).Color3(Color.Forms key events in this guide.ColorBufferBit | ClearBufferMask. clicked by the user for key events to be sent to our handler. 20). GL.Vertex2(10. glControl1. 20). Adding it to the glControl1 and not the Form.return.Modelview).End().Triangles). Every time Space is pressed. private void glControl1_KeyDown(object sender.KeyCode == Keys. GL. ie. variable that we'll increment in a KeyDown event handler. KeyEventArgs e) { if (e.SwapBuffers().

} We add GL. Only on resize.Clear(ClearBufferMask. if (e. 0).Focused) // Simple enough :) . glControl1..Invalidate().LoadIdentity().Modelview).ColorBufferBit | ClearBufferMask. GL. glControl1 is not painted all the time.Translate(x. KeyEventArgs e) { if (!loaded) return. // position triangle according to our x variable .. Easy. GL. } . obscured window and a couple of more situations do Paint events actually get triggered. and we run our program. } } Focus behaviour If you're anything like me you're wondering a little how this focusing behaves. What we would like to do is have a way of telling the window manager "This control needs to be repainted since the data it relies on has changed".MatrixMode(MatrixMode.Translate() to our Paint event handler: private void glControl1_Paint(object sender.Space) { x++. PaintEventArgs e) { . But wait! Nothing happens when we push Space! The reason is. GL.DepthBufferBit). let's find out! A simple way is painting the triangle yellow when glControl1 is focused and blue when it is not.KeyCode == Keys. the operating systems window manager (Windows/X/OSX) makes sure as few Paint events as possible happens. Right: private void glControl1_Paint(object sender.x++. 0.. We want to notify the window manager that our glControl1 should be repainted.. if (glControl1.. GL. PaintEventArgs e) { if (!loaded) return. Invalidate() to the resque: private void glControl1_KeyDown(object sender.

GL. Well it seems our SetupViewport() will come in handy once more! Add an event handler to the Resize event of glControl1 and fill it in: private void glControl1_Resize(object sender. EventArgs e) { SetupViewport(). it's Resize event is triggered.Color3(Color.. } There is still one problem though: if you shrink the window using eg. Space should work alright. That's because the window manager makes assumptions about where the (0. GL. the bottom-right resize grip of the window. Our general fix to alleviate this problem is instructing the window manager that we really want the repaint to occur upon any resize event: private void glControl1_Resize(object sender.Color3(Color.the triangle is repainted continously!). } So now anytime the triangle is yellow. else GL.Yellow). } . 0) pixel of a control resides. The other piece to find is "What do we need to update when a GLControl is resized?" and the answer is "The viewport and the projection matrix". That is true for glControl1 too.Blue). (Try resizing using the top-left grip instead .Begin(BeginMode. glControl1. no repaint will trigger automatically. and when it's blue any keyboard input will be ignored.Forms control is resized. Freedom at last: resizing the window We will now turn our attention to a sore teeth: window resizing.Triangles).Invalidate(). EventArgs e) { SetupViewport(). namely in the top-left corner of the control. That's one piece in the puzzle.. . Anytime a Windows.

Now there are several ways to achieve this.Forms thread. . changing rotation in the timers Tick event handler. But we don't have any loop. void Application_Idle(object sender. EventArgs e) { } One good thing about the Idle event is that the corresponding event handlers are executed on the Windows. Another is adding a wild Thread to the soup. float rotation = 0. It is not associated with any Form or other control. SetupViewport(). Application.Idle += new EventHandler(Application_Idle). We want an event triggered every now and then. GL.IsIdle) { rotation += 1. We will take a third path and use a Windows.I want my main loop: driving animation using Application. One is adding a Timer control to our form. That is good since it means we can access all GUI controls without having to worry about threading issues. // press TAB twice after += } void Application_Idle(object sender. EventArgs e) { loaded = true.Forms event designed just for the purpose of being executed "when nothing else is going on": meet the Application. a pain we would have to deal with if we cooked our own thread. EventArgs e) { // no guard needed -.ClearColor(Color. but with the program as a whole. what if we wanted our triangle to rotate continously? This would be childs play in a main loop scenario: just increment a rotation variable in the main loop. The first is a little too high-level and slow while the second is really low-level and a bit harder to get right. This event is special in a number of ways as you may have guessed already.for example in the Load event: private void glControl1_Load(object sender. you'll have to add it manually -.we hooked into the event in Load handler while (glControl1.Idle So.Idle event. We only have events! To mend for this lack of continuity we have to force Windows. fast enough to get that realtime interactive feeling. before we render the triangle.Forms to do it our way. So we simply increment our rotation variable in the Idle event handler and Invalidate() glControl1 -.SkyBlue). You cannot hook into it from the GUI as usual.

if (glControl1. don't you?) The reason is that windowed 3d rendering just is a lot slower than full-screen rendering.Focused) GL. } private void glControl1_Paint(object sender.Render().Rotate(rotation. Vector3.Color3(Color.Yellow)..UnitZ). // OpenTK has this nice Vector3 class! GL.Triangles). else GL.Color3(Color.. But you can reduce the damage by using a technique called frame-rate independent animation. but you want your game to run on your neighbours computer too. .. } Happy wonders! Look: The triangle rotates slower when the window is big! How come? (This might not be true if you have a super-fast-computer with a super-fast-graphicscard. GL. The idea is simple: increment the rotation variable not with 1 but with .. in general.Begin(BeginMode. PaintEventArgs e) { Render().Blue). } } Let's update our rendering code while we're at it: private void Render() { .

TotalMilliseconds. if we could measure the time it takes to perform the glControl amount that depends on the current rendering speed (if the speed is has a granularity of 10 or more milliseconds. sw. double milliseconds = sw.0 there is a class available to do high-precision time measurements called Stopwatch.TotalMilliseconds.. rotation += deltaRotation. how long time it takes to render a frame. we would be close to making some kind of frame-rate-independent animation.Start(). increment rotation with a larger amount than if the speed is high). Since . But you need to be able to measure the current rendering speed or. Here's how to use it: Stopwatch sw = new Stopwatch().Elapsed. but everything that has been going on since our last Idle run: Stopwatch sw = new Stopwatch(). It's quite simple now that we've got a Stopwatch. But there is an even more elegant way: let's measure all time that is not Application.. glControl1. } Cool! The triangle spins with the same speed regardless of window size. sw. (don't try with DateTime. EventArgs e) { // no guard needed -.Start(). MyAdvancedAlgorithm().Start().Now -.. void Application_Idle(object sender. // reset stopwatch sw.Reset().Elapsed.worthless.) Now. equivalently. I want an FPS counter! Yeah me too.NET2.Stop(). // we've measured everything since last Idle run double milliseconds = sw. which is in the same size as typical frame rendering -.Stop(). // restart stopwatch // increase rotation by an amount proportional to the // total time since last Idle run float deltaRotation = (float)milliseconds / 20. EventArgs e) { .Idle time! Then we'll be sure it is not just the painting that is measured. sw. sw. .Invalidate(). // available to all event handlers private void glControl1_Load(object sender. // start at application boot } float rotation = 0.0f.we hooked into the event in Load handler sw.

TotalMilliseconds. } double accumulator = 0. rotation += deltaRotation.Stop().Reset().ToString(). private void Animate(double milliseconds) { float deltaRotation = (float)milliseconds / 20. } float rotation = 0. if (accumulator > 1000) { label1. glControl1. int idleCounter = 0. Accumulate(milliseconds). return timeslice.The idea is just counting the Idle runs. accumulator -= 1000. Animate(milliseconds).Invalidate(). double timeslice = sw. so we need an accumulator variable adding all time slices together. Quite a lot of logic started to add up in the Idle event handler. accumulator += milliseconds. private void Accumulate(double milliseconds) { idleCounter++.0f. sw. // don't forget to reset the counter! } } Our FPS counter in all its glory: . and every second or so. so I split it up a little: void Application_Idle(object sender. sw. } private double ComputeTimeSlice() { sw.Start(). idleCounter = 0.Elapsed. EventArgs e) { double milliseconds = ComputeTimeSlice(). update a Label control with the counter! But we'll have to know when a second has passed.Text = idleCounter.

// Yey! . using System.Graphics.Diagnostics.Data. using OpenTK. using System.Text. Application.Can't you put the complete source code somewhere? Sure. EventArgs e) { .NET Colors can be used directly! SetupViewport().OpenGL. } Stopwatch sw = new Stopwatch(). // start at application boot } void Application_Idle(object sender. // available to all event handlers private void glControl1_Load(object sender. GL. // press TAB twice after += sw. using System.Windows.Forms. using System. namespace GLControlApp { public partial class Form1 : Form { bool loaded = false.ComponentModel.ClearColor(Color. here it is: using System. using System.Graphics. using OpenTK. EventArgs e) { loaded = true.Start(). public Form1() { InitializeComponent().SkyBlue). using System.Drawing.Idle += new EventHandler(Application_Idle).

if (accumulator > 1000) { label1.Clear(ClearBufferMask.Elapsed. -1.LoadIdentity().Ortho(0. Animate(milliseconds). accumulator += milliseconds. 0. sw. idleCounter = 0. // Bottom-left corner pixel has coordinate (0. h. } float rotation = 0.Translate(x. return timeslice. PaintEventArgs e) { if (!loaded) return. private void Accumulate(double milliseconds) { idleCounter++.Projection). . GL. GL. GL. glControl1.Stop().Viewport(0. int h = glControl1.Start().MatrixMode(MatrixMode.double milliseconds = ComputeTimeSlice(). 0) GL. double timeslice = sw. 0). accumulator -= 1000.Invalidate(). w. } private void SetupViewport() { int w = glControl1. } double accumulator = 0.LoadIdentity(). h). GL.ToString().ColorBufferBit | ClearBufferMask. // don't forget to reset the counter! } } private double ComputeTimeSlice() { sw. 0.Reset(). w. rotation += deltaRotation.DepthBufferBit).0f.Width. 0.Text = idleCounter. 1). int idleCounter = 0.Modelview).Height. Accumulate(milliseconds). private void Animate(double milliseconds) { float deltaRotation = (float)milliseconds / 20. GL.TotalMilliseconds. sw.MatrixMode(MatrixMode. GL. GL. // Use all of the glControl painting area } private void glControl1_Paint(object sender.

Rotate(rotation. And you have added handlers to the Paint event of both.* method! . glCtrl1.UnitZ).DepthBufferBit). EventArgs e) { SetupViewport().KeyCode == Keys.Color3(Color. Let's say you have one GLControl named glCtrl1 and one named glCtrl2. This is what you do in the event handler of glCtrl1 (of course you do something similar in the event handler of glCtrl2!): private void glCtrl1_Paint(object sender. GL.Vertex2(100. It is even simple! All you have to do is "make the appropriate GLControl current".Color3(Color. GL. // Ohh. It's that simple? GL.Vertex2(10.if (glControl1. // OpenTK has this nice Vector3 class! GL. GL. 20). . } } } Next step: What about multiple GLControls at once? Yeah it is possible.MakeCurrent(). Vector3.* or Glu. 20).Vertex2(100. glControl1. } } private void glControl1_Resize(object sender.ColorBufferBit | ClearBufferMask. else GL..Clear(ClearBufferMask..Triangles). glControl1. private void glControl1_KeyDown(object sender. KeyEventArgs e) { if (e. GL. glControl1.Yellow)..Begin(BeginMode.SwapBuffers().Focused) GL.Invalidate(). GL. } The same is true for any code calling a GL.Invalidate(). 50).End(). } int x = 0. PaintEventArgs e) { if (!loaded) return.Blue).Space) { x++.

1. with the GL.UnitZ. Classes are passed by reference by default in C#. use the ref and out overloads. OpenGL 3.Add(ref v1. 2. The most popular example is Vertex Arrays. This page describes a few rules you need to keep in mind: Rules of thumb 1. A good rule of thumb is to make no more than 300500 OpenGL calls per frame. but measurable. while the OpenGL/OpenAL bindings are quite optimized. Vector3 v3 = Vector3. ref v2. This is because Vector3. Matrix4 etc.ShareContexts property. 6. not classes. v3 = v1 + v2. 4. Try to minimize the number of OpenGL calls per frame. out v3). Use server storage rather than client storage. To minimize the impact of this overhead. It is strongly recommended that you replace legacy Vertex Arrays with Vertex Buffer Objects. as the garbage collector (GC) may move the contents of the buffer in memory. 5. display lists. Vector3 v2 = Vector3. etc. You can disable this behavior via the GraphicsContext.NET). the transition from managed into unmanaged code incurs a small. // requires three copies. This means. which can be achieved by avoiding Immediate Mode. when compared to plain C.Zero. Avoiding pitfalls in the managed world This text is intended for someone with a C/OpenGL background. For optimal math routine performance. created on any other context. which do not suffer from this problem. 3. but a little more important in the OpenTK case. are structures. Vector3 v1 = Vector3. fast! . This approach cannot be used in a Garbage Collected environment (as .***Pointer family of functions. overhead. some things work slightly differently in the managed world. // nothing is copied. in favour of Display Lists and VBO's. Vector3.0 will not contain any functions with client storage.Although each GLControl has its own GraphicsContext. This is true for any programming environment utilizing OpenGL. try to minimize the number of OpenGL/OpenAL calls. Even though OpenTK automatically translates GL/AL calls from C# to C. Unlike OpenGL 2.UnitX. slow. OpenTK will share OpenGL resources by default. A few legacy OpenGL functions use pointers to memory managed by the user. any context can use textures. 7.

.The same holds true when calling OpenGL functions: GL.Now or other DateTime-based method on any periods shorter than a couple of seconds. 6.0f. fast! // copy the whole v1 structure. it may even go backwards on occasion!) Using DateTime to measure very long operations (several seconds) is OK. you can use the commercial tool gDEBugger [anyone: any similar tools for Mac? Any free tool for Linux?] References: http://www. They are generally nicer to handle than the arrays which the C API expects. 1. 1. Chapter 3: OpenTK.Math To help you write cleaner code. Stopwatch sw = new Stopwatch(). .Stop(). Note: Avoid using DateTime. sw.0 / Mono 1. Function overloads like this can be found all over OpenTK. Use it like this: 3.Vertex3(ref v1.0f.Cyan ). slower! Measuring performance 1. OpenTK defines commonly used vectors. 1.0f. is via the . GL. If you are on Windows.gamedev.Audio.0f ).Color( 0.0f ).Elapsed. 4. GL. GameWindow provides built-in frames-per-second counters [Someone with confidence in the details please fill in here] However.4 Stopwatch class. (rumour has it.Color() in various ways.TotalMilliseconds.Drawing library GL. you can download Fraps to measure how many frames per second are rendered in your application. since its granularity is 10 ms or worse.Start(). 2. // requires the System. For example: OpenTK allows you to set the parameters of GL. 1.Color( MyColor ).2. double ms = sw. 5. 7. where applicable. the color cyan is used in this case. // Your code goes here. GL.Graphics and OpenTK.Color( Color.0f. // pass a pointer to v1. quaternions and matrices to extend the standard scalar types.X). A simple and convenient way to measure the performance of your code.Vertex3(v1). Vector3 MyColor = new Vector3( 0.. For linux (and Windows). GLControl does not.asp?topic_id=484756&whichp.NET 2.

3-component vector of the type Double. The implications of this can be read here. Vector2d TexCoord = new Vector2d( 0. Vector3h Normal = (Vector3h)Vector3.Overview Everything listed below is type-safe.4-component vector of the type Single.standard scalar type with 64 Bits of precision.4-component vector of the type Double.2.4x4 double-precision Matrix. Casts For symmetry with established types.Math types can be cast and allow serialization.4-component vector of the type Half. Vectors    Half-precision Floating-point o Half . o Vector2 . Row-Major Matrices    Matrix3d . Quaterniond .2-component vector of the type Single. o Vector3d . Quaternion   Quaternion .UnitX. o Vector3h . 0. Single-precision Floating-point o Single . o Vector4d . o Vector2d .double-precision floating-point Quaternion using 4 components. Matrix4d .3-component vector of the type scalar type with 16 Bits of precision.standard scalar type with 32 Bits of precision.single-precision floating-point Quaternion using 4 components.5 ). These are all value types (struct). o Vector2h . More information can be found here.3x3 double-precision Matrix.2-component vector of the type Double. o Vector4 . Matrix4 .2-component vector of the type Half.4x4 single-precision Matrix. Instance and Static Methods . o Vector4h . Vector2h HalfTexCoord = (Vector2h)TexCoord. o Vector3 . code using these types will work across all supported platforms.3-component vector of the type Single. Double-precision Floating-point o Double . not reference types. all OpenTK.

It is highly recommended you take a look at this resource to learn about the essential concepts in OpenGL.Math is specifically designed for computer graphics.Transform( Vector4.UnitX.Identity ). while others are static. . This is sometimes called the Significand. Chapter 4: OpenTK. Matrix4. Vector3 Normal = Vector3. 1. it does not support arithmetic operations. but called Mantissa in all OpenTK documentation. Normal.UnitX.. your System requires appropriate drivers for hardware acceleration.. Try to stay within [0.Normalize().PI to Half will result in 3. 10 Mantissa Bits. It might not be obvious that some functionality is only available for instances of the structs. 5 Exponent Bits.. you have to perform any calculations with it in single. When using the Half type you should be aware of it's poor precision:    Casting Math. the function reference and inline documentation serve that purpose.0 . So if you want to use Half for Texture Coordinates or Normals. Internally the 16 Bit are represented similar to IEEE floating-point numbers:    1 Sign Bit.or double-precision first and then cast the final result to half-precision. It is important to understand that this type is a pure container to transfer data to or from the GPU. which can become huge at high resolutions. the worse the Epsilon gets.Graphics (OpenGL and ES) In order to use OpenGL functions. because it can help your vertex struct to stay within the 16 or 32 Byte boundary. It is commonly used to reduce the memory footprint of floating-point textures. The further away the value of the floating-point number gets from 1.1406. Numbers above 2048 and below -2048 will be rounded to the closest representable number. It is also useful for Vertex attributes.0] range for best accuracy. Half-Type The new half-precision floating-point type in OpenTK.0.The exact methods for each struct would be too numberous to list here. The OpenGL Programming Guide is a book written by Silicon Graphics engineers and will introduce the reader into graphics programming. which processors like. Vector4 TransformedVector = Vector4.

. resulting in a 2. Use GraphicsMode. As per the OpenGL specs. etc.Default GameWindow window = new GameWindow(). The context routes your OpenGL commands to the hardware driver for execution . You can request an embedded (ES) context by specifying GraphicsContextFlags. 3. and how to use OpenTK. compatible mode or specify the color.0-compatible GraphicsContext with 32bpp color.Embedded to the flags parameter. the context will use the highest version that is compatible with the version you specified. 4). 24.GraphicsContext is a cross-platform wrapper around an OpenGL context. If you are creating the context manually.0-compatible GraphicsContext with GraphicsMode. The default values are 1 and 0 respectively. The default value will construct a desktop (regular OpenGL) context. [Constructors] OpenTK creates a GraphicsContext automatically as part of the GameWindow and GLControl classes:     You can specify the desired GraphicsMode of the context using the mode parameter.which means you cannot use any OpenGL commands without a valid GraphicsContext. // Creates a 3. Todo: Missing Pages    window-related. You can specify the OpenGL version you wish to use through the major and minor parameters.1 context.These pages are more focused about OpenTK specific changes to the C API. GLControl control = new GLControl(new GraphicsMode(32. you must specify a valid IWindowInfo instance to the window parameter (see below). depth. stencil and anti-aliasing level manually.Utility related The GraphicsContext class [Introduction] The OpenTK. 24bpp depth // 8bpp stencil and 4x anti-aliasing.Default to set a default. OpenTK. 8. (brings the reader to the level of a Quickstart Template) GLSL related changes. Examples: // Creates a 1. 0).Graphics.Utility classes to assist with some common tasks. This is the default window the GraphicsContext will draw on and can be modified later using the MakeCurrent method.

Run() // Note 2: invisible windows fail the pixel ownership test. Context. context_ready.IsBackground = true. instead. thread. you can also instantiate a GraphicsContext manually. Config = OpenTK. // handle . This is very simple to achieve: // The new context must be created on a new thread // (see remarks section.Exists) { window.AutoReset).the X11 root window // visual . // you cannot use an invisible window for offscreen rendering. EventWaitHandle context_ready = new EventWaitHandle(false. if (Config. window. Thread thread = new Thread(() => { INativeWindow window = new NativeWindow(). as indicated below: using using using using OpenTK.Platform. while (window.Sometimes. // Limit CPU usage. context. i.Default. // Note 1: new windows remain invisible unless you call // INativeWindow.WaitOne().Start().Sleep(1).Platform.the desired X11 visual for the window IWindowInfo wi = null.the X11 screen id // root .MakeCurrent(null). If necessary.e. thread.a Win32. OpenTK. you will need to provide an amount of platform-specific information. Utilities = OpenTK.Configuration. MakeCurrent().Graphics.WindowInfo). EventResetMode.Visible = true or IGameWindow.ProcessEvents(). // Note 4: this code can be used as-is in a GLControl or GameWindow.RunningOnWindows) . if necessary } }). // You will need to use a framebuffer object. // Note 3: context sharing will fail if the main context is in use.MakeCurrent(window.WindowInfo). For this.Utilities. X11 or Carbon window handle // display . // Create an IWindowInfo for the window we wish to render on. // This means that the contents of the front buffer are undefined. you might wish to create a second context for your application. IGraphicsContext context = new GraphicsContext(GraphicsMode.the X11 display connection // screen . below) // We need to create a new window for the new context. // Perform your processing here Thread. A typical use case is for background loading of resources.

CreateX11WindowInfo(display. [Remarks] A single GraphicsContext may be current on a single thread at a time.RunningOnMacOS) wi = Utilities. A common use-case is integration of OpenGL 3.issuing OpenGL commands from a thread without a current context may result in a GraphicsContextMissingException. wi).Graphics into an existing application.CreateDummyContext(). // Construct a new IGraphicsContext using the IWindowInfo from above. This is a safeguard placed by OpenTK: under normal circumstances. it is usually advisable to restrict all OpenGL commands to a single thread. All OpenGL commands are routed to the context which is current on the calling thread . If you wish to use OpenGL on multiple threads. For this reason. false). screen.Default. If a context is already current on this thread. visual). it will be replaced and made non-current. you'd get an abrupt and unexplained crash. [Methods]  MakeCurrent You can use the MakeCurrent() instance method to make a context current on the calling thread.wi = Utilities.Graphics with windows created through SDL or other libraries: // The 'external' context has to be current on the calling thread: GraphicsContext context = GraphicsContext.CreateWindowsWindowInfo(handle). This allows you to use OpenTK. Both alternatives can be quite complicated to implement correctly. you can either: o o create one OpenGL context for each thread.RunningOnX11) wi = Utilities.CurrentContext static property. it is possible to instantiate a 'dummy' GraphicsContext for any OpenGL context created outside of OpenTK. else if (Config. You can make a context non-current by calling MakeCurrent(null) on the correct thread. The GLControl provides the . IGraphicsContext context = new GraphicsContext(GraphicsMode. typically your main application thread. or use MakeCurrent() to make move a single context between threads.trying to make it current on two or more threads will result in an error.x through OpenTK. A single context may be current on a single thread at any moment . To retrieve the current context use the GraphicsContext. root. false. handle. and use asynchronous method calls to invoke OpenGL from different threads. Finally.CreateMacOSCarbonWindowInfo(handle. else if (Config.

back-right. GameWindow and GLControl also offer this parameter in their constructors. both GameWindow and GLControl use a cached GraphicsContext for efficiency.Clear(ClearBufferMask. In other words.Forms. GL. Note that caching the current context will be more efficient than retrieving it through GraphicsContext. Resize or Paint event. like GL. you must call MakeCurrent() on the correct GLControl for each Load. [Stereoscopic rendering] You can create a GraphicsContext that supports stereoscopic rendering (also known as "quad-buffer stereo").ColorBufferBit | ClearBufferMask. GLControl. Contexts that support quad-buffer stereo distinguish the back and front buffers between "left" and "right" buffer. The contents of the new back buffer are undefined after the call to SwapBuffers().CurrentContext. by setting the stereo parameter to true in the context constructor.SwapBuffers(). Single-buffered contexts are considered deprecated. The SwapBuffers() method "swaps" the front and back buffers and displays the result of the rendering commands to the user.Windows. For this reason.  SwapBuffers OpenTK creates double-buffered contexts by default. To use multiple contexts on a single thread. call MakeCurrent to select the correct context prior to any OpenGL commands. A double-buffered context offers two color buffers: a "back" buffer. the context contains four distinct color buffers (hence the name): back-left.GLControl. Check out the stereoscopic rendering page for more information ([Todo: add article and link]). The GameWindow does not provide a similar API. since they do not work correctly with compositors found on modern operating systems.CurrentContext.DepthBufferBit).Application thread. if you have two GLControls on a single form. For example.MakeCurrent() and GameWindow. where all rendering takes place. The typical rendering process looks similar to this: // Clear the back buffer.DrawElements // Display the final rendering to the user GraphicsContext.BeginInvoke() method to simplify asynchronous method calls from secondary threads to the main System. // Issue rendering commands. front-left and front-right.MakeCurrent() are instance methods that simplify the handling of contexts. . and a "front" buffer which is displayed to the user.

This method will return a new GraphicsContext instance for the context that is current on the calling thread. To access this information. In other words.Please note that quad-buffer stereo is typically not supported on consumer video cards.Glfw. OpenTK. like Ati's FireGL/FirePro or Nvidia's Quadro series.1. [Accessing internal information] GraphicsContext hides an amount of low-level. you can pass the handle (IntPtr) of a specific external context to CreateDummyContext. For example. OpenTK requires the existence of an OpenGL context prior to the initialization of the OpenGL subsystem. 8. 8. Using an external OpenGL context with OpenTK Starting with version 0.glfwOpenWindow(640. you cannot use any OpenGL methods prior to the creation of a GraphicsContext.Glfw. Trying to enable stereo rendering on consumer video cards will typically result in a context without stereo capabilities. You will need a workstation-class video card.GetAddress("glGenFramebufferEXT"). You will typically call this method as soon as the external context is created.Context. Optionally. 480. 16.Graphics. Tao.Handle. 0.GraphicsContext. If you create the OpenGL context through an external library (for example SDL or GTK#). Please note that it is an error to call CreateDummyContext() multiple times for the same external context. Textures . you will need to inform OpenTK of the context's existence using the GraphicsContext. This information includes the raw context handle. to enable stereo rendering.9.Graphics methods normally. IntPtr function_address = (my_context as IGraphicsContextInternal). the platform-specific IGraphicsContext implementation and methods to initialize OpenGL entry points (GetAddress() and LoadAll()). the external context need not be current on the calling thread. 8.Glfw: Tao. implementation-specific information behind the IGraphicsContextInternal interface.CreateDummyContext() static method. in this case. 8.GLFW_WINDOW). // You may now use OpenTK.CreateDummyContext(). using Tao. cast your GraphicsContext to IGraphicsContextInternal: IntPtr raw_context_handle = (my_context as IGraphicsContextInternal).

PixelFormat. bmp_data.Imaging.Texture2D. return id. 0. int id = GL.Enable) and bind the texture (GL. so disable mipmapping (otherwise the texture will not appear).Drawing.BindTexture) prior to rendering. bmp. 0. GIF. you should enable texturing (with GL. use TextureMinFilter.Bitmap class (MSDN documentation).Uniform1) and use it in your shaders.ReadOnly.LockBits(new Rectangle(0.PixelFormat. This class can decode BMP.Height). Frame Buffer Objects and Pixel Buffer Objects. we can use GL.TexParameter(TextureTarget.The following pages will describe the concepts of OpenGL Textures. (int)TextureMagFilter.Graphics.UnlockBits(bmp_data).GenTexture().Width. int LoadTexture(string filename) { if (String. Bitmap bmp = new Bitmap(filename).IsNullOrEmpty(filename)) throw new ArgumentException(filename). using System. TextureParameterName. bmp. Here is how: using System.Graphics.GenerateMipmaps() to create // mipmaps automatically.Texture2D. PixelType. GL. bmp_data.LinearMipmapLinear to enable them.TextureMinFilter.Texture2D. 0. it is useful to know how to actually load a texture into OpenGL.Linear). (int)TextureMinFilter. These concepts apply equally to OpenGL and OpenGL|ES . } Now you can bind this texture id to a sampler (with GL.Height.Ext. bmp_data. TextureParameterName. Bitmap bmp_data = bitmap.Format32bppArgb).OpenGL.OpenGL. PNG and TIFF images into system memory. id).Bgra. using OpenTK.Drawing. In that case. GL. PixelInternalFormat. // We haven't uploaded mipmaps. OpenTK. bmp.BindTexture(TextureTarget. EXIG.UnsignedByte. so the only thing we have to do is send the decoded data to OpenGL.GenerateMipmaps() or GL.Imaging.Drawing. // On newer video cards.TexImage2D(TextureTarget.Scan0). GL.Texture2D.Linear). System. If you are not using shaders. GL.TexParameter(TextureTarget. . ImageLockMode.Width. A simple way to achieve this is to use the System.Drawing. JPG.Rgba.TextureMagFilter.differences between the two will be noted in the text or in the example source code. Loading a texture from disk Before going into technical details about textures in the graphics pipeline.

... o All filter modes are allowed..0f]x[0. o Texture Coordinates are addressed parametrically: [0. 1.. Note that 1 and 2 both use the same tokens. GL.g: 640*480. (Exception: S3TC Texture Compression does not allow borders) 2. GL.2 drivers.g: 640*480.SupportsExtension( "ARB_texture_rectangle" ) must evaluate to true.. 1.0f] o All wrap modes are allowed. 1... S3 Texture Compression Introduction A widely available Texture Compression comes from S3. (default is ClampToEdge) o Borders are not supported. o MipMaps are allowed. o All filter modes are allowed. Texture2D Power of two sized (POTS) E. . mostly due to Microsoft licensing it and including it into DirectX. The only difference between them is the size. E.0f] o All wrap modes are allowed. o Borders are supported. There exist 3 kinds of 2D textures: 1. (Exception: S3TC Texture Compression does not allow borders) 3. which is basically a copy of the Texture in Video Memory. It was added into the file format DDS (DirectDraw Surface).0f]x[0.0f . o MipMaps are not allowed. (default is Linear) o Texture Coordinates are addressed non-parametrically: [0. o Borders are supported.2D Texture differences The most commonly used textures are 2-dimensional.g: 1024² These are supported on all OpenGL 1..height] o Only Clamp and ClampToEdge wrap modes are allowed. TextureRectangle Arbitrary size. o Texture Coordinates are addressed parametrically: [0.width]x[0.0f . Texture2D Non power of two sized (NPOTS) E.0f . o Only Nearest and Linear filter modes are allowed.SupportsExtension( "ARB_texture_non_power_of_two" ) must evaluate to true. o MipMaps are allowed. 1.0f ..

Accuracy: R5G6B5A8 DXT5. as they offer no beneficial functionality over established formats. Compressed vs. However in OpenGL you typically draw more than a single Pixel. every bilinear lookup requires reading 4 Texels. Graphic cards usually support this locality by using a small . 16 Bytes per Block. there is a catch involved when reliably shrinking an image to 25% of it's uncompressed size: A lossy compression technique. Although both formats . load times and also render times. alot of Pixels will be very close to each other. DXT1. When the Triangle is rasterized. This means that the worst case scenario involves reading 4 Blocks. This is the ideal and not a restriction. Avoiding DXT2/4 is strongly recommended. is different to the one used in JPG compression. if you would only draw a single Pixel on the screen all this would not bring any noticable performance gains. but usually only 1-2 Blocks are used to achieve the bilinear lookup. which translates into smaller disk size. the S3TC format was developed with graphics hardware in mind.are designed to compress an Image. And yes. which can be altered by tweaking the Filter options when compressing the image. or a close neighbour. which means their 2x2 lookup is very likely in the same 4x4 Block used by the last lookup. Since a Block consists of 4x4 Texels. but the uncompressed 4 Texels of RGBA need 16 Bytes too. That's why those formats have barely been used.which must be examined for the bilinear lookup . The Formats are named DXTn. where ( 1 <= n <= 5 ) ) does is encode the whole Image into Blocks of 4x4 Texel. but will internally use a 4x4 Block for a Texture with the size of 2x1 (the other Texels in the Block are undefined). The DXT Formats What the S3 Texture Compression (abbreviation: S3TC.are in the same Block. Uncompressed You probably guessed it already.Every graphics accelerator compatible with DirectX 7 or higher supports this Texture Compression. the specification allows any non-power-of-two dimension. etc). and are partially not supported by hardware and export/import tools. A bilinear Texture lookup usually reads 2x2 Texels from the Texture and interpolates those 4 Texels to get the final Color. at least a Triangle. but they include pre-multiplied Alpha which is problematic when blending with images with explicit Alpha (RGBA. like 640x480 or a power of 2. 16 Bytes per Block. If you do the maths now you will notice that the compressed image actually needs 16 Bytes for 1 Block of RGBA Color. Thus the ideal compressed Texture dimension is a multiple of and . 8 Bytes per Block. This quality loss involved. instead of storing every single Texel of the Image. When using uncompressed Textures.jpg . Accuracy: R5G6B5 or R5G5B5A1 DXT3. actually it would be slower if multiple Blocks must be read to do the lookup. DXT3/5. Accuracy: R5G6B5A8 The formats DXT2 and DXT4 do exist. which can be nicely fit into these Blocks. This results into an 1:6 compression for DXT1 and 1:4 compression for DXT3/5. there is a good chance that all 4 Texels .

amount of memory in the chip for a dedicated Texture Cache. If a Cache hit is made, the cost for reading the Texels is very low, compared to reading from Video Memory. That's why S3TC does decrease render times: the earlier mentioned 16 Bytes of a DXTn Block contain 16 Texels (1 Byte per Texel), while 16 Bytes of uncompressed Texture only contain 4 Texels (4 Bytes per Texel). Alot more data is stored in the 16 Bytes of DXTn, and alot of lookups will be able to use the fast Texture Cache. The game Quake 3 Arena's Framerate increases by ~20% when using compressed Textures, compared to using uncompressed Textures. Restrictions Although you might be convinced now that Texture Compression is something worth looking into, do handle it with care. After all, it's a lossy compression Technique which introduces compression Artifacts into the Texture. For Textures that are close to the Viewer this will be noticed, that's why 2D Elements which are drawn very close to the near Plane - like the Mouse Cursor, Fonts or User Interface Elements like the Health display - are usually done with uncompressed Textures, which do not suffer from Artifacts. As a rule of thumb, do not use Texture Compression where 1 Texel in the Texture will map to 1 Pixel on the Screen. Using OpenTK.Utilities .dds loader At the time of writing, the .dds loader included with OpenTK can handle compressed 2D Textures and compressed Cube Maps. Keep in mind that the loader expects a valid OpenGL Context to be present. It will only read the file from disk and upload all MipMap levels to OpenGL. It will NOT set minification/magnification filter or wrapping mode, because it cannot guess how you intent to use it. void LoadFromDisk( string filename, bool flip, out int texturehandle, out TextureTarget dimension) Input Parameter: filename A string used to locate the DDS file on the harddisk, note that escape-sequences like "\n" are NOT stripped from the string. Input Parameter: flip The DDS format is designed to be used with DirectX, and that defines GL.TexCoord2(0.0, 0.0) at top-left, while OpenGL uses bottom-left. If you wish to use the default OpenGL Texture Matrix, the Image must be flipped before loading it as Texture into OpenGL. Output Parameter: texturehandle If there occured any error while loading, the loader will return "0" in this parameter. If >0 it's a valid Texture that can be used with GL.BindTexture. Output Parameter: dimension This parameter is used to identify what was loaded, currently it can return "Invalid", "Texture2D" or "TextureCube". Example Usage

TextureTarget ImageTextureTarget; int ImageTextureHandle; ImageDDS.LoadFromDisk( @"", true, out ImageTextureHandle, out ImageTextureTarget ); if ( ImageTextureHandle == 0 || ImageTextureTarget == TextureTarget.Invalid ) // loading failed // load succeeded, Texture can be used. GL.BindTexture( ImageTextureTarget, ImageTextureHandle ); GL.TexParameter( ImageTextureTarget, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Linear ); int[] MipMapCount = new int[1]; GL.GetTexParameter( ImageTextureTarget, GetTextureParameter.TextureMaxLevel, out MipMapCount[0] ); if ( MipMapCount == 0 ) // if no MipMaps are present, use linear Filter GL.TexParameter( ImageTextureTarget, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.Linear ); else // MipMaps are present, use trilinear Filter GL.TexParameter( ImageTextureTarget, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.LinearMipmapLinear );

Remember that you must first GL.Enable the states Texture2D or TextureCube, before using the Texture in drawing. Useful links: ATi Compressonator: nVidia's Photoshop Plugin: nVidia's GPU-accelerated Texture Tools: Detailed comparison of uncompressed vs. compressed Images: OpenGL Extension Specification: Microsoft's .dds file format specification (was used to build the OpenTK .dds loader) DXT Compression using CUDA Real-Time YCoCg-DXT Compression

Last Update of the Links: January 2008

Frame Buffer Objects (FBO)
Every OpenGL application has at least one framebuffer. You can think about it as a digital copy of what you see on your screen. But this also implies a restriction, you can only see 1 framebuffer at a time on-screen, but it might be desireable to have multiple off-screen framebuffers at your disposal. That's where Frame Buffer Object (FBO) comes into play. Typical usage for FBO is High Dynamic Range Rendering, Shadow Mapping and other Render-To-Texture effects. Assuming the buzzwords tell you nothing, here's a quick example scenario. We have a Texture2D of a sign that has some wooden texture and reads "Blacksmith". However you intend to localize that sign, so the german version of your game reads "Schmiede" or the spanish version "herrería". What are the options? Manually create a new Texture for every sign in the game with a paint program? No. All you need is the wooden texture of the sign, without any letters. The texture can be used as target for Render-To-Texture, and OpenTK.Fonts provides you a way to write any text you like ontop of that texture. The traditional approach to achieve that was rendering into the visible framebuffer, read the information back with GL.ReadPixels() or GL.CopyTexSubImage(), then clear the screen and proceed with rendering as usual. With FBO the copy can be avoided, since it allows to render directly into a texture. Framebuffer Layout A framebuffer consists of at least one of these buffers:

A depth buffer, with or without stencil mask. Typical depth buffer formats are 16, 24, 32 Bit integer or 32 Bit floating point. Stencil buffers can only be 8 Bits in size, a good mixed depth and stencil format is depth 24 Bit with stencil 8 Bit. Color buffer(s) have 1-4 components, namely Red, Green, Blue and Alpha. Typical color buffer formats are RGBA8 (8 Bit per component, total 32 Bit) or RGBA16f (16 Bit floating point per component, total 64 Bit). This list is far from complete, there exist dozens of formats with different amount of components and precision per component.

Please note that there is no requirement to use both. It's perfectly valid to create a FBO which has only a color attachment but no depth attachment. Or the other way around. When you use more than one buffer, some restrictions apply: All attachments to the FBO must have the same width and height. All color buffers must use the same format. For example, you cannot attach a RGBA8 and a RGBA16f Texture to the same FBO, even if they have the same width and height. OpenGL 3.0 does relax this restriction, by allowing attachments of different sizes to be attached. But only the smallest area covered by all attachments can be written to. The Extension

Using a Texture2D. The wooden texture is drawn. Let's take the wooden "Blacksmith" sign example from earlier again. they can be used for color formats aswell. and a new type: the renderbuffer. however this is reported to be very slow so far. Cannot be bound as sampler for shaders. The texture must be attached and the FBO bound. The copy from renderbuffer into a texture will perform worse than rendering directly to the texture. just like every other texture. Using a renderbuffer.EXTX_mixed_framebuffer_formats allows attaching different formats to the framebuffer. Con:    Does not allow MipMaps. Allows multisampling through Extensions. The final Texture is copied into a Texture2D. Con:  Might be slower than a renderbuffer. which can be bound when drawing the geometry of the sign. filter and wrapping modes. The wooden texture is drawn into the framebuffer. The required end result must be a Texture2D. Only Text is drawn. To give an overview about the options. Text is drawn. The screen must be cleared when done. Rectangle. 3D or Cube map. As a rule of thumb. here are some brief summaries how to accomplish obtaining the desired Texture2D: 1. They are not restricted to depth or stencil like the name might suggest. . Can be bound as sampler to a shader. Renderbuffers FBO allows 2 different types of targets to be attached to it. 2D. The already known textures 1D. Using a visible framebuffer. filter or wrapping mode to be specified. or the screen must be cleared. Text is drawn. depending on hardware. Renderbuffer Pro:   May support formats which are not available as texture. Restricted to be a 2-dimensional image. The renderbuffer must be attached and the FBO bound. do not use a renderbuffer if you plan to use the FBO attachments as textures at some later stage. Either the renderbuffer is redundant now. Done. The final Texture is copied into a Texture2D. Texture2D Pro:   Allows MipMaps. 2. 3.

DepthComponent32.Texture2D. (RenderbufferStorage)All.Ext.FramebufferExt.g.BindFramebuffer( FramebufferTarget. FboHeight.Ext. const int FboWidth = 512. will disable the last bound FBO and return rendering back to the visible window-system provided framebuffer. ColorTexture.Clamp ). so I'm not going through the purpose of handles. I'm assuming you read the VBO tutorial before this. RenderbufferTarget.UnsignedByte. (int) TextureMinFilter.Nearest ). PixelFormat.BindTexture( TextureTarget. GL. TextureParameterName. out FboHandle ).Rgba8.TextureWrapT. 0.Delete* functions again.RenderbufferExt.BindTexture( TextureTarget. // prevent feedback.Ext. GL. E.BindFramebuffer( FramebufferTarget.FramebufferExt. TextureParameterName. FboWidth.Texture2D. 0 ).Zero ). GL. GL.RenderbufferExt. const int FboHeight = 512. FramebufferAttachment.Texture2D. GL. PixelType.TexImage2D( TextureTarget. . GL.RenderbufferExt.Texture2D.TexParameter( TextureTarget. // test for GL Error here (might be unsupported format) GL. PixelInternalFormat. IntPtr.Texture2D. GL.FramebufferExt. // Create Color Texture GL.GenTextures( 1.Ext.Texture2D. FboWidth. 0 ).Ext.ColorAttachment0Ext. GL.TextureWrapS. ColorTexture ).Nearest ). uint DepthRenderbuffer. out ColorTexture ).TextureMagFilter. (int) TextureWrapMode. DepthRenderbuffer ). reading and writing to the same image is a bad idea // Create Depth Renderbuffer GL.TexParameter( TextureTarget. GL. uint ColorTexture.Ext.Ext.DepthAttachmentExt.TexParameter( TextureTarget.Clamp ). TextureParameterName.Texture2D. then attach the texture and renderbuffer to the FBO. TextureTarget. 0.Example Setup To give a concrete example how all this theory looks in practice: let's create a color texture. FboHandle ).Rgba. FramebufferAttachment.Ext. 0 ).TextureMinFilter. Note that this is a C API and the same rule of binding 0 to disable or detach something is valid here too. a depth renderbuffer and a FBO.BindRenderbuffer( RenderbufferTarget.Bind* and GL. out DepthRenderbuffer ).FramebufferRenderbuffer( FramebufferTarget. // test for GL Error here (might be unsupported format) // Create a FBO and attach the textures GL.Gen*. GL. GL.GenRenderbuffers( 1. GL. GL.RenderbufferStorage(RenderbufferTarget.GenFramebuffers( 1. GL.FramebufferExt. uint FboHandle. FboHeight). (int) TextureMagFilter.Texture2D. TextureParameterName. (int) TextureWrapMode. DepthRenderbuffer ).FramebufferTexture2D( FramebufferTarget.TexParameter( TextureTarget.

PopAttrib( ).by calling GL.Ext. At this point you may bind the ColorTexture as source for drawing into the visible framebuffer. the best course of action is to detach the DepthRenderbuffer too and delete the renderbuffer and the FBO. In the example setup above. // restores GL. 0 ). but you may ofcourse specify it manually using GL.Viewport() parameters using the GL.ViewportBit ).the texture contents itself is not affected .FramebufferExt.DrawBuffer( (DrawBufferMode)FramebufferAttachment.but only draw to one of them . FboHeight ).Viewport() and GL. Selecting multiple color buffers to write to is done with the GL. which expects an array like this: . GL.Ext.Back ).// now GL. However the FBO would then be incomplete due to the missing color attachment. 0. That is only a problem if the FBO is bound again and the texture is used at the same time for being a FBO attachment target and the source of a texturing operation.DrawBuffer(s). GL. Special care has to be taken about 2 states that are always affected by FBOs: GL.DrawBuffer(s) A FBO supports multiple color buffer attachments.PopAttrib().just keep it attached to the FBO and make sure no feedback situation arises.Viewport() parameters GL.PushAttrib() and GL. for example simply 0.Viewport(). This will cause feedback effects and is most likely not what you intended. check the end of this page for a snippet.CheckFramebufferStatus( FramebufferTarget.BindFramebuffer( FramebufferTarget.DrawBuffer() command.DrawBuffer( DrawBufferMode. It is valid to attach the same texture or renderbuffer to multiple FBO at the same time. the Viewport was stored and restored using GL. Do not repeatedly attach and detach the same Texture if you want to update it every frame . Switching framebuffer targets is such an expensive operation that the cost of the 2 extra calls to set up drawbuffers and viewport can be ignored.Viewport( 0. // stores GL. when done .ColorAttachment0Ext ). if they have the same dimension and the same format. instead of creating multiple depth buffers and copy between them. // since there's only 1 Color buffer attached this is not explicitly required GL. You may detach the ColorTexture from the FBO . FboWidth. but be aware that it is still attached as target to the created FBO.PushAttrib( AttribMask.FramebufferTexture2D() and attach a different target than ColorTexture to the ColorAttachment0 slot.FramebufferExt ) can be called. // render whatever your heart desires. When switching from the visible framebuffer to a FBO. // return to visible framebuffer GL.Ext. It is allowed to attach multiple color buffers . Example: you can avoid copies and save memory by attaching the same depth buffer to the FBOs.DrawBuffers() command. you should always set a proper viewport and drawbuffer.. GL..

CopyTex*() calls. // fugly. Remarks For the sake of simplicity. bufs ). but the more likely case is that you requested a doublebuffered context.. the window-system provided framebuffer was called "visible framebuffer".ReadBuffer().0: EXT_framebuffer_multisample allows the creation of renderbuffers with n samples per image. ) and the number of allowed Drawbuffers at the same time through GL. EXT_framebuffer_blit allows to bind 2 FBO at the same time. For a more detailed description you'll have to dig through the official specification.SwapBuffers(). When using double buffers. to avoid that slow computers show unfinished images on screen. (DrawBuffersEnum)FramebufferAttachment. In practice this makes only sense if you're writing shaders with GLSL.GetInteger( GetPName.9. such as that you may not want to print with standard fonts on the sign. In reality this is only true if you requested a single-buffer context from OpenGL. use GL. the 'back' buffer is the one used for drawing and never visible on screen.2 GL.DrawBuffersEnum[] bufs = new DrawBuffersEnum[2] { (DrawBuffersEnum)FramebufferAttachment.. (Look up "gl_FragData" for further info) The exact number how many attachments are supported by the hardware must be queried through GL. One for reading and one for writing. the 'front' buffer is the one that is visible on screen. . because they are off-screen at all times..Length.. Without this Extension the active framebuffer is used for both: reading and writing.MaxDrawBuffers.CheckFramebufferStatus .DrawBuffers( bufs. The wooden "Blacksmith" sign example has some hidden complexities that are ignored for the sake of simplicity.MaxColorAttachmentsExt.ColorAttachment1Ext }. will be addressed in 0.ReadPixels() or GL.GetInteger( GetPName. These Extensions were merged into ARB_framebuffer_objects with OpenGL 3. . ) To select which buffer is affected by GL. This page does not cover all commands exposed by FBO. The two buffers are swapped with each other when you call this. FBO are not designed to be double buffered.ColorAttachment0Ext. This code declares the color attachments 0 and 1 as buffers that can be written to. Snippet how to interpret the possible results from GL. that words in different languages can have different length or that it might be desireable to add an additional mask when writing the text to simulate the paint peeling off the sign.

} .CheckFramebufferStatus( FramebufferTarget. } /* case FramebufferErrorCode." ).private bool CheckFboStatus( ) { switch ( GL.WriteLine("FBO: An object has been attached to more than one attachment point. } case FramebufferErrorCode. Other causes of this error are that the width or height is zero or the z-offset is out of range in case of render to volume." ). }*/ case FramebufferErrorCode. } case FramebufferErrorCode. } case FramebufferErrorCode.WriteLine( "FBO: The color attachments have different format. break." ).WriteLine( "FBO: There are no attachments.WriteLine( "FBO: Attachments are of different size.FramebufferIncompleteDrawBufferExt: { Trace.DrawBuffers() doesn’t have an attachment. This could mean there’s no texture attached or the format isn’t renderable. } case FramebufferErrorCode.GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_E XT: { Trace.WriteLine( "FBO: One or more attachment points are not framebuffer attachment complete. break. break.FramebufferIncompleteFormatsExt: { Trace. For color textures this means the base format must be RGB or RGBA and for depth textures it must be a DEPTH_COMPONENT format.FramebufferIncompleteMissingAttachmentExt: { Trace. All color attachments must have the same format."). break.FramebufferIncompleteDimensionsExt: { Trace.FramebufferCompleteExt: { Trace. break.FramebufferIncompleteAttachmentExt: { Trace. All attachments must have the same width and height.FramebufferExt ) ) { case FramebufferErrorCode. return true.WriteLine( "FBO: An attachment point referenced by GL." )." )." ).Ext.WriteLine( "FBO: The framebuffer is complete and valid for rendering. break.

WriteLine( "FBO: The attachment point referenced by GL. 3 float for the Normal direction and 3 float to specify the Position. for using Immediate Mode please refer to the red book. In Shader Program driven rendering it is also possible to specify custom Vertex Attributes which are previously unknown to OpenGL. This format contains 2 float for Texture Coordinates. } case FramebufferErrorCode. In the fixed-function environment a Vertex commonly includes FramebufferErrorCode. [StructLayout(LayoutKind.)" ). which is much more elegant to handle than a float[] array. Focus is on storing the Geometry directly in Vertex Buffer Objects (VBO). Vertices) specifies a number of Attributes associated with a single Point in space.FramebufferIncompleteReadBufferExt: { Trace. } Geometry These pages of the book discuss how to define. Normal. this is really bad. namely InterleavedArrayFormat." ). break.FramebufferUnsupportedExt: { Trace.ReadBuffers() doesn’t have an attachment. The only Attribute that is not optional an must be specified is the Vertex's Position.T2fN3fV3f. such as Radius or Bone Index and Weight for Skeletal Animation. Thanks to the included Math-Library in OpenTK. break.WriteLine( "FBO: Status unknown.T2fN3fV3f public Vector2 TexCoord. } default: { Trace." ). we're allowed to specify an arbitrary Vertex struct for our requirements. public Vector3 Normal. . } } return false. Color and/or Texture Coordinates. 1. usually consisting of 3 float. reference and draw geometric Objects using OpenGL. (yes.WriteLine( "FBO: This particular FBO configuration is not supported by the implementation. For the sake of simplicity we'll re-create one of the Vertex formats OpenGL already knows. The Vertex A Vertex (pl. break.Sequential)] struct Vertex { // mimic InterleavedArrayFormat.

4f ). as Vertex Array or sent into a Vertex Buffer Object.UnitX.5f.Normal = Vector3. instead of storing 100 times the same Vertex in Vertices. Vertices[ i ]. Addressing elements is as convenient as in the following example: Vertices = new Vertex[ n ]. Vertices[ i ]. We can now declare an Array of Vertices to describe multiple Points and allow easy indexing/referencing them. We can also use collections to store our Vertices.public Vector3 Position. we can reference it 100 times from the Indices Array: uint[] Indices. 2. // -1 < i < n start at Index 0 and end at Index n-1. Vertices[ i ]. // the Vectors are structs.TexCoord. An Index is simply a byte. Basically the Indices Array is used to describe the primitives and the Vertex Array is used to declare the corner points. This is usually expected when you begin drawing in either Immediate Mode (GL.DrawElements. .Y = 1f. The Vertex-Array Vertices can now be created and filled with data. -3f. Now the Vertices and Indices Arrays can be used to describe the edges of any Geometric Pritimitve Type. referencing an element in the Vertices Array.TexCoord.X = 0. Vector2 UV = Vertices[ i ]. but it's recommended you stick with a simple Array to make sure your Indices are valid at all times. ushort or uint.DrawArrays or GL.) (Remember that arrays // examples how to assign values to the Vector's components: Vertices[ i ].Begin). // create a new Vector and copy it to Position.UnitX into the Normal Vector. // this will copy Vector3. Geometric Primitive Types OpenGL requires you to specify the Geometric Primitive Type of the Vertices you wish to draw. Once the Arrays are filled with data it can be drawn in Immediate Mode. // Ofcourse this also works the other way around. } This leads to a Vertex consisting of 8 float. or 32 byte. . so the new keyword is not required.TexCoord. Vertex[] Vertices.Position = new Vector3( 2f. GL. So if we decide to draw a single Vertex 100 times at the same spot. using the Vectors as the source.

e. While OpenGL allows to draw Quads or Polygons aswell. v4. v2 and v3. if the source data isn't a LineStrip. in order to visualize the Vertices of the Object. v0. Some more possibilities are:     QuadStrip. which are internally set up to be used with Quads. you will see that v3 in a Quad is used to finish the shape. it is quite easy to run into lighting problems if the surface is not perfectly planar. Polygon can be drawn as LineLoop TriangleFan can be drawn as Polygon or LineLoop The smallest common denominator for all filled surfaces (i. Internally. if the Vertices were originally intended to be drawn as Quads. 1: In the above graphic all valid Geometric Primitive Types are shown.Fig. v5 which isn't something that belongs to any surface. This is important. no Points or Lines) is the Triangle. OpenGL breaks Quads and Polygons into Triangles. This Geometric Primitive Type has the special attribute of always being planar and is currently the best way to describe a 3D Object to GPU hardware. However Points and Lines are an Exception here. Examine Figure 1. You can draw every other Geometric Primitive Type as Points. TriangleStrip and LineStrip can be interchanged. will result only in garbage being displayed. in order to rasterize them. Quads can be drawn as Lines with the restriction that there are no lines between v1. The next drawn Triangle will be v3. because drawing a set of Vertices as Triangles. . their winding is Clockwise (irrelevant for Points and Lines). while Triangles uses v3 to start the next shape.

MultiDrawArrays. n Quads = Vertex * (4n). the QuadStrip is a more compact representation of a sequence of connected Quads. basically the Polygon will be split to Triangles in the end anyways. but the very first and last issued Vertex are automatically connected by an extra Line segment. you get 2 Vertices. n Lines = Vertex * (2n). 7. Polygon Note that the first and the last Vertex will be connected automatically. n Line Segments in the Loop = Vertex * (1n). Note: It might look like an inefficient brute force approach at first. but it has it's advantages over TriangleStrip. polygons must be planar or be displayed incorrectly. 3. Note: This primitive type should really be avoided whenever possible. it is possible to arrange Triangles in a way that makes good use of the Vertex Caches. since you are not required to supply Triangles in sequenced strips. n Points = Vertex * (1n). which leads to multiple draw calls when drawing a mesh.DrawArrays().MultiDrawElements or GL. 2. Lines Two Vertices form a Line. almost for free. otherwise the split into Triangles will become visible. thus this is usually only used with GL. Another Problem is that there is only 1 single Polygon in a begin-end block. Triangles This way to represent a mesh offers the most control over how the Triangles are sorted. LineLoop Same as LineStrip. most neighbour Triangles will share 2 Vertices (an edge). that are stored in the Vertex Cache. Care has to be taken that the surface is planar. every consecutive issued Vertex marks a joint in the Line. QuadStrip Like the Triangle-strip. 8. 9. Most of all. 5. but you are not restricted to a certain Direction and forced to insert degenerated Triangles. n Quads in Quadstrip = Vertex * (2+2n). or using the Extensions GL. since those are typically rectangular aswell. just like LineLoop. Like Quads. This is basically the same what stripification does. TriangleStrip The idea behind this way of drawing is that if you want to represent a solid and closed Object. Points Specifies 1 Point per Vertex v.1. Quads Quads are especially useful to work in 2D with bitmap Images. LineStrip The first Vertex issued begins the LineStrip. a Triangle always consists of 3 Vertex. n Triangles = Vertex * (3n). If the Triangle you currently want to draw shares an edge with one of the Triangles that have been recently drawn. Polygon with n Edges = Vertex * (1n). n Line Segments in the Strip = Vertex * (1+1n) 4. 6. You start by defining the initial Triangle (3 Vertices) and after that every new .

This can be a huge performance boost for dynamic meshes and is for years the best overall solution for storing . Triangle. Ofcourse you can glEnd(). Should TriangleStrip get an core/ARB command to start a new strip within the begin/end block (only nVidia driver has such an Extension to restart the primitive) this might change. Texture Coordinates and Normals . When looking at the graphic. A workaround to avoid exiting the begin/end block is to create 2 or more degenerate Triangles (you can imagine them as Lines) at the end of a strip and then start the next one.directly in the Video-card's Memory. but this also comes at the cost of processing Triangles that will inevitably be culled and aren't visible. followed by border Vertices. like the caps of a cylinder. rather than storing it in System Memory and pass it to the graphics Hardware every time we wish to draw it. Colors. Ofcourse you can experiment with the GL.a Vertex Buffer Objects Introduction The advantage of VBO (Vertex Buffer Objects) is that we can tell OpenGL to store information used for drawing .Triangle will only require a single new Vertex for a new Triangle.and Quad-strips might look quite appealing due to their low memory usage. 3. if necessary. which will be reused for all Triangles in the Fan. They are beneficial for certain tasks.both. Especially when optimizing an Object to be in a Vertex Cache friendly layout. and start a new strip.Meshes. static and dynamic .Triangles into your programs though. n Triangles in Strip = Vertex * (2+1n). 3 ushort per Triangle isn't much memory. but that costs API calls. for example Quads are very commonly used in orthographic drawing of UI Elements such as Buttons. It is very useful to represent convex ngons consisting of more than 4 vertices and disc shapes. the big disadvantage of TriangleStrip is that there is no command to tell OpenGL that you wish to start a new strip while inside the glBegin/glEnd block. It's just not realistic that you can have all your 3D Objects in Quads and OpenGL will split them internally into Triangles anyway. Don't hardwire BeginMode. . 10.MultiDraw Extension mentioned above. While this has been already doable with Display Lists before. Note: While this primitive type is very useful for storing huge meshes (2+1n Vertices per strip as opposed to 3n for BeginMode.Triangles). Text or Sprites. but currently the smaller data structure of the strip does not make up for the performance gains a Triangle List gets from Vertex Cache optimization. but Triangles are the best primitive type to represent an arbitrary mesh. the number of Triangles can be much higher. it is essential to start new strips in order to reuse Vertices from previous draws. VBO has the advantage that we're able to retrieve a Pointer to the data in Video Memory and read/write directly to it. TriangleFan A fan is defined by a center Position. because it's not restricting locality and allows further optimizations. and still allows to index 64k unique Vertex in a mesh. but using it will break using other Extensions such as DirectX 10 instancing.

must be enabled.VertexArray). (named VBO) and the later is pointing at those vertices to define geometry (named IBO).Creation Handling VBOs is very similar to handling Texture objects. GL. just like classic Vertex Arrays. we can generate&delete handles.ElementArrayBuffer. 0 ). This has the advantage that. GL.BindBuffer( BufferTarget.NormalArray). for example you could store only the vertices in the VBO and keep the indices in system memory. one VBO containing all Vertex information (Texture. Delete The OpenGL driver should clean up all our mess when it deletes the render context.BindBuffer( BufferTarget. it should be checked with GL. Although it is unlikely. uv. Also.ElementArrayBuffer to implement LOD on the same set of vertices. GL. GL. Theres two important things to keep in mind though: 1) While working with VBOs. Binding To select which object you currently want to work with.DrawElements call. OpenGL could complain that it ran out of memory or that the extension is not supported.GetError. For this tutorial we will need 2 objects. First we acquire two Objects to use: uint[] VBOid = new uint[ 2 ].DeleteBuffers( 2.BindBuffer( BufferTarget. bind them or fill them with data.ArrayBuffer. for example you could build different triangle lists for BufferTarget. If everything went smooth we have 2 objects to work with available now. VBOid[ 1 ] ). 2) All Vertex Array related commands will be used on the currently bound objects until you explicitly bind zero '0' to disable hardware VBO. It is not required to bind a buffer to both targets. if using Normals.ElementArrayBuffer. The first is used to store position.EnableClientState(EnableCap. when we have uploaded the data to the VBO/IBO later on. 0 ). we can draw the whole mesh with a single GL. normals. GL. ref VBOid ). it's always a good idea to clean up on your own where you can. GL. etc. simply by binding the desired element array. We remove the objects we reserved at the buffer creation by calling: GL.ArrayBuffer.BindBuffer( BufferTarget. .GenBuffers( 2. out VBOid ). VBOid[ 0 ] ).ElementArrayBuffer.EnableClientState(EnableCap. GL.ArrayBuffer or BufferTarget. simply bind the handle to either BufferTarget. Normal and Position in this example case) and an IBO (Index Buffer Object) referencing Vertices from the VBO to form Triangles. the two objects are not tied together in any way.

GL.MapBuffer (more about this later). We make sure the correct object is bound (it is not required to do this. explaining the options in the enum BufferUsageHint in more detail. it will place your data in the best suited place for your purposes. The function GL. Just here to clarify on which object we currently work on) GL. however the mesh could index way more Vertices using a type like uint. GL. saving memory compared to a 4 Bytes UInt32 per index. BufferUsageHint. it would not make a difference if we set up the VBO first. GL. we make sure the binding is correct. Indices.BindBuffer( BufferTarget.BufferData We will start by preparing the IBO. (IntPtr) ( Indices. Vertices.BufferSubData which is quite straightforward to use once you are familiar with GL.ArrayBuffer. the second is the amount of memory (in bytes) we need allocated to hold all our data. we will focus on using GL.ElementArrayBuffer. In the example application ushort has been used for Indices. 1.Passing Data There are several ways to fill the object's data. OpenGL now has a copy of Indices available and we could dispose the array. VBOid[ 1 ] ).UnmapBuffer .StaticDraw ).65535] are more available Vertices than used by most real-time rendered meshes. OpenGL will store this data as 2 Bytes per index. we simply start with the shorter one.StaticDraw ). The last parameter is an optimization hint for the driver. BufferUsageHint.ArrayBuffer. assuming we have the Index Count of the array stored in a variable for the draw call later on. The third parameter is pointing at the data we wish to send to the graphics card.ElementArrayBuffer. if the buffer is already bound. Using ushort. VBOid[ 0 ] ). give a pointer to the Vertex count.MapBuffer / GL. 2.BindBuffer( BufferTarget. because 16 Bits [0. Again.BufferData( BufferTarget. GL. the Vertices are next.Length * 8 * sizeof( float ) ).Length * sizeof( ushort ) ).BufferData( BufferTarget.Zero and you may send the data at a later stage with GL.BufferData and directly writing to video memory. That's all. this can be IntPtr. The third option would be GL. (IntPtr) ( Vertices.. and finally the usage hint.BufferData's first parameter is the target we want to use.BufferData. There's a table at the bottom of this page. Now that we've stored the indices in an IBO. GL.

StaticDraw properly according to what you intend to do with the Data. unsafe { fixed ( ushort* SystemMemory = &Indices[0] ) { ushort* VideoMemory = (ushort*) VideoMemoryIntPtr. there's a table at the bottom of this page. which help the driver understand what you're going to do with the data. this alternative will give us a pointer to the video memory reserved by the object. Note that the data's object is locked until we unmap it. for ( int i = 0. IntPtr. fully procedural objects. This is useful for dynamic models that have no copy in client memory that could be used by GL. (IntPtr) ( Indices. i < Indices.BindBuffer( BufferTarget.ElementArrayBuffer. The pointer is now invalid and may not be stored for future use.Zero.g.ReadOnly. Further reading Visit this link in order to tell OpenGL about the composition of your Vertex data. BufferAccess. VBOid[ 0 ] ). Valid access flags for the pointer are BufferAccess. IntPtr VideoMemoryIntPtr = GL. and this link for drawing the data. once we're done we must release the lock. so we want to keep the timespan over which we use the pointer as short as possible.BufferData. the pointer towards the Indices is actually IntPtr. BufferUsageHint. Optimization: .BufferData( BufferTarget.ReadWrite. BufferAccess.ElementArrayBuffer.StaticDraw ). First we make sure that we got the desired object bound and reserve memory.ElementArrayBuffer. i++ ) VideoMemory[ i ] = SystemMemory[ i ]. GL.Zero. Note that you should change BufferUsageHint.Length * sizeof( ushort ) ).MapBuffer again.ToPointer(). // simulate what GL.While the first described technique to pass data into the objects required a copy of the data in system memory.WriteOnly or BufferAccess. GL.ElementArrayBuffer ).MapBuffer(BufferTarget. if we wish to modify the object again.UnmapBuffer( BufferTarget. because we only need an empty buffer. we have to call GL. We may now write some data into the buffer. since you wish to rebuild it every single frame (e. Now we're able to request a pointer to the video memory. particle system).BufferData would do } } GL.Length.WriteOnly).

the first parameter tells that we have 2 floats for Texture Coordinates (T2f). Also note that either reading from a VBO or wrapping it into a Display List is very slow and should both be avoided. Table 1: BufferUsageHint..Static.Draw Means the buffer will be used to sending data to GPU.T2fN3fV3f. This command has the advantage that it's very obvious to the OpenGL driver what layout of data we have supplied. Assumed to be a 1-to-n update-to-draw. while the old data will be cleaned up once it's not used in any draw operations anymore. Although this is a bad idea.. The last parameter should point at Indices.Read Means the data must be easy to access.... if we want to update all data in the buffer object by using GL. and it may be possible for the driver to . BufferUsageHint. Assumed to be a n-to-n update-to-draw. but we already sent them to the VBO in video memory.MapBuffer and not retrieve any of the old data. Assumed to be a 1-to-1 update-to-draw. 3. will most likely be system or AGP memory. no stride (zero) is correct. Means the data is drawn multiple times before it changes.MapBuffer will return a new pointer to a valid memory location of the requested size to write to.. null ). because mapping the buffer is a more expensive operation than just calling GL.. The solution to making this somewhat efficient is first calling GL.BufferData with a IntPtr.Dynamic.Read and . which tells the driver that the old data isn't valid anymore.One hint from the nVidia whitepaper was regarding the situation.InterleavedArrays() What GL.. third.. no need to point at them again: GL..Zero again. but build it on the fly.Draw operations.b Attribute Offsets and Strides Setting Strides and Offsets for Vertex Arrays and VBO There are 2 ways to tell OpenGL in which layout the Vertices are stored: 1. it might be necessary in cases where you have no copy of the data in system memory. . etc. Calling GL.Stream.InterleavedArrays does is enable/disable the required client states for OpenGL to interpret our passed data.. 0.Copy Means we are about to do some . The second parameter is the stride that will be jumped to find the second. video memory (Static|StreamDraw) or AGP (DynamicDraw) ...InterleavedArrays( InterleavedArrayFormat. . set of texcoord/normal/position values.. BufferUsageHint. Means the data is specified once (during initialization). 3 floats for Normal (N3F) and 3 floats for position (V3F). Since our Vertices are tighly packed.BufferData. GL. Means the data is very volatile and will change every frame.

VertexArray.. The third parameter is the number of bytes of the Vertex struct. 2.. 4.. EnableCap. 8 * sizeof( float ). this makes perfect sense if you recall the layout of our Vertex struct.NormalPointer( NormalPointerType.DrawElements) make sure to enable them again or you won't see anything. GL. but is variable for Texture coordinates and Position (and Color). For GL. 3. Remember that GL. GL.TexCoordPointer (after calling GL.DrawArrays(. // Force OpenGL to finsh drawing while the arrays are still pinned. Vertex Arrays can be safely used like this: pseudocode: float[] Positions..TextureCoordArray or changing GL.Float.VertexPointer( 3.NormalArray.. Byte 8-19 for the Normal and Byte 20-31 for the Vertex Position.).InterleavedArrays will change states. TexCoordPointerType. 3. (IntPtr) ( 0 ) ).c Vertex Arrays Although it's really not recommended using them (besides for compiling to a Display List). 8 * sizeof( float ). GL.NormalPointer or GL. 2. 4.TexCoordPointer( 2.optimize the memory. } } . GL. unsafe { fixed (float* PositionsPointer = Positions) fixed (float* NormalsPointer = Normals) { GL.Float. The second parameter is the type of the components. The first parameter is the number of components to describe that attribute. Setting the offsets/strides manually For the Vertex format InterleavedArrayFormat.Float. The last parameter indicates the byte offset of the first appearance of the attribute. (IntPtr) ( 5 * sizeof( float ) ) ).. if you manually disable EnableCap. This stride is used to define at which offset the next Vertex begins..NormalPointer(. NormalsPointer). PositionsPointer). 8 * sizeof( float ). Byte 0-7 are used for the Texture Coordinates.VertexPointer.InterleavedArrays and before calling GL. GL. float[] Normals. EnableCap. GL. (IntPtr) ( 2 * sizeof( float ) ) ).Finish()..NormalPointer this is always 3 components. GL. the correct pointer setup is: 3.T2fN3fV3f.VertexPointer(. VertexPointerType. 1.

That's why it's recommended to use Display Lists or Vertex Buffer Objects instead.VertexAttribPointer() was called. Vertex Array Objects Vertex Array Objects (abbreviation: VAO) are storing vertex attribute setup and VBO related state.*Pointer and GL.Draw* may not be released.BindBuffer() at the point of time when GL. Binding a VAO is as simple as void GL. Drawing . which forces CPU and GPU to work in sync and not taking advantage of parallel processing.DisableVertexAttribArray() GL.VertexAttribPointer() GL. This allows to reduce the number of OpenGL calls when drawing from VBOs. 4. For arrays smaller than 85000 Bytes it is also important to call GL. but once the pin is released the Garbage Collector is allowed to move or cleanup your array.EnableVertexAttribArray() GL. A more technical description can be found at the OpenGL Wiki.You must use the unsafe float* overloads for this to work.Finish will obviously kill performance. 5. This leads to difficult to trace access violations with medium sized VA (without waiting for OpenGL to finish processing it) and will randomly occur at frames where the GC kicks in. since you have a blocking statement executed.Finish in order for the CPU to wait until the GPU is done reading all pinned data. a handle must be generated: void GL.GenVertexArrays( uint[] ).Finish command for a very short VA or ones larger than 85000 Bytes. Please visit this discussion in the forum for more information. void GL. because all attribute declaration and pointer setup only needs to be done once (ideally at initialization time) and is afterwards stored in the VAO for later re-use.IsVertexArray( uint ). You might get away without the GL. GL. But before a VAO can be bound. not the object overloads (which pin internally) The important bit is that the pin between GL. bool GL.BindVertexArray( uint ).VertexAttribIPointer() Indirectly it also saves the state set by GL. The currently bound VAO records state set by the following commands: GL.DeleteVertexArrays( uint[] ).

Normal3( Vertices[ i ].DrawArrays( BeginMode.DrawElements( BeginMode. GL. DrawElementsType. ushort. 8. Count is the number of Elements to render. 21. It uses an unsigned Array (byte. GL.End( ). Indices ). 19. Vertices. } GL. i++ ) 5.TexCoord ).End( ). GL. 1. drawing a whole Mesh with a single Command.Length ).UnsignedInt.Length. with the change that you may specify a starting index rather than starting at 0.DrawElements . GL. i++ ) 15.DrawRangeElements( BeginMode.Vertex3( Vertices[ i ].TriangleStrip. for ( uint i = 0.Normal3( Vertices[ Indices[ i ] ]. In order for all GL. GL. GL. object ) This function behaves largely like GL.VertexArray must be enabled first. 0.Length.Normal ). 4. This is particularly useful for 3D Models where the Triangles describing the surface share Edges and Vertices. This line will automatically draw all Vertex contained in Vertices in order of appearance in the Array. 22.Begin( BeginMode.TexCoord ). int Length.DrawElements behaviour 13. GL. 18. { 16. // GL. { 6. 17. 7.In order to tell OpenGL to draw primitives for us.Position ). int End.DrawArrays( BeginMode. i < Vertices. 9. int Start. object ) Like DrawArrays this Command is used together with Vertex Arrays or VBO.Vertex3( Vertices[ Indices[ i ] ].Points ). int First. there's basically two ways to go: 1.Points ). 12. Vertex Buffers (or Vertex Arrays).Points. Immediate Mode. GL. 11. EnableCap. // behaviour equal to GL. 23.Draw*-Functions to output geometric Primitives. 2.TexCoord2( Vertices[ i ]. int Count. GL. GL.Normal ).TexCoord2( Vertices[ Indices[ i ] ]. Start and End are the smallest and largest Array-Index into Indices. DrawElementsType. uint) to Index the Vertex Array.Position ). GL.DrawElements( BeginMode.Begin( BeginMode.DrawElements.DrawArrays behaviour 3. Indices. DrawElementsType. as in specifying every single Vertex manually. Immediate Mode Hereby every single Vertex we wish to draw has to be issued manually. GL. 2. see setting Attribute Pointers. 14. i < Indices. 20.Length. GL. for ( uint i = 0. GL. int Length ) This Command is used together with Vertex Arrays or Vertex Buffer Objects. // GL. } 10.

gl_InstanceID < primcount. Indices.b Drawing Optimizations This page is just giving a starting point for . 26.txt http://www. gl_InstanceID is a uniform variable available to the Vertex Shader.GL. Extension References http://www.opengl. First. Length.DrawElementsInstanced( BeginMode. int Any error usually kills the framerate. Enable backface culling with GL.TriangleStrip. which (in conjunction with GL.DrawRangeElements instead of GL.Length-1. the links below provide more in-depth information. int primcount ) This function is only available on DX10 hardware and basically behaves like this: 25. 24. DrawElementsType. gl_InstanceID < primcount. Length ).CullFace) rather than relying only on the Z-Buffer.txt 5.DrawElements( BeginMode. Object ). int Length. int Length. GL. Organize drawing in a way that requires as few state changes as possible. for (int gl_InstanceID=0.DrawElements for a slight performance gain.DrawArraysInstanced( BeginMode.txt http://www.UniformMatrix) can be used to assign each Instance drawn it's own unique orientation Matrix. Use GL. object. gl_InstanceID++ ) GL.. DrawElementsType. If you're drawing a scene that covers the whole screen each frame. BeginMode.Enable(EnableCap. int primcount ) This function is only available on DX10 hardware and internally unrolls into: 27. Use GL. GL.Length.UniformMatrix) can be used to assign each Instance drawn it's own unique Orientation Matrix. which (in conjunction with GL. gl_InstanceID++ ) GL. only clear depth buffer but not the color buffer.. DrawElementsType. Indices.) wherever applicable.        Make sure there are no OpenGL errors. Indices ). Avoid Immediate Mode or Vertex Arrays in favor of Display Lists or Vertex Buffer Objects. Don't enable states that are not needed.opengl.DrawRangeElements( BeginMode. for (int gl_InstanceID=0. gl_InstanceID is a uniform variable available to the Vertex Shader.

com/developer/SDK/AMD_SDK_Samples_May2007/Documentations/.com/object/gpu_programming_guide.html Links: OpenGL rendering pipeline . Take advantage of S3TC Texture compression and Vertex Cache optimizing Last edit of Links: March 2008 6.htm http://ati.opentk.. http://developer. OpenTK's procedural objects Placeholder http://www.mesa3d.

Rendering works by projecting 3-dimensional objects to a 2-dimensional plane. so .

Each fragment receives interpolated vertex shader data from the primitive it belongs to. . which are then divided by the vertex' w-component (perspective divide) and clipped against the [-1. The Vertex Shader is responsible for transforming each Vertex from Object Coordinates into Clip Coordinates. which is at least position and depth. As a final step. However even the hardwired logic can be manipulated by setting OpenGL's state machine's toggles.0. it would belong between the Triangle.. However the most in-depth description of the functionality can only be found in the official OpenGL specification. This output is called a "fragment".0] range of normalized device-coordinate space (NDC). The resulting transformed geometric primitive types can now be rasterized into fragments. Geometry and Fragment Shader) while the rest is hardwired.BindFragDataLocation(). the fragment muss pass a series of tests called the Fragment Operations. Fragment Operations A Fragment is a candidate to become a pixel in the framebuffer. Most of these tests can be toggled through GL. This page will cover the pipeline operations involved between input and output. After rasterization of the primitive. the viewport application will offset&scale the normalized devicecoordinates to window coordinates. OpenGL applies a series of tests in order to eliminate the fragment early to avoid updating the framebuffer. the resulting Z is used to discard fragments even before the fragment shader is executed.they can be displayed on a screen. The primitive assembly will use the resulting Clip Coordinates to create geometric primitives.+1. gl_FragData[] or set by GL. which are shown in the diagram and described in detail in the OpenGL specification. but contains a few commands which belong to the ARB_compatibility extension and may be unavailable. Line or Point rasterization. which is done by using VBO (and optionally VAO). For every fragment. the pages below cover this in more detail. There is one noteworthy special case found in some modern hardware. and the fragment shader. In order to begin drawing. Also note: This diagram targets the OpenGL 3.Enable/Disable.2 pipeline. Before this can happen. In the diagram to the left. A Vertex and Fragment Shader are required and OpenGL must know about the 3D object. Some of the steps involved are fully programmable (namely: Vertex. The functionality is called "Early-Z" or "HyperZ". The fragment shader's output must be either gl_FragColor. In modern OpenGL the 3D objects are read from Vertex Buffer Objects (VBO) and the resulting image is written to a framebuffer. which is a candidate to become a pixel in the framebuffer. This functionality is not exposed to OpenGL and works behind the scenes.

I. height ). since the pixels of framebuffer objects are owned by the GL. not the window system.Scissor( x. 02. .ScissorTest GL. 01.GetInteger( GetPName.ScissorTest ).. there would be no point in determining whether Depth Test passes or not. If the draw framebuffer is the default framebuffer. State Queries To determine whether ScissorTest is enabled or disabled. Height is used to specify the vertical extension of the rectangle. later tests are ignored. // default Only a single command is related to the ScissorTest.ScissorTest ). This test allows the window system to control the GL’s behavior.1 spec" wrote: This test is used to determine if the pixel at the current location in the framebuffer is currently owned by the GL context. Cannot explain it better.The tests are executed from top to bottom. the window system controls pixel ownership. width. when a GL window is obscured. Possible results are that the fragment is discarded or that some subset of the subsequent per-fragment operations are applied to the fragment.. The values set by GL. if a fragment did not pass an early test. for instance. If the draw framebuffer is a framebuffer object. If it is not.ScissorBox. y. If the fragment does not pass the Scissor Test. "GL 3. GL.Scissor() can be queried by GL.    X and Y are used to specify the lower-left corner of the rectangle. ).Enable( EnableCap. the window system decides the fate the incoming fragment. By default the parameters are set to cover the whole window.ScissorTest ). GL. // returns an array . The ScissorTest can be enabled or disabled using EnableCap. Scissor Test The Scissor Test is used to limit drawing to a rectangular region of the viewport. only fragments inside the rectangle are passed to later pipeline stages. Width is used to specify the horizontal extension of the rectangle. Pixel Ownership Test Citation with minor modifications. use Result = GL.e.IsEnabled( EnableCap. When enabled.Disable( EnableCap. the pixel ownership test always passes.

invert ) the temporary coverage bitmask is generated by the value parameter . it is called 'alpha-to-coverage' and replaces the legacy alpha test. "figure out whether this is true" wrote: The coverage bitmask of incoming fragments can be set in a Fragment Shader with the variable gl_Coverage. out buffers) is 1. Multisampling can be also applied to transparent textures.Multisample is disabled but GetPName. If EnableCap.SampleCoverage Using GL.0 means that no samples are covered. However. blades of grass or the leaves of trees. A multisample buffer contains multiple samples per pixel.0.Multisample)) and make sure that GL. the Alpha value is read and used to generate a temporary coverage bitmask which is then combined through a bitwise AND with the fragment's coverage bitmask. the coverage is obtained by interpreting the alpha as a percentage: an alpha of 0. alpha-to- coverage will be disabled. a multisample buffer with 4 samples per pixel and an Alpha value of 0.SampleAlphaToCoverage is used. while a value of 1. when EnableCap. There are three OpenGL states related to alpha-to-coverage.before the bitwise AND with the fragment's coverage bitmask.Enable(EnableCap. depth and stencil values. a value of 0 indicates it will be left untouched. enable multisampling (GL. EnableCap. . Only samples who's bit is set to 1 after the bitwise AND are updated.SampleBuffers is 1. Multisample Fragment Operations (WIP) Multisampling is designed to counter the effects of aliasing at the edges of a primitive.SampleBuffers. like wire fences.GetInteger(GetPName. EnableCap.and if the invert parameter is true it is bitwise inverted .03.Disable()    EnableCap.SampleAlphaToCoverage For each sample at the current pixel. The term 'coverage' refers to a bitmask that is used to determine which of these samples will be updated: a coverage value of 1 indicates that the relevant sample will be updated. when it is rasterized into fragments.SampleCoverage( value. with each sample having it's own color. http://www. In this case. For example. they are controlled by GL.5 indicates that half of the samples are covered (their coverage bit is 1) and two are not covered (coverage bit is 0).name/index.humus.AlphaToOne Each Alpha value is replaced by 1.0 indicates that all samples are covered.php?page=Comments&ID=230 To enable alpha-to-coverage.Enable() and GL.

AlphaToOne can be queried with Result = GL.SampleCoverage is enabled.Multisample. Simply put.IsEnabled( cap ) The value set by GL.SampleCoverage( value.GetFloat( GetPName.SampleCoverage() can be queried with GL.SampleAlphaToCoverage. A more OpenGL related example: In any vehicle simulation the interior of the cockpit is usually masked by a stencil buffer..255] .StencilTest ). the Stencil Buffer is assumed to be 8 Bit large and able to represent the values 0. . ) The boolean set by GL. In order to use the Stencil buffer..StencilTest GL. the window-system provided framebuffer .. . Stencil Test The Stencil buffer's primary use is to apply a mask to the framebuffer.. the fragment is always passed to the next pipeline stage. EnableCap. as they would not contribute to the final image anyway.Enable( EnableCap..SampleCoverage and EnableCap. OpenGL's Stencil testing allows you to layer several of these masks over each other. so you may use a can of spraypaint to paint shapes. GL.SampleCoverage The values set by the command GL.must explicitly contain a logical stencil buffer. The paint will only pass the holes you had cut out and be blocked otherwise by the cardboard.ClearStencil( int ).   value is a single-precision float used to specify the Alpha value used to create the coverage bitmask..SampleCoverageValue.Disable( EnableCap.SampleCoverageInvert.GetBoolean( GetPName. ) 04.SampleCoverage() can be queried with GL.255 StencilTest functionality is enabled and disabled with EnableCap.or the Stencil attachment of a FBO . // default The value used by GL. That way alot of fragments of the outside landscape can be skipped.GL. For the purpose of clarity in this article. // 0 is the default. EnableCap. If there is no stencil buffer. invert is a boolean toggle to control whether the bitmask is bitwise inverted before the AND operation. State Queries The states of EnableCap. you can think of it as a cardboard stencil where you cut out holes. invert ) are only used when EnableCap.StencilTest ).Clear() commands can be set through: GL. Range [0. because it does not have to be redrawn every frame.

StencilFunc().Always. It sets the comparison function.StencilFunc() The command GL.Less .StencilFunc() but allows separate comparison functions for front. the command GL. but DepthTest fails.StencilMask() but allows separate comparison functions for front.    ref is an integer value to compare against. GL. GL.behavior when StencilTest fails.If StencilTest is enabled. ref. range [0 .Equal . regardless of DepthTest. mask ) is used to specify the conditions under which the StencilTest succeeds or fails.Test will succeed if ( ref & mask ) >= ( pixel & mask ) o StencilFunction. zfail.StencilFunc( func. o StencilFunction. o StencilFunction. writing can be limited by a bitfield through a bitwise AND. Note: The command GL. func of the test can have the following values.Test will succeed if ( ref & mask ) != ( pixel & mask ) o StencilFunction. Note: The command GL.StencilMaskSeparate() behaves exactly like GL.Notequal . zpass ) can be used to decide what action should be taken if the fragment passes the test. zfail . GL..Test will succeed if ( ref & mask ) == ( pixel & mask ) o StencilFunction.Always .Never . the default is StencilFunction.behavior when StencilTest succeeds.StencilMask( bitmask ).StencilOp() Depending on the result determined by GL.and back-facing polygons.Greater .and backfacing polygons. By default this value is 0. reference value and mask for the Stencil Test.   fail . Only the bits which are set to 1 are considered.StencilOp( fail. 255] mask is a bitfield which will be used in a bitwise AND. // By default all bits are set to 1. o StencilFunction.Test will never succeed.Gequal .Test will succeed if ( ref & mask ) > ( pixel & mask ) The word 'pixel' means in this case: The value in the Stencil Buffer at the current pixel.Lequal . .Test will succeed if ( ref & mask ) <= ( pixel & mask ) o StencilFunction.Test will succeed if ( ref & mask ) < ( pixel & mask ) o StencilFunction. By default all bits are set to 1.Test will always succeed.StencilFuncSeparate() behaves exactly like GL.

the default for all operations is StencilOp. . The bits available in the Stencil Buffer can be queried by GL.Keep         StencilOp.StencilClearValue. StencilOp.GetInteger( GetPName.StencilFunc() call.StencilPassDepthPass .StencilOp's parameter 'zfail' GetPName. Note: The command GL.GetInteger() and the following parameters: GetPName.increment Stencil Buffer by 1. If the result is greater than 255.set Stencil Buffer to ref value as specified by last GL. State Queries To determine whether StencilTest is enabled or disabled.GL.. use Result = GL.StencilOp() but allows separate comparison functions for front.GetInteger and the following parameters: GetPName.decrement Stencil Buffer by 1..Keep . or if StencilTest succeeds and DepthTest is disabled.Do not modify the Stencil Buffer.Incr .StencilValueMask .StencilFunc ..increment Stencil Buffer by 1. it becomes 0. StencilOp.StencilTest ). If the Stencil Buffer currently contains the bits 00111001.GL. StencilOp.ClearStencil() can be queried by GL. The state of the Stencil comparison function can be queried with GL. StencilOp.IncrWrap . The value set by GL. zpass .Replace . It is clamped at 0. StencilOp. it becomes 255..StencilRef . It is clamped at 255.StencilOp's parameter 'fail' GetPName. The bitfield set by GL.StencilFunc's parameter 'func' GetPName. ). The following values are allowed.StencilWritemask.GL.and back-facing polygons.StencilOpSeparate() behaves exactly like GL.IsEnabled( EnableCap. StencilOp.StencilFail .DecrWrap .StencilFunc's parameter 'ref' GetPName.StencilMask() can be queried by GL.StencilPassDepthFail . . ).GetInteger( GetPName.GL.StencilFunc's parameter 'mask' The state of the Stencil operations can be queried with GL.GL.GetInteger( GetPName.. If the result is less than 0.Decr .decrement Stencil Buffer by 1.Zero .StencilBits. it is set to 11000110.Invert .behavior when both tests succeed..set Stencil Buffer to 0. StencilOp.GL.StencilOp's parameter 'zpass' . .Bitwise invert. ).

If the fragment in question is further away from the eye than an already existing pixel.. I. With Intellisense you should not have any problems finding them.DepthFunc The command often called ZBuffer. In order to use the Depth buffer.Stencil***Separate() functions have been used. the fragment cannot be visible and is Related Extensions for further reading http://www. Range: [0.Clear() commands can be set through: GL.txt (promoted to core in GL 2.Less. GL. // true is the default GL. // 1.e.If the GL.txt (basically the same functionality as ATI_separate_stencil.0 . writing to the Depth buffer can be toggled by a boolean flag.Enable( EnableCap. DepthFunction.DepthTest ).or the Depth attachment of a FBO .Disable( EnableCap.0 is the default. the window-system provided framebuffer . The name was chosen due to X and Y being used to describe horizontal and vertical displacement on the screen. Function of the test can have the following values. the tokens GetPName.txt (promoted to core in GL 2.DepthMask( bool ).ClearDepth( double ). If there is no Depth buffer.0] If DepthTest is enabled.Never .Test will never succeed. so Z is used to measure the distance perpendicular to the screen.StencilBack*** become available to query the settings for back-facing polygons. The general purpose of this buffer is determining whether a fragment is occluded by a previously drawn pixel. the fragment is always passed to the next pipeline stage.DepthTest ).org/registry/specs/EXT/stencil_wrap. 1. Depth Test A commonly used logical buffer in OpenGL is the Depth buffer.opengl.Always .Test will always succeed.0) http://www.txt the default is DepthFunction.opengl.opengl.0) http://www.DepthTest GL. GL.opengl.must explicitly contain a logical Depth buffer.   DepthFunction. . DepthTest functionality is enabled and disabled with EnableCap.DepthFunc( func ) is used to specify the comparison method used whether a fragment is closer to the eye than the existing pixel it is compared to. // default The value used by GL. not in core though) 05.

Notequal . there is no rule that must satisfy ( near < far )... The Depth range can be queried with GL.DepthRange The command GL. please read Depth buffer . )..0 ).Test will succeed if ( fragment depth <= pixel depth ) DepthFunction. The boolean set by GL.DepthClearValue.DepthWritemask.DepthRange. Occlusion Query Occlusion queries count the number of fragments (or samples) that pass the depth test. .Less . . the complex object is drawn.DepthRange( near.Test will succeed if ( fragment depth < pixel depth ) DepthFunction. ).DepthTest ).. use Result = GL. Both parameters are expected to be of double-precision floating-point and must lie within the range [0.Equal .Gequal ..Test will succeed if ( fragment depth == pixel depth ) DepthFunction. The Depth comparison function can be queried with GL.DepthBits.DepthFunc. Please read Conditional Render for a convenient solution. If an object is drawn but 0 fragments passed the depth test. State Queries To determine whether DepthTest is enabled or disabled. ).GetFloat( GetPName. The bits available in the Stencil Buffer can be queried by GL.GetFloat( GetPName.GetInteger( GetPName.. 0.ClearDepth() can be queried by GL. far ) is used to define the minimum (near plane) and maximum (far plane) z-value that is stored in the Depth Buffer.Greater ...0 . . . For an in-depth explanation how the distribution of z-values in the Depth buffer works.IsEnabled( EnableCap.Test will succeed if ( fragment depth >= pixel depth ) DepthFunction.GetBoolean( GetPName. // returns an array 06. it is fully occluded by another object.The gritty details.. In practice this means that a simplification of an object is drawn using an occlusion query (for example: A bounding box can be the occlusion substitute for a truck) and only if fragments of the simple object pass the depth test.Test will succeed if ( fragment depth > pixel depth ) GL.Test will succeed if ( fragment depth != pixel depth ) DepthFunction.0. . It is allowed to call GL.0]. )... 1. .DepthRange( 1. which is useful to determine visibility of objects.GetInteger( GetPName.Lequal . ).      DepthFunction.DepthMask() can be queried by GL. The value set by GL.

BeginQuery() and GL. However this is not very efficient to use because the CPU will spin in a loop until the GPU is done counting. which also covers other caveats of occlusion queries. but before it is confirmed to be available any query of the count is not reliable.EndQuery(). ref MyOcculsionQuery ). If DepthTest is disabled all fragments will automatically pass it and the occlusion test becomes pointless.ColorMask and GL.DepthMask to false for the purpose of the occlusion query. Occlusion Query handles are generated and deleted similar to other OpenGL handles: uint MyOcclusionQuery.EndQuery( QueryTarget. Conditional Render .QueryResult. // draw..Note that the simplified object does not actually have to become visible. by the time the CPU is querying the result of the count the GPU might not be done counting yet. GetQueryObjectParam. It is very important to understand that this process is running asynchronous.GetQueryObject( MyOcculsionQuery.BeginQuery( QueryTarget. at the cost of slight visual glitches (an object may become visible one frame later than it should).SamplesPassed ). GL. out MyOcculsionQuery ). while ( ResultReady == 0 ) { GL. GL.Enable/Disable state associated with it is the DepthTest. one can set GL.DeleteQueries( 1. MyOcculsionQuery )..SamplesPassed. The draw commands which contribute to the count must be enclosed with GL.GetQueryObject( MyOcculsionQuery. out MyOcclusionQueryResult ). GL. You can read a very detailed description of this technique on Chapter 29 of GPU Gems 1. before you check the results of the query.QueryResultAvailable. GL. GL. In other words frame n executes the query and frame n + 1 reads back the results. Instead continue drawing as normal and wait for the next frame. } uint MyOcclusionQueryResult=0. This approach hides the latency inherent in occlusion queries and improves performance. uint ResultReady=0. The following code will get a reliable result. // MyOcclusionQueryResult is now reliable. GetQueryObjectParam. out ResultReady ). A better approach is to do the occlusion queries in the first frame and do not wait for a result.GenQueries( 1. OpenGL provides additional query commands to determine whether the occlusion query result is available. The only GL.

Draw().. GL. // etc. NvConditionalRender. A small cube which acts as ocludee.EndQuery( QueryTarget. out MyOcculsionQuery). GL.DeleteQueries(1. GL. public void OnLoad() { GL. the sphere is drawn. the cube is drawn unconditionally.DepthTest ).Draw().SwapBuffers().BeginConditionalRender( MyOcculsionQuery. in the given scene there are 3 objects:    A huge cylinder which acts as occluder.. depending on the outcome of the occlusion query used for the cube. } public void OnUnload() { GL. Think of it as a box that is anywhere in the "room" but not intersecting the pillar. This is probably best shown by a simple example.Enable( EnableCap.NV. } Although the running program might only show a single object on screen (the cylinder). // etc.SamplesPassed. Here is some pseudo-code how the implementation looks like. If the cube is fully occluded by the cylinder. ref MyOcculsionQuery).SamplesPassed ). uint MyOcculsionQuery.NV.EndConditionalRender(). MyCube.BeginQuery( QueryTarget. } public void OnRenderFrame() { // The cylinder is drawn unconditionally and used as occluder for the Cube and Sphere MyCylinder. MyOcculsionQuery ).. A small sphere which sits ontop of the cube.QueryWaitNv ). // Next. but the samples which passed the depth test are counted. MySphere. // depending on whether any sample passed the depth test. GL.The Extension NV_conditional_render adds a major improvement to occlusion queries: it allows a simple if ( SamplesPassed > 0 ) conditional to decide whether an object should be drawn based on the result of an occlusion query. GL. Only drawing of the sphere might be skipped.. drawing the sphere can be skipped.GenQueries(1.Draw(). . this. the cube is always drawn too. Think of it as a pillar in the center of the "room".

In order to use blending. That behaviour is desireable for opaque objects.Enable( EnableCap. For example: Drawing a character with skeletal animation is usually expensive. GL. then enable blending (also set the desired blend equation and factors) and finally the glass is drawn.Disable( EnableCap.BlendEquation( mode ) specifies how the results from stage 1 are .Blend. no blending can occur and behaviour is the same as if blending was disabled. a cylinder can be drawn using an occlusion query and the character is only drawn if the cylinder is not occluded. use GL. Blending is an operation to mix the incoming fragment color (SourceColor) with the color that is currently in the color buffer (DestinationColor). This happens in two stages for each channel of the color buffer: 1. index). The most common way to use them is drawing a simple bounding volume (of a more complex object) to determine whether samples passed and only draw the complex object itself. The DestinationColor is multiplied by the DestinationFactor. If there is no Alpha channel.Blend ). 07. Blending functionality for all draw buffers is enabled and disabled with EnableCap. Where index is used to specify the draw buffer associated with the symbolic constant GL_DRAW_BUFFER(index).Please note that this is not the standard case how to use occlusion query. every fragment is either rejected or written to the framebuffer. the logical color buffer must have an Alpha channel. GL. to determine whether it should be drawn at all. The factors used in this stage can be controlled with GL. index). Blending Without blending.Blend GL.Blend ).BlendEquation() The results of the above multiplications are then combined together to obtain the final result.BlendFunc() The SourceColor is multiplied by the SourceFactor. GL. The correct order of operation to draw a simple scene containing a solid table with a transparent glass ontop of it: draw the opaque table first. // default To enable or disable only a specific buffer if multiple color buffers are attached to the FBO. 2. but it does not allow rendering of translucent objects. The equation used in this stage can be controlled with GL. if the bounding volume is not occluded.Enable( IndexedEnableCap.Disable( IndexedEnableCap. or the color buffer uses color-index mode (8 Bit).BlendEquation The command GL.Blend.

DestinationColor.Blue DestinationColor.Red ). // specified by GL.Blue.Red = DestinationColor.Red.Red*SourceFactor. DestinationColor. BlendEquationMode.Red SourceColor.Green = min( SourceColor. Floating-point color buffers are not clamped.Blue*DestinationFactor.Red. DestinationColor. FinalColor. FinalColor. FinalColor. FinalColor. FinalColor.Green. DestinationColor.Alpha ). DestinationColor.Red*SourceFactor.Red*SourceFactor. FinalColor.FuncSubtract: FinalColor.Alpha = min( SourceColor.Alpha*DestinationFactor.Red = SourceColor.Blue*DestinationFactor.Red = max( SourceColor. FinalColor.Green ).Red + DestinationColor. SourceFactor and DestinationFactor are ignored.Blue*SourceFactor.Green.Alpha*DestinationFactor.Green ). 1. // incoming fragment DestinationColor.Max: When using this parameter. FinalColor.Red. FinalColor.Alpha.Green. If you wanted to implement this with OpenTK.Min: When using this parameter.Green = SourceColor.Green*SourceFactor.Red. DestinationColor.Red DestinationColor.Red = SourceColor.Green + DestinationColor.Red*DestinationFactor.Blue = min( SourceColor.0 .Green.Red*DestinationFactor. BlendEquationMode. DestinationColor.Green DestinationColor.Red = min( SourceColor.Alpha.0] prior to computing the result.Alpha*SourceFactor.FuncReverseSubtract: FinalColor. DestinationFactor. FinalColor.Blue + DestinationColor.Green*SourceFactor. FinalColor.Math.Blue = SourceColor. FinalColor.BlendFunc() FinalColor. DestinationColor.Blue = SourceColor. // framebuffer contents SourceFactor.Alpha ). // the resulting color Please note that for fixed-point color buffers both Colors are clamped to [0. it would look like this: Color4 SourceColor. FinalColor.Green*DestinationFactor.Alpha*SourceFactor.Red*DestinationFactor.Alpha + DestinationColor.FuncAdd: This is the default.Red ).Red.Blue.Alpha DestinationColor.Blue = max( SourceColor.Green = SourceColor.Blue ). SourceFactor and DestinationFactor are ignored.Alpha = SourceColor.Green = max( SourceColor.Alpha.Alpha = max( SourceColor. . FinalColor. FinalColor.Blue*SourceFactor.. BlendEquationMode.Alpha = SourceColor. Clamping into this range is left out in this code to improve legibility.Green*DestinationFactor. BlendEquationMode.Blue. BlendEquationMode.Alpha.Blue ).combined with each other.Blue.

0-DestinationColor.Alpha. 0.Green SourceColor.0] before it is passed to the next pipeline stage.0. DestinationColor. dest ) is used to select the SourceFactor (src) and DestinationFactor (dest) in the above equation.Green.Red.Alpha ) .DstColor: Color4 (DestinationColor. A ) is used to specify a constant color that can be used by GL.SrcAlpha: Color4 (SourceColor. For the scope of this page it is used to define the variable Color4 ConstantColor.Blue*SourceFactor.OneMinusDstAlpha: Color4 (1.Alpha.SrcColor: Color4 (SourceColor.Red.0-SourceColor.0) .0. 1..Zero: Color4 (0.Green*DestinationFactor.Alpha. 1. DestinationColor. GL.One and DestinationFactor is BlendingFactorDest. FinalColor.Green. no clamping occurs for floating-point color buffers.Blue.0-DestinationColor. 1.0-SourceColor.Alpha*DestinationFactor. 1.Alpha) .Alpha) .0. 1.0-DestinationColor.0DestinationColor.0-SourceColor. f.Alpha.0-SourceColor.0-SourceColor.Alpha) . 1.Alpha SourceColor.BlendColor( R.BlendEquationSeparate( modeRGB.Alpha.Alpha.Green.Blue.Alpha.Blue. 0.FinalColor. 0.Alpha. 1. 1.0.Blue.SrcAlphaSaturate: f = min( SourceColor. 1. DestinationColor.ConstantColor: Color4 ( ConstantColor.Green.Alpha*SourceFactor. The command GL. GL.Green*SourceFactor. 1.Alpha.Green = DestinationColor. ConstantColor.Alpha) . OpenGL 2.Green. SourceFactor is set to BlendingFactorSrc.Alpha) . f. the result in FinalColor is clamped to [0.BlendColor The command GL..BlendFunc The command GL.0-SourceColor.0 .0DestinationColor.Alpha) .Alpha.Blue.Alpha ).0-SourceColor.Alpha.One: Color4 (1.Alpha. 1.Alpha) .OneMinusDstColor: Color4 (1. modeAlpha ) accepts the same parameters as GL. By default.Red. 1. B.Blue*DestinationFactor. 1. SourceColor.Blue SourceColor.Green.Red.Alpha.0DestinationColor.Blue.OneMinusSrcAlpha: Color4 (1. DestinationColor. ConstantColor. If the color buffer is using fixed-point precision.BlendFunc( src. 1.             . Color4 ( f.0DestinationColor. SourceColor. FinalColor.0. 1.Zero.BlendEquation( mode ).Blue = DestinationColor. ConstantColor. SourceColor.OneMinusSrcColor: Color4 (1.0-SourceColor.Alpha) .0-DestinationColor.DstAlpha: Color4 (DestinationColor.Red. SourceColor.0 ) . G. 1. DestinationColor.0 and later supports separate equations for the RGB components and the Alpha component respectively. DestinationColor.Alpha = DestinationColor.0-DestinationColor. SourceColor. which gives the same result as if blending were disabled. SourceColor.BlendFunc().Alpha.0) .0. 1. 1.

Please refer to the inline documentation for details.Blue.BlendFuncSeparate() The selected blend equation can be queried by using GL.BlendFuncSeparate( srcRGB. use Result = GL.0-ConstantColor.set by GL. . no conversion is applied.Alpha.BlendFuncSeparate() GetPName.BlendEquationAlpha .Alpha.set by GL. Green and Blue values after blending are converted into the non-linear sRGB color space.BlendSrc . dstRGB.BlendDst .BlendDstAlpha .set by GL.0-ConstantColor. dstAlpha ) accepts the same factors as GL. 1. index).BlendSrcRgb . for source and destination respectively. If any of those conditions is false.BlendEquationSeparate() 08.BlendEquation .Alpha.Alpha.set by GL.set by GL.0ConstantColor.FramebufferSrgb is enabled and if the color encoding for the framebuffer attachment is sRGB (as in: not linear). The selected blend factors can be queried separately for source and destination by using GL.Alpha) OpenTK uses the enums BlendingFactorSrc and BlendingFactorDest to narrow down your options what is a valid parameter for src and dest.Alpha) .Blend.   If those conditions are true.Alpha ) .Blend ). ConstantColor. the Red.BlendFuncSeparate() GetPName.BlendSrcAlpha .OneMinusConstantColor: Color4 ( 1.BlendEquationSeparate() GetPName.set by GL.set by GL.set by GL.Alpha. The command GL.BlendDstRgb .Red. To query blending state of a specific draw buffer: Result = GL.BlendFuncSeparate() GetPName. 1.Alpha. OpenGL 2.GetInteger() with       GetPName. State Queries To determine whether blending for all draw buffers is enabled or disabled.BlendEquation() GetPName. 1. sRGB Conversion This stage of the pipeline is only applied if EnableCap.BlendFunc() GetPName. srcAlpha.0-ConstantColor.0ConstantColor. SourceFactor and DestinationFactor.OneMinusConstantAlpha: Color4 (1.BlendFunc() GetPName.IsEnabled(IndexedEnableCap.set by GL.ConstantAlpha: Color4 (ConstantColor. 1.Green.0-ConstantColor.BlendFunc( src.0 and later supports separate factors for RGB and Alpha.GetInteger() with    GetPName. dest ).IsEnabled( EnableCap. Not all parameters are valid factors for both.0-ConstantColor. ConstantColor. ConstantColor. 1.BlendEquationRgb .0-ConstantColor.   . 1.

Logical operations are performed independently for each Red. use EnableCap.e. 10. GL. dithering is performed separately for Red. which is usually RGBA8. and B.. I. GL. 09.IsEnabled( EnableCap. In RGBA mode.Dither. In order to apply the Logicial Operation.Disable( EnableCap. but the image to be drawn is actually calculated with higher precision. Logical Operations Before a fragment is written to the framebuffer. OpenGL behaves as if Blending is disabled regardless whether it was previously enabled. the programmer has no control over how the image is manipulated (the graphics hardware decides which algorithm is used) besides enabling or disabling dithering with EnableCap. If the color buffer is fixed-point.0 . Green. and the unmodified Alpha form a new RGBA color value.Dither ).Enable( EnableCap.Enable( EnableCap. // default .Disable( EnableCap. Dithering also applies when a RGBA32f color is converted to display on the screen. // default GL.0] and then converted to a fixed-point value. dithering is used to find one or more representable colors to ensure the image shown on the screen is a best-match between the capability of the monitor and the computed image. This is always needed when working with 8 Bit color-index mode. After the selected operation is completed. The resulting four values are sent to the subsequent dithering operation. 1.ColorLogicOp ). In a similar way. where only 256 unique colors can be represented. Dithering is the only state that is enabled by default. OpenGL can dither the fragment from a high precision color to a lower precision color. destination is overwritten. a logical operation is applied which uses the incoming fragment values as source (s) and/or those currently stored in the color buffer as destination (d). Blue and Alpha value and if the framebuffer has multiple color attachments.ColorLogicOp ).Dither ). but due to using patterns the appearance of many shades of gray can be represented. the logical operation is computed and applied separately for each color buffer.The resulting values for R. Green. State Query The state of dithering can be queried through Result = GL. If Logic Op is enabled.Dither ). Dithering Dithering is similar to halftoning in newspapers. Only a single color (black) is used in contrast to the paper (white).ColorLogicOp GL. each component is clamped to the range [0. Blue and Alpha. G.

You can improve performance significantly by removing the bmp.LogicOp or EnableCap.And: s & d LogicOp.e.OrReverse: s | !d LogicOp.Clear: 0 LogicOp. Make sure you have bound the correct framebuffer object before calling GrabScreenshot(). Otherwise. Call GrabScreenshot() from your main rendering thread. use GL.Or: s | d LogicOp..OrInverted: !s | d LogicOp. only indexed color buffers (8 Bit) are affected..Xor: s XOR d LogicOp. If you wish to save a video.LogicOp ). i. ) How to save an OpenGL rendering to disk You can use the following code to read back an OpenGL rendering to a System.AndReverse: s & !d LogicOp.IndexLogicOp. To select the logical operation to be performed.CopyInverted: !s LogicOp.Bitmap.Note: If you use EnableCap. where op is by default LogicOp.Equiv: !(s XOR d) LogicOp.Set: all 1's State Queries The state of LogicOp can be queried with Result = GL. rather than a single screenshot. Hints:     Don't forget to call Dispose() on the returned Bitmap once you are done with it.RotateFlip() call and saving the resulting image as a BMP rather than a . consider modifying this method to reuse the same Bitmap. .LogicOpMode.                 LogicOp.IsEnabled( EnableCap. you will run out of memory rapidly.Copy.Copy: s LogicOp. the thread which contains your GraphicsContext.LogicOp() can be queried with GL.Drawing.AndInverted: !s & d LogicOp.Noop: d LogicOp.GetInteger( GetPName. Which operation has been set through GL.Nand: !(s & d) LogicOp.Invert: !d LogicOp.LogicOp( op ). You can then use the Save() method to save this to disk.Nor: !(s | d) LogicOp.

Bgr. System.Imaging.Drawing. 2. is the difference between a real-time recording and a slideshow.UnlockBits(data). poor support for complex scripts) and (b) it only supports 2d text.Height). bmp. GL.ReadPixels(0. PixelFormat. There are many programs that can encode a stream of consecutive BMP files into a high definition video.UnsignedByte. System.RotateNoneFlipY). dynamic text can be reasonably efficient as long as care is taken to update only regions that are actually modified. PixelType. 0.LockBits(this.Bitmap with the contents of the current framebuffer public static Bitmap GrabScreenshot() { if (GraphicsContext. data.Width.Graphics.ImageLockMode.Width. Use Graphics. } } How to render text using OpenGL The simplest way to render text with OpenGL is to use System. This approach is extremely efficient for text that changes infrequently. OpenTK.Scan0). OpenTK.Drawing. Bitmap bmp = new Bitmap(this. Render the OpenGL texture as a fullscreen quad. given suitable hardware and a little optimization (as outlined above).OpenGL.Imaging.Drawing (i.ClientRectangle. Moreover. because only step 3 has to be performed every frame.Drawing. Additionally.CurrentContext == null) throw new GraphicsContextMissingException(). This approach has three steps: 1.Drawing. bmp. care should be taken to recreate the Bitmap and OpenGL texture whenever the parent window changes size.ClientSize.WriteOnly. This code can record 720p/30Hz video relatively easily.RotateFlip(RotateFlipType.Drawing.Height. this. This is especially important if you wish to record a video . 3. The downside of this approach is that (a) rendering is constrained by the capabilities of System.PixelFormat.ClientSize. this. .ClientSize. return bmp.ClientSize. PNG file. using using using using static class GraphicsHelpers { // Returns a System.Imaging.Graphics.BitmapData data = bmp. this.e. Upload the Bitmap to an OpenGL texture. System. System.Format24bppRgb).DrawString() to render text to a Bitmap.

LoadIdentity().OpenGL. PixelType.BindTexture(text_texture). (int)All.Zero). Bitmap text_bmp.Rgba.Width. Height.Texture2D. . GL. int text_texture.Sample code: using System. e) => { // Ensure Bitmap and texture match window size text_bmp.Drawing. GL. // Do this every frame.Height).UnsignedByte. // Do this only when text changes. 0.UnlockBits(data). data. }. ClientSize.BindTexture(text_texture). IntPtr. PixelType.Bgra. // Do this only when text changes.TexParameter(TextureTarget.Width. PixelType. System. gfx.Drawing. GL. 0. TextureParameterName.Linear)..Height).TextureMinFilter. TextureParameterName.OnLoad += (sender.TexSubImage2D(TextureTarget.Graphics. text_bmp.). GL.Transparent).Height).GenTexture(). // just allocate memory.Bgra.DrawString(. text_bmp = new Bitmap(ClientSize.Bgra.MatrixMode(MatrixMode.Texture2D.Dispose(). text_bmp. render using a quad. PixelInternalFormat. ImageLockMode.UnsignedByte. text_bmp.Width.Width. PixelFormat..LockBits(new Rectangle(0.Linear). 0.Projection). GL. text_bmp.Resize += (sender.FromImage(text_bmp)) { gfx. 0. 0. GL.TextureMagFilter.PixelFormat. PixelFormat.TexParameter(TextureTarget. text_bmp. // Finally. GL.Scan0).Height.UnsignedByte. Width.Drawing.Texture2D.Texture2D. // Render text using System. window.Rgba.Width. ClientSize. // match window size text_texture = GL. e) => { // Create Bitmap and OpenGL texture text_bmp = new Bitmap(ClientSize. PixelFormat. text_bmp. using OpenTK. text_bmp. BitmapData data = text_bmp. so we can update efficiently using TexSubImage2D }.Clear(Color.Height.TexImage2D(TextureTarget. 0. GL. GL. (int)All. window.Imaging.ReadOnly.Zero). 0.TexImage2D(TextureTarget. IntPtr. // Draw as many strings as you need } // Upload the Bitmap to OpenGL. using (Graphics gfx = Graphics. PixelInternalFormat.Format32bppArgb). 0.Texture2D.

Vertex2(0f.Begin(BeginMode.Sleep.Vertex2(0f. GL. Buffers and X-Ram OpenTK. GL. Rather use . Audio Capture Extension.Enable(EnableCap. If this is a Problem. Devices. -1. GL. Note that some functions of the OpenAL API are not imported for safety reasons. Enumeration Extension. GL. Regarding compatibility.Sleep than Alut. GL. 1).Vertex2(1f.Ortho(0. GL. Height. . namely the Reverb Effect and Lowpass Filter.BlendFunc(BlendingFactorSrc.Enable(EnableCap.One.TexCoord(0f.Net's Thread. Width.1: Multi-Channel Buffer playback Extension.Quads). GL. the "Generic Software" and "Generic Hardware" implementations of the OpenAL driver support OpenAL 1.CreateBuffer* instead of Alut.Blend).OneMinusSourceAlpha).Vertex2(1f. GL. 0f).wav files to toy around with and a few .Audio. GL.TexCoord(1f. It is recommended using these book pages as a starting point. GL.Texture2D).GL. but will provide you with some . and Alut. 0.1 and a few EFX Extensions. 0f). 1f). 1f). Chapter 5: OpenTK. 0f). and visit the online resources from the OpenAL website's documentation page for in-depth information. GL. OpenAL contains the following classes:      AL "Audio Library" Alc "Audio Library Context" Alut "Audio Library Utilities" XRam "Memory Extension" Efx "Effects Extension" OpenAL 1. Downloading the OpenAL SDK is not required.LoadMemory*.TexCoord(1f.AudioContext handles Device and Context allocation through Alc. like Pango#. This method can be easily generalized to use a more powerful text rendering library. 1f).TexCoord(0f. If the used Device cannot handle EAX natively. 0f). GL.End().pdf files not available directly at the OpenAL site.1 Crashcourse will give an introduction how to use OpenAL in your applications. BlendingFactorDst. 1f). GL. the driver will attempt to emulate the missing features.Audio (OpenAL) The OpenAL 1.0 Extensions that were included into 1. please voice it in the forum. 1.

BufferData() to pass the raw sound data into OpenAL's internal memory.dll in the application directory if ( XRam. 42f. sound. Buffers Buffers in OpenAL are merely the storage for audio data in raw format.5f). 500f. Example code: try { AudioContext AC = new AudioContext().Instantiating a new AudioContext with a parameterless constructor will initialize the default Device and Context and makes it current.Hardware ). out MyBuffers ).IsInitialized ) XRam. it's use is optional and not required. AL.Sine. XRamStorage.GetError() != ALError.BufferData(MyBuffers[0]. To use the Extension. // optional MyBuffers[1] = Alut.Exit(). . // must be instantiated per used Device if X-Ram is desired. cannot continue Application. this currently requires Alut.BufferData() or using the AudioReader class (which loads a file from disk).GenBuffers(). // reserve 2 Handles uint[] MyBuffers = new uint[2]. // Load a .ReadToEnd()). } // Create a sinus waveform through parameters. a Buffer Name (often called Handle) must be generated using AL. The AudioReader functions implicitly use AL. if ( AL. This buffer can now be filled using AL.GenBuffers( 2.SetBufferMode( ref MyBuffer[0]. 1.IsInitialized ) XRam. The instantiated object contains a bool that returns if the Extension is usable. Calling the instance's Dispose method will destroy the Device and Context.wav file from disk if ( XRam. } XRam = new XRamExtension( ). X-Ram The X-Ram Extension allows to manually assign Buffers a storage space.SetBufferMode( ref MyBuffer[1]. // optional AudioReader sound = new AudioReader(filename) AL. // See next book page how to connect the buffers to sources in order to play them. which should be checked before calling one of the Extension's Methods. the XRam wrapper must be instantiated (per used Device). which will take care of most ugly things with Extensions for you.CreateBufferWaveform(AlutWaveform.NoError ) { // respond to load error etc. XRamStorage. } catch( AudioException e) { // problem with Device or Context.Hardware ).

Vector3 Position = new Vector3( 1f.SourceStop( MySources[1] ). 2. ALSourcei. Gain (Volume amplification) and more. ALSource3f. All I can give here is a brief overview that might help you make the decision if EFX is what you need. Now that the Buffer Handle has been assigned a sound. (int)MyBuffers[0] ).Source( TestSources[0].Source( MySources[1]. // free previously reserved Handles AC.GenSources( 2. Sources and EFX Sources Sources represent the parameters how a Buffer Object is played back. . A description of the sound data in the Buffer can be queried using AL. ref MySources ). ALSourceb. AL. // halt playback AL.SourcePlay( MySources[0]).Dispose(). (int)MyBuffers[1] ). You will have to download the OpenAL SDK to get a copy of "Effects Extension Guide.pdf" from Creative labs.Source( MySources[1].Source( MySources[1].ReadLine().Source( MySources[0].Buffer. 2f. out MySources ). ref MyBuffers ). ALSourcef. // source loops infinitely AL. AL. Console. // attach the buffer to a source AL. // gen 2 Source Handles AL. // wait for keystroke before exiting AL.Gain. // free Handles // now delete Buffer Objects and dispose the AudioContext EFX I'm sorry to do this.SourceStop( MySources[0] ).85f ). ref Position ). AL. 3f ). for in-depth information about programming with DSPs.Position. but if you want to work with EFX there is no other way. The settings can be set/get by using AL.Buffer. // start playback AL.Source and AL. Continuation of the sourcecode from previous page: uint[] MySources = new uint[2].GetBuffer(). true ). AL. Velocity.// Cleanup on application shutdown AL. ALSourcei. These parameters include the Source's Position.Looping.DeleteBuffers( 2. AL.DeleteSources( 2. we need to attach it to a Source for playback.SourcePlay( MySources[1] ). 0.GetSource functions.

binding the reserved Handle 0 to a Slot will detach the previously bound Effect Object from it. and either filter the "dry signal" that goes directly into the output mixer. Context and Listener Like in OpenGL. which is useful to achieve the effect of obstruction. The settings can be set/get by using AL. The Slots are only used if there is a valid Effect Object attached to them. You can create multiple Contexts per Device. which use the same Device. The new OpenAL Objects that come with EFX are "Effect". whose output goes directly into the final output mix. Flanger. OpenAL does not have an equivalent to SwapBuffers(). to make sure a Source is properly positioned. or filter the "wet signal" that is forwarded to an Auxiliary Effect Slot. With EFX you may reroute a source's output through Filters and/or into Auxiliary Effect Slots. Environmental effects might look nice as a "selling point" on paper. 3. This is very similar to OpenGL's Projection Matrix. Listener The Listener represents the position and orientation of the Camera in the environment.GetSource functions. Note that in contrast to OpenGL. but do not add any gameplay value to a Strategy game. or a 2D platform game.My advice is ignoring EFX. It makes sense to handle it together with your OpenGL camera. a Context can be understood as an instance of OpenAL State.Source and AL.   In vanilla OpenAL you load a Buffer. attach it to a Source and besides the Source's parameters that's all the influence you have about what ends up in the mixer. with the . thus there can be only one per Context. or until the programmer manually stops the Source playback. but each Context has the restriction of 1 Listener it's own unique Sources. Distortion. Auxiliary Effect Slots are containers for Effect Objects. The addition to OpenAL with EFX Extension is the rerouting of output signals. obstacles or doors. A Sources are automatically played until the end of their attached Buffer is reached. "Auxiliary Effect Slot" and "Filter". Types of Effects are for example Echo. A Filter can be attached to a source. occlusion or exclusion of a sound due to environment features like walls. Chorus. etc. unless your game project is in 1st Person 3D. An Effect Object stores the type of effect and the values for parameters of that effect. Buffer Objects on the other hand may be shared by Contexts. This allows more fine control about how a Source sounds when played.

2. Vector3 ListenerTarget = new Vector3(ListenerPosition. OpenGL and OpenAL is assumed. Vector3.Frustum(-0.0). // update OpenAL .Cosinus). 50. ref ListenerPosition).LoadIdentity(). Cosinus). GL. float listenerAngle) { // prepare some calculations float Sinus = (float)Math. -0. It builds on the previous two chapters and a good grasp of C#.X + Sinus. // update OpenGL .1.Projection).exception that there is no Frustum culling for audio (you may not see something behind you.1333. A sample Camera.Cos(listenerAngle). 0.Orientation. 0.Listener( position GL. } Chapter 6: OpenTK. float Cosinus = (float)Math.MatrixMode(MatrixMode.Compute (OpenCL) [Describe the OpenTK. Optimizations . GL. 0. ListenerPosition.Compute namespace] Chapter 7: OpenTK. Vertex Cache Optimizations Graphic cards usually have 2 Caches designed to help processing Vertices.UnitY). but you can hear it).LookAt(ListenerPosition. ref ListenerDirection.UnitY).place listener at camera AL. ListenerPosition.1333. Glu. Pre T&L Cache This Cache merely stores the untransformed Vertex read from a VBO.Listener(ALListenerfv. ListenerTarget. one of their favorite tasks. AL. OpenGL and OpenAL.1.Sin(listenerAngle).Net/Mono.Z .Input [Discuss the input classes provided by OpenTK] Chapter 8: Advanced Topics This chapter discusses advanced topics on the interaction of . Vector3 ListenerDirection = new Vector3(Sinus. taken from the OpenAL manual: void PlaceCamera(Vector3 ListenerPosition.Position. 0.Y. ref Vector3.

.3) rather then ( . either strips or lists -the performance difference between strips and lists is negligible when taking advantage of the post-TnL cache. this Cache is typically very small (8 is minimum.. so the IBO issues Triangles in this order (0.1. While Pre-T&L Cache optimization only operates on the Post T&L optimization will only operate on Indices (Primitives).pdf http://www. and GeForce 4 MX chipsets to effectively 18 (actual 24) on GeForce 3 and GeForce 4 Ti chipsets. Storing these post-transform and lighting (post-TnL) vertices avoids recomputing the same values whenever a vertex is shared between multiple triangles and thus saves time.2044.0.html (DirectX based detector) Useful quotes: truncated quote from: http://developer. The postTnL cache increases rendering performance by up to 2x.princeton.cs.html "When rendering using the hardware transform-and-lighting (TnL) pipeline or vertexshaders. Post T&L Cache The more valuable Cache is the one storing the transformed results from the Vertex Shader.regarding this part of the Cache are simply sorting your Vertices in order of post-TnL cache is a strict First-In-First-Out buffer. GeForce 2..2.html http://ati. Nonindexed draw-calls cannot take advantage of the http://developer.clootie.html http://www.2).. 12-24 common) holding only very few Entries.nvidia.nvidia.html (ancient) http://developer. Typically the Post T&L is calculated first. because GL.umd.cs. .DrawArrays cannot make any assumptions which Vertices are actually and varies in size from effectively 10 (actual 16) vertices on GeForce 256...nvidia.amd. .com/object/nvtristrip_library. .php the GPU intermittently caches transformed and lit vertices. It will only work with indexed primitives passed to The draw-call must be with an indexed primitive-type (see above)..amd. This Cache is typically extremely large. being able to hold ~64k Vertices on a Geforce 3 and up.The mesh needs to be submitted in a single draw-call to optimize batch-size. as it is then impossible for the GPU to know which vertices are shared." Last Update of the Links: January 2008 .com/developer/i3d2006/I3D2006-Sander-TOO. Links for further reading http://ati. and the Pre T&L sorting step is performed on the optimized Indices Array.

Please do not use it! // The OpenGL disposable pattern class GraphicsResource: IDisposable { int resource_handle. a different methodology must be followed.Destroy += ContextDisposed. resource_handle = [. since the context used to create the resources is not available in the finalizer thread! Since OpenGL functions cannot be called in finalizers. to release all related resources. by extending the concept of the OpenGL context. This poses some problems on OpenGL resource deallocation. and allocate the resource context = GraphicsContext.CurrentThread. The following code describes the implementation of the "OpenGL disposable pattern" in OpenTK.Thread.Net arsenal. pinning and performance considerations] GC & OpenGL (work in progress) As discussed in the previous chapter. Last. System. not only because it increases productivity but also because it provides extremely fast memory allocations (compared to standard C/C++ malloc/new). By modifying the finalizer logic we can provide a way to flag resources as 'dead'.". GC finalization occurs on the finalizer thread. generational because it distinguishes long-lived objects objects from temporary ones. } .Threading.Net Framework features an aggressive. but it is easy to adapt this code to any managed OpenGL project: // This code is out-of-date.Garbage Collection Performance The .].ManagedThreadId)). if (context == null) throw new InvalidOperationException(String. [Describe the unmanaged resource pool. and destroy them from the main thread.Format( "No OpenGL context available in thread {0}. and compacting because it moves data in memory to avoid leaving holes behind. we can use the Dispose() method to deterministaclly destroy OpenGL resources in the main thread. // The OpenGL handle to the resource GraphicsContext context. By implementing the disposable pattern. we can be notified of context destruction. // The context which owns this resource public GraphicsResource() { // Obtain the current OpenGL context. context...CurrentContext. generational and compacting Garbage Collector (GC): aggressive because it knows the location and reachability of every managed object. The GC is a great tool in the .

EventArgs e) { context.Disposable Pattern --private void ContextDisposed(IGraphicsContext sender.RegisterForDisposal(this). and they are destroyed through the DisposeResources() method.CurrentContext. context.#region --. } } } ~GraphicsResource() { Dispose(false). while garbage collect-able OpenGL resources consume slightly more memory (due to the reference to the GraphicsContext). } public void Dispose() { Dispose(true). // TODO: Shared resources shouldn't be destroyed here. } // If the owning context is current then destroy the resource. // TODO: Is the "manual" flag necessary? Simply checking for the // owning context should be enough.Destroy -= ContextDisposed. Resource creation takes a small performance hit due to the call to GraphicsContext. private void Dispose(bool manual) { if (!disposed) { if (!context.SuppressFinalize(this). The whole process is deterministic: it is your responsibility to call DisposeResources at appropriate time intervals (or setup up a timer event to do this for you).KeepAlive(this).IsCurrent || !manual) { GC. Resources are added to this queue through the RegisterForDisposal() call. } #endregion } In OpenTK. Prefer .. // otherwise flag it (so it will be destroyed from the correct thread). GC. each GraphicsContext class maintains a queue of OpenGL resources that need to be destroyed. } else { // Destroy resource_handle through OpenGL disposed = true. Dispose().

Chapter 9: Hacking OpenTK This chapter contains instructions for people wishing to modify or extend OpenTK. It covers topics related to OpenGL.Build: this assembly provides the cross-platform build system for OpenTK. OpenGL|ES and OpenCL bindings. OpenCL and general OpenTK usage.0 or higher). It describes the project structure. OpenAL. which shows how to setup and build an OpenTK application. In addition to these assemblies. OpenTK provides a QuickStart project. coding style and various caveats and hacks employed by OpenTK to achieve wider platform support.Compatibility: this assembly provides an upgrade path for applications compiled against older versions of OpenTK and the Tao Framework.calling the Dispose() method to destroy resources instead of relying on the GC. which converts the OpenGL|ES and OpenCL C headers to XML files. Project Structure The OpenTK project consists of a number of managed. OpenTK Structure The OpenTK solution provides the following public namespaces: . It consists of two assemblies:   Converter. Bind. it is added to this dll. When a deprecated method is removed from core OpenTK. Finally. It provides the Graphics. OpenTK. The current implementation in OpenTK does not take shared contexts into account this will be taken care of in the near future. Sharpdevelop (version 2. wrapper design. as finalizable resources are only collected on a generation 1 or 2 GC sweep. which adds OpenGL support to System. which converts the OpenGL . OpenTK.Windows.spec files or the Converter XML files into C# code. cross-platform assemblies:      OpenTK: this is the core OpenTK assembly. Audio and Compute APIs. OpenTK. OpenTK. the math toolkit and the platform abstraction layer. OpenGL|ES. It can be used to generate MSBuild-compatible project files for use with Visual Studio (version 2005 or higher).Forms applications.0 or higher) and MonoDevelop (version 2. OpenTK maintains a custom binding generator which generates the OpenGL.Examples: this assembly provides a number of samples built with OpenTK.GLControl: this assembly provides the GLControl class.

Platform namespace. The public API of OpenTK is completely cross-platform. OpenTK places an emphasis in usability and developer efficiency. OpenTK.Factory.Platform. All platform-specific code is contained in internal interfaces under the OpenTK. DisplayResolution) as well as query the platform configuration. Classes that do not rely on platform-specific code and classes that contain performance-sensitive code do not use this pattern: the various math classes. To that end. NativeWindow). Joystick). most public classes act as façades that forward method calls to the correct platform-specific implementation. In that sense. OpenTK. NativeWindow and the various input classes. the AudioContext and AudioCapture classes all fall into these categories. Wrapper Design OpenTK provides . it utilizes a number of . perform 3d math. public Foo() { implementation = OpenTK. while staying true to the nature of the native interface. OpenTK.Platform: contains classes to extend OpenTK or interact with the underlying platform. OpenGL ES.Net constructs that are not available in native C by default: . GraphicsMode.CreateFoo(). OpenCL and OpenAL bindings.Default.Bar().Net wrappers for a various important native APIs: OpenGL. public class Foo : IFoo { IFoo implementation. the OpenGL. Unlike similar libraries. GraphicsContext. interact with the monitor (DisplayDevice. } #region IFoo Members public void Bar() { implementation. OpenAL and OpenCL (in progress).Compute: contains bindings for OpenCL. Mouse.Graphics: contains bindings for OpenGL and OpenGL|ES. } #endregion } This pattern is used in all public OpenTK classes that need platform-specific code to operate: DisplayDevice. OpenTK. DisplayResolution.Input: contains classes to interact with input devices (Keyboard.      OpenTK: contains classes to create windows (GameWindow. OpenTK.Audio: contains bindings for OpenAL.

3.khronos. takes advantage of . The bindings are generated through an automated binding generator. 5. fonts. Cross-platform support. GL.Graphics code: GL. Inline documentation for functions and parameters. 'al'). Official API specifications:     OpenGL: Generics instead of void pointers. etc) As such.creativelabs.        Strongly-typed enum parameters instead of integer constants. Is OpenTK limited to games? No! OpenTK can be . which makes the bindings usable by all . input devices.Net languages. OpenAL and OpenCL 2.used in scientific visualizations.aspx OpenCL: http://www. OpenAL: http://connect. Namespaces instead of function prefixes ('gl'. Consider the following code snippet: 4. provides helper functions (math. on the other hand..). What is the difference between OpenTK and the Tao framework? The Tao framework tries to follow the unmanaged APIs as closely as possible.End(). // Tao.Vertex3(Vector3. Vector3d. strong-typing and generics. accessible through IDE tooltips (intellisense).Begin(BeginMode. GL.Net features like function overloading. which allows the bindings to be used by any platform supported by .Net or Mono. CLS-compliance. // OpenTK. Automatic extension loading. 7. The following pages describe the generation process in OpenGL ES: http://www. VR.khronos. Function overloads instead of function suffices (Vector3 instead of Vector3f>. and 3. allows . GL.opengl.Up). which converts the official API specifications into C# code.Points). is roughly analogous to SlimDX.Yellow). . Appendix 1: Frequently asked questions [General Questions] 1. SDL or GLFW. abstracts away the platform-specific code for window creation. 2. modeling/CAD software and other projects.and has been .. What is the Open Toolkit exactly? The Open Toolkit is a C# library that: 1.Net programs to access OpenGL.Color3(Color.OpenGl code: .

OpenGl. Gl. 0). 0).check out the project database. However. 'ub'. but it illustrates the difference nicely: OpenTK removes unecessary cruft ('gl'.TopMost = true. All these become more important as a project grows in size.OpenGl. Is OpenTK safe to use? How mature is it? OpenTK is considered safe for general use. 'f').Windows references with OpenTK and OpenTK.Platform.OpenAl and Tao. with regular bugfix and feature releases. the Open Toolkit mirrors the raw OpenGL API.GL_POINTS). . [Windows. strong-types). A lighter version is also made available for the iPhone through the MonoTouch project (recompilation required).glEnd().9. 12. OpenGL is not object-oriented. It is being used successfully by both free and commercial projects and the library is under active development.SimpleOpenGlControl. Tao. There are other differences not so readily apparent: OpenTK will not allow you to pass invalid data to OpenGL (wrong tokens or non-valuetype data).Net/Mono) introduce some unique performance considerations . it plays better with intellisense (inline documentation. How can I make my Form fullscreen? Use the following code snippet: 2.Net ('Color'). users have contributed object-oriented libraries built on top of OpenTK . There is no official support for Windows Mobile or Android at this point. myForm. Tao. 9. uses strongly-typed enums ('BeginMode') and integrates better with .OpenAl and Tao. However. 11. OpenTK introduces minimal overhead over raw OpenGL.9-2. All features work across platform without recompilation. Which platforms does OpenTK run on? OpenTK is primarily tested on Windows. Performance is always a concern. Is OpenTK slow? No. Gl.glBegin(Gl. but is also known to work on Solaris and *BSD variants.glColor3ub(255. Simply replace your Tao.refer to our documentation for more information. Will my Tao project run on OpenTK? Starting with version 0. do note that the underlying runtimes (. overloads. while gaining advantage of all OpenTK features.Gl. Does OpenTK change that? No. 8. The code is trivial. it checks for OpenGL errors automatically in debug builds.glVertex3f(0.Compatibility and your project will as before. myForm. Gl.None. This was a conscious design decision. 255. to avoid introducing artificial limitations. 3.FormBorderStyle = FormBorderStyle. 1. Linux and Mac OS X. OpenTK is compatible with Tao. I care about speed.Forms & GLControl Questions] 1. so please report an issue if you believe something could run faster.Windows.Platform. 10.

class CustomGLControl : GLControl 7. Closed-source and / or commercial projects will be reviewed by the Open Toolkit team and approved on a case by case basis.WindowState = FormWindowState. Keep backups! . Every project in the database receives a unique project page and gains access to the issue tracker and the project release service. 24. 3. extend or be somehow related to the Open Toolkit library. nor fitness of purpose towards any application. Your project must be released under an OSI approved license. express or implied. antialiasing or OpenGL 3. 2. 24bpp z-depth.9-4 or newer. for the behavior of the project database. This is a free service provided to the Open Toolkit community that comes without any warranty. Appendix 2: Function Reference You can read the function reference online. All registered users may submit their own projects. 0. : base(new GraphicsMode(32.Maximized. public CustomGLControl() 11. How can I save a screenshot? Refer to the "How to save an OpenGL rendering to disk" section in the documentation. How do I use stencil.myForm. { } } [Graphics questions] 1.ForwardCompatible) 12. Your project must use. 4. // OpenGL version is major=3. // 32bpp color. GraphicsContextFlags. { 8. under the Documentation/ folder. subject to the following restrictions: 1. 4). A PDF version of this reference is included with your OpenTK distribution.x with GLControl? Create a custom control that inherits from GLControl: 6. My GLControl. the OpenT Toolkit team does not offer you any warranty. In case there is any doubt.9. you acknowledge that: 1. By submitting a project to the database. Please upgrade to OpenTK 0. 5. 8. 8bpp stencil and 4x antialiasing 9. minor=0 10. Appendix 3: The project database The project database is an index of projects related to the Open Toolkit.Load event isn't fired.

type a descriptive name for your project (e. 6. for whatever reason. 5.0. In the "project categories" section.2. The first step is to select your project from the drop-down list. This information is important. features). 2.g. Fill in the release version. It is reserved for the Open Toolkit. what it does and any other information you deem relevant (e.g. requirements. project/opentk).0 (not yet released at the time of writing). Don't forget to add a link to the homepage of the project (if any).x here. you should choose 1. Please note that OpenTK is backwards compatible. without prior notice. This should match the actual version in your project properties (you can view this information in Visual Studio by rightclicking your project. as it indicates whether different projects can be used together. as users tend to avoid projects without or with low quality screenshots. o You can use the control key to select multiple categories. you can improve their quality by enabling antialiasing and anisotropic filtering.9. If your project targets OpenTK 1. In the "short project name" field. o If your project is closed-source or commercial. which means you should choose the lowest OpenTK version that can support your project. The Open Toolkit team maintains the right to remove any project from the database or terminate the whole database.g. If your screenshots display 3d graphics. click on Create content -> Project and complete the required information: 1. Upload screenshots for your project. you should choose 0. In the "full description" field. In the "full project name" field.x. For example. describe what your project is. you must select the relevant categories. 4.9. type a compact name for your project. click "Contributed" and select all relevant categories. 2. . if your project uses relies on OpenTK 0. To create a project. 3. This will be used in the URL of the project page and the issue tracker (e. o Please do not use the "Core" category.5. Do not use spaces or any other special characters. its source code repository and license! Creating a project release Once you have created a project. selecting properties and then "assembly information". Choose which OpenTK version your project targets. Click next to proceed to the actual release page: 1. The Open Toolkit library). you can create a project release by clicking on Create content -> Project release. Creating a project Only registered users are allowed to create projects. This step is very important.

Likewise for SharpDevelop and MonoDevelop). You can optionally add an exta identifier to convey more information (typical identifiers include "beta", "rc", "final" and "wip"). 3. Fill in the "body" textbox with your release notes. 4. Optionally, you can upload your release to using the file field. Please consult with us before uploading releases bigger than 20MB! If your release is very large, consider using a torrent for distribution. You can also redirect the downloads to an external resource (e.g. your own homepage or sourceforge), by using a html redirect. Copy the following code to a file named [project name]-[release number].html (e.g. opentk-0.9.5.html), edit the necessary links and upload it through the "file" field:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Redirecting to download.</title> <meta http-equiv="REFRESH" content="0;url=" /> </head> <body>Redirecting to the <a href="">download page</a>.</body> </html>

Appendix 4: Links
The following pages contain links selated to game development, graphics and audio programming in general.

Models and Textures
File Formats don't matter. Please do not add commercial 3D model sites, unless they offer a huge collection of free models aswell. Textures Models Sites that have both Tools Generates Explosion Sprite sheets Procedural Materials Reference Images, Concept Art and the likes Disclaimer: Some links lead directly to download sections of websites in order to be convenient for the reader. The Copyright and license details vary between the websites, if you follow one of the links above it is your own responsibility to read and acknowledge the websites terms of use. The authors of this link collection take no liability for any misuse or copyright violation by the readers.

OpenGL Books and Tutorials
OpenGL related Books The 'red book' (start here when in doubt) OpenGL Programming Guide for Mac OS X Programming with OpenGL: Advanced Rendering GPU Gems 1

GPU Gems 2 GPU Gems 3 OpenGL related Tutorials Transforms & Drawing Step by Step, from a Triangle to more complex scenes Lots of Extension-related Examples Misc. advanced Tutorials GLSL related Tutorials Compilation, Linking & State Material & Lighting Articles, Demos & Research Papers Old Website, Forum is a goldmine Misc. advanced Demos /w Source Voxel & Raytracing

Programming links
    

MSDN Library, and especially its .Net subsection is an essential programming resource. Rico Mariani's Performance Tidbits provide invaluable information on .Net optimization techniques. Gamedev contains game news and articles on game development. OpenGL SDK is an one stop resource for OpenGL related questions. OpenGL registry contains specifications for all OpenGL functions. For advanced developers.

Tools & Utilities
   

Blender is an excellent 3d modeller and editor. NShader adds GLSL syntax highlighting to Visual Studio 2008. glView is an excellent utility that shows the extensions supported on your platform. Available for Windows and Mac OS platforms. GLIntercept is a free and open-source OpenGL function call interceptor. Windows only.

Written as annotated code) Clockwork Coders (GLSL) Code Colony (Camera. NBidi is a . AgateLib is a cross-platform 2d OpenGL library with an OpenTK driver.Net/Mono bindings to libraries like OpenAL. shadows.    The Tao Framework is a collection of . DevIL. Golem3D is a cross-platform model editor that uses OpenTK. ODE and more. math and more) NeHe OpenGL tutorials (wide range of topics.Net Implementation of the BIDI algorithm for complex Hebrew and Arabic RTL scripts. from simple to advanced. Tutorials External tutorials     Lighthouse3D (High quality tutorials on GLSL. Vertex Arrays) Appendix 5: Translations The Open Toolkit manual is available in the following languages:    Deutsch Ελληνικά Russian .

Sign up to vote on this title
UsefulNot useful