flopoloco's picture

OpenGL3 Mini Framework

Here's an example of a project I have going on. My purpose is to create a very simple and generic 3D framework for my projects. Currently it's far from perfect, and it's design goals are very simple, I want to work futher into in after the summer and create a basic 3D game engine.

I want to give you an idea of what is going on, see if it helps you and use it as you like, learn OpenGL experiment and do your own stuff freely.

How to use it?

Create C# console project and include these assemblies.
OpenTK, System, System.Drawing

Program.cs (use this to initiate the application)

using System;
 
namespace MiniFramework
{
	class Program
	{
		public static void Main(string[] args)
		{
			var t = new TexturedCube();
			t.Run();
		}
	}
}

TextureCube.cs (this is the actual example)

using System;
using System.Drawing;
using Imaging = System.Drawing.Imaging;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using OpenTK.Input;
 
namespace MiniFramework
{
	public class TexturedCube : GameWindow
	{
		VertexPosTex[] vertices;
 
		int handleVBO, handleVAO, handleEBO, handleShader;
		int shaderlocPosition, shaderlocTexture,
			shaderlocLightColor, shaderlocLightAmbient,
			shaderlocModelMatrix, shaderlocProjMatrix;
 
		Camera camera;
		Texture texture;
 
		float colorRed, colorGreen, colorBlue, colorAmbient;
 
		protected override void OnLoad(EventArgs e)
		{
			// Init OpenGL
			GL.ClearColor(Color.CornflowerBlue);
			GL.Enable(EnableCap.DepthTest);
			GL.FrontFace(FrontFaceDirection.Cw);
 
			// Create a camera
			camera = new Camera();
			camera.Move(0f, 0f, -10f);
 
			// Load the texture
			texture = new Texture();
			texture.Load("../carbon.jpg");
 
			// Generate vertices from data
			vertices = VertexPosTex.GenerateFromData(Data.Cube.Position,
			                                         Data.Cube.Texture);
 
			// Create buffers
			handleVBO = BufferLoader.CreateStruct(vertices);
			handleEBO = BufferLoader.CreateIndex(Data.Cube.Index);
 
			// Create shader program
			handleShader = ShaderLoader.CreateProgram(Data.Shader.VertTexture2D,
			                                          Data.Shader.FragTexture2DLightDirectional);
			GL.UseProgram(handleShader);
 
			// Get shader locations
			shaderlocPosition = GL.GetAttribLocation(handleShader, "Position");
			shaderlocTexture = GL.GetAttribLocation(handleShader, "TexCoord");
			shaderlocLightColor = GL.GetUniformLocation(handleShader, "LightColor");
			shaderlocLightAmbient = GL.GetUniformLocation(handleShader, "LightAmbient");
			shaderlocModelMatrix = GL.GetUniformLocation(handleShader, "ModelMatrix");
			shaderlocProjMatrix = GL.GetUniformLocation(handleShader, "ProjectionMatrix");
 
			#region Create vertex array
			GL.BindBuffer(BufferTarget.ArrayBuffer, handleVBO);
 
			GL.GenVertexArrays(1, out handleVAO);
			GL.BindVertexArray(handleVAO);
 
			GL.VertexAttribPointer(shaderlocPosition, 3, VertexAttribPointerType.Float,
			                       false, VertexPosTex.Size, 0);
			DebugGL.CheckGL();
 
			GL.VertexAttribPointer(shaderlocTexture, 2, VertexAttribPointerType.Float,
			                       false, VertexPosTex.Size, Vector3.SizeInBytes);
			DebugGL.CheckGL();
 
			GL.BindVertexArray(0);
			GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
			#endregion
 
			// Set light values
			colorAmbient = 0.5f;
			colorRed = 1.0f;
			colorGreen = 1.0f;
			colorBlue = 0.25f;
		}
 
		protected override void OnUpdateFrame(FrameEventArgs e)
		{
			float speed = 10f * (float)e.Time;
 
			if (Keyboard[Key.W])
				camera.Move(0f, 0f, speed);
 
			if (Keyboard[Key.S])
				camera.Move(0f, 0f, -speed);
 
			if (Keyboard[Key.A])
				camera.Move(-speed, 0f, 0f);
 
			if (Keyboard[Key.D])
				camera.Move(speed, 0f, 0f);
 
			if (Keyboard[Key.Space])
				camera.Move(0f, speed * 0.5f, 0f);
 
			if (Keyboard[Key.ControlLeft])
				camera.Move(0f, -speed * 0.5f, 0f);
 
			if (Keyboard[Key.Up])
				camera.Turn(-speed * 0.5f, 0f);
 
			if (Keyboard[Key.Down])
				camera.Turn( speed * 0.5f, 0f);
 
			if (Keyboard[Key.Left])
				camera.Turn(0f, -speed * 0.5f);
 
			if (Keyboard[Key.Right])
				camera.Turn(0f, speed * 0.5f);
		}
 
		protected override void OnRenderFrame(FrameEventArgs e)
		{
			GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
			camera.Update();
 
			#region Update uniforms
 
			GL.UniformMatrix4(shaderlocModelMatrix, false, ref camera.ViewMatrix);
			GL.UniformMatrix4(shaderlocProjMatrix, false, ref camera.ProjectionMatrix);
 
			GL.Uniform3(shaderlocLightColor, colorRed, colorGreen, colorBlue);
			GL.Uniform1(shaderlocLightAmbient, colorAmbient);
 
			#endregion
 
			BufferViewer.ViewVertexPosTex(handleVBO, handleVAO, handleEBO,
			                              shaderlocPosition, shaderlocTexture,
			                              texture, Data.Cube.Index.Length);
 
			SwapBuffers();
		}
 
		protected override void OnResize(EventArgs e)
		{
			camera.OnResize(Width, Height);
		}
	}
}

Common.cs (this is the file that contains most re usable code)

using System;
using System.Drawing;
using Imaging = System.Drawing.Imaging;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
 
namespace MiniFramework
{
	public class Texture : IDisposable
	{
		private int textureObject = 0;
		public int TextureObject { get { return textureObject; } }
 
		public Texture()
		{
		}
 
		public void Dispose()
		{
			if (textureObject != 0)
			{
				GL.DeleteTexture(textureObject);
			}
		}
 
		public void Load(string filename)
		{
			Bitmap bitmap = new Bitmap(filename);
			Imaging.BitmapData data = bitmap.LockBits(
				new Rectangle(0, 0, bitmap.Width, bitmap.Height),
				Imaging.ImageLockMode.ReadOnly, Imaging.PixelFormat.Format32bppArgb);
 
			textureObject = GL.GenTexture();
			GL.BindTexture(TextureTarget.Texture2D, textureObject);
			GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba,
			              bitmap.Width, bitmap.Height, 0, PixelFormat.Bgra,
			              PixelType.UnsignedByte, data.Scan0);
 
			GL.TexParameter(TextureTarget.Texture2D, 
			                TextureParameterName.TextureMinFilter,
			                (int)TextureMinFilter.Linear);
 
			GL.TexParameter(TextureTarget.Texture2D, 
			                TextureParameterName.TextureMagFilter,
			                (int)TextureMagFilter.Linear);
 
 
			bitmap.UnlockBits(data);
		}
 
		public void Bind()
		{
			GL.ActiveTexture(TextureUnit.Texture0);
			GL.BindTexture(TextureTarget.Texture2D, textureObject);
		}
 
		public void Unbind()
		{
			GL.BindTexture(TextureTarget.Texture2D, 0);
		}
	}
 
	public class Camera
	{
		private Vector3 position;
		private Vector3 target;
		private Vector3 up;
		public Matrix4 ViewMatrix;
		public Matrix4 ProjectionMatrix;
 
		private float yaw, pitch;
 
		public Camera()
		{
			target = -Vector3.UnitZ;
			up = Vector3.UnitY;
			ViewMatrix = Matrix4.Identity;
		}
 
		public void Move(float x, float y, float z)
		{
			position.X += x;
			position.Y += y;
			position.Z -= z;
		}
 
		public void Turn(float x, float y)
		{
			pitch += y;
			yaw += y;
 
			//ViewMatrix *= Matrix4.CreateFromAxisAngle(Vector3.UnitY, yaw);
//				Yaw += y;
//				
//				rotationMatrix = Matrix4.CreateRotationY(Yaw);
//				rightDirection = Vector3.Transform(rightDirection, rotationMatrix);
//				lookDirection = Vector3.Transform(lookDirection, rotationMatrix);
		}
 
		public void Update()
		{
			GL.MatrixMode(MatrixMode.Modelview);
			ViewMatrix = Matrix4.LookAt(position, target, up);
			GL.LoadMatrix(ref ViewMatrix);
		}
 
		public void OnResize(int width, int height)
		{
			GL.Viewport(0, 0, width, width);
			ProjectionMatrix = Matrix4.CreatePerspectiveFieldOfView(
				(float)Math.PI / 4, (float)height/width, 1f, 100f);
 
//				ProjectionMatrix = Matrix4.CreatePerspectiveFieldOfView(
//					(float)Math.PI / 4, width / (float)height, 1f, 100f);
			GL.MatrixMode(MatrixMode.Projection);
			GL.LoadMatrix(ref ProjectionMatrix);
		}
	}
 
	public struct VertexPosTex
	{
		public Vector3 Position;
		public Vector2 Texture;
 
		public VertexPosTex(Vector3 position, Vector2 texture)
		{
			Position = position;
			Texture = texture;
		}
 
		public static readonly int Size = Vector3.SizeInBytes + Vector2.SizeInBytes;
 
		public static VertexPosTex[] GenerateFromData(float[] position, float[] texture)
		{
			VertexPosTex[] verts = new VertexPosTex[position.Length / 3];
 
			for (int i = 0; i < verts.Length; i++)
			{
				int j;
 
				j = (i + 1) * 3;
				verts[i].Position = new Vector3(position[j - 3],
				                                position[j - 2],
				                                position[j - 1]);
 
				j = (i + 1) * 2;
				verts[i].Texture = new Vector2(texture[j-2],
				                               texture[j-1]);
			}
 
			return verts;
		}
	}
 
	public struct VertexPosTexNormal
	{
		public Vector3 Position;
		public Vector2 Texture;
		public Vector3 Normal;
 
		public VertexPosTexNormal(Vector3 position, Vector2 texture)
		{
			Position = position;
			Texture = texture;
			Normal = Vector3.Zero;
		}
 
		public static readonly int Size = (Vector3.SizeInBytes * 2) + Vector2.SizeInBytes;
 
		public static VertexPosTexNormal[] GenerateFromData(float[] position, float[] texture)
		{
			VertexPosTexNormal[] verts = new VertexPosTexNormal[position.Length / 3];
 
			for (int i = 0; i < verts.Length; i++)
			{
				int j;
 
				j = (i + 1) * 3;
				verts[i].Position = new Vector3(position[j - 3],
				                                position[j - 2],
				                                position[j - 1]);
 
				j = (i + 1) * 2;
				verts[i].Texture = new Vector2(texture[j-2],
				                               texture[j-1]);
			}
 
			return verts;
		}
	}
 
	public static class DebugGL
	{
		public static void CheckGL()
		{
			ErrorCode code = GL.GetError();
 
			if (code != ErrorCode.NoError)
			{
				throw new ApplicationException(code.ToString());
			}
		}
 
		public static void CheckGLSL(ref int shader)
		{
			int result;
			GL.GetShader(shader, ShaderParameter.CompileStatus, out result);
 
			if (result == 0)
			{
				string info;
				GL.GetShaderInfoLog(shader, out info);
				throw new ApplicationException(info);
			}
		}
	}
 
	public static class Data
	{
		public static class Cube
		{
			//TODO: Make it readonly? Test...
			public static float[] Position = {
				// Front face
				-1f, -1f, 1f,	// 1*3=3		3-3=0		3-2=1		3-1=2
				1f, -1f, 1f, 	// 2*3=6		6-3=3		6-2=4		6-1=5
				1f, 1f, 1f, 	// 3*3=9
				-1f, 1f, 1f,	// 4*3=12
				// Right face
				1f, -1f, 1f, 
				1f, -1f, -1f, 
				1f, 1f, -1f, 
				1f, 1f, 1f,
				// Back face
				1f, -1f, -1f, 
				-1f, -1f, -1f, 
				-1f, 1f, -1f, 
				1f, 1f, -1f,
				// Left face
				-1f, -1f, -1f, 
				-1f, -1f, 1f, 
				-1f, 1f, 1f, 
				-1f, 1f, -1f,
				// Top Face	
				-1f, 1f, 1f, 
				1f, 1f, 1f,
				1f, 1f, -1f, 
				-1f, 1f, -1f,
				// Bottom Face
				1f, -1f, 1f, 
				-1f, -1f, 1f,
				-1f, -1f, -1f, 
				1f, -1f, -1f
			};
 
		public static float[] Texture = {
			// Font Face
			0f, 1f, // 1*2 2		2-2=0		2-1=1
			1f, 1f, // 2*2 4		4-2=2		4-1=3
			1f, 0f, // 3*2 6		6-2=4		6-1=5
			0f, 0f, // 4*2 8		8-2=6		8-1=7
			// Right Face
			0f, 1f,
			1f, 1f,
			1f, 0f,
			0f, 0f,
			// Back Face
			0f, 1f,
			1f, 1f,
			1f, 0f,
			0f, 0f,
			// Left Face
			0f, 1f,
			1f, 1f,
			1f, 0f,
			0f, 0f,
			// Top Face	
			0f, 1f,
			1f, 1f,
			1f, 0f,
			0f, 0f,
			// Bottom Face
			0f, 1f,
			1f, 1f,
			1f, 0f,
			0f, 0f
		};
 
		public static uint[] Index = {
			// Font face
			0, 1, 2, 2, 3, 0, 
			// Right face
			7, 6, 5, 5, 4, 7, 
			// Back face
			11, 10, 9, 9, 8, 11,
			// Left face
			15, 14, 13, 13, 12, 15, 
			// Top Face	
			19, 18, 17, 17, 16, 19,
			// Bottom Face
			23, 22, 21, 21, 20, 23,
		};
		}
 
		public static class Shader
		{
 
 
			public static string VertTexture2D = @"
#version 330
 
in vec3 Position;
in vec2 TexCoord;
 
uniform mat4 ModelMatrix;
uniform mat4 ProjectionMatrix;
 
out vec2 TexCoord0;
 
void main()
{
	gl_Position = ProjectionMatrix * ModelMatrix * vec4(Position, 1.0);
	TexCoord0 = TexCoord;
}
";
 
 
		public static string VertTexture2DDiffuse = @"
#version 330
 
in vec3 Position;
in vec2 TexCoord;
in vec3 Normal;
 
uniform mat4 ModelMatrix;
uniform mat4 ProjectionMatrix;
 
out vec2 TexCoord0;
out vec3 Normal0;
 
void main()
{
	gl_Position = ProjectionMatrix * ModelMatrix * vec4(Position, 1.0);
	TexCoord0 = TexCoord;
	Normal0 = (ModelMatrix * vec4(Normal, 0.0)).xyz;
}
";
 
 
		public static string FragTexture2D = @"
#version 330
 
in vec2 TexCoord0;
uniform sampler2D Sampler;
 
void main()
{
	gl_FragColor = texture2D(Sampler, TexCoord0);
}
";
 
 
		public static string FragTexture2DLightDirectional = @"
#version 330
 
in vec2 TexCoord0;
 
uniform sampler2D Sampler;
uniform vec3 LightColor;
uniform float LightAmbient;
 
void main()
{
	gl_FragColor = texture2D(Sampler, TexCoord0) *
					vec4(LightColor, 1.0f) *
					LightAmbient;
}
";
 
 
		public static string FragTexture2DDiffuse = @"
#version 330
 
in vec2 TexCoord0;
in vec3 Normal0;
 
uniform sampler2D Sampler;
uniform vec3 LightColor;
uniform float LightAmbient;
uniform float LightDiffuse;
uniform vec3 LightDirection;
 
void main()
{
	vec4 ambientColor = vec4(LightColor, 1.0f) * LightAmbient;
	float diffuseFactor = dot(normalize(Normal0), -LightDirection);
	vec4 diffuseColor;
 
	if (diffuseFactor > 0) {
		diffuseColor = vec4(LightColor, 1.0f) * LightDiffuse *
						diffuseFactor;
	}
	else {
		diffuseColor = vec4(0, 0, 0, 0);
	}
 
	gl_FragColor = texture2D(Sampler, TexCoord0) * (ambientColor + diffuseColor);
}
";
		}
	}
 
	public static class ShaderLoader
	{
		public static int CreateProgram(string vertexSource, string fragmentSource)
		{
			int vert = GL.CreateShader(ShaderType.VertexShader);
			GL.ShaderSource(vert, vertexSource);
			GL.CompileShader(vert);
			DebugGL.CheckGLSL(ref vert);
 
			int frag = GL.CreateShader(ShaderType.FragmentShader);
			GL.ShaderSource(frag, fragmentSource);
			GL.CompileShader(frag);
			DebugGL.CheckGLSL(ref frag);
 
			int program = GL.CreateProgram();
			GL.AttachShader(program, vert);
			GL.AttachShader(program, frag);
			GL.LinkProgram(program);
			DebugGL.CheckGL();
 
			return program;
		}
	}
 
	public static class BufferLoader
	{
		public static int CreateStruct(VertexPosTex[] vertices)
		{
			int buffer = 0;
 
			GL.GenBuffers(1, out buffer);
			GL.BindBuffer(BufferTarget.ArrayBuffer, buffer);
			GL.BufferData(BufferTarget.ArrayBuffer,
			              new IntPtr(vertices.Length * VertexPosTex.Size),
			              vertices, BufferUsageHint.StaticDraw);
			DebugGL.CheckGL();
 
			GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
 
			return buffer;
		}
 
		public static int CreateStruct(VertexPosTexNormal[] vertices)
		{
			int buffer = 0;
 
			GL.GenBuffers(1, out buffer);
			GL.BindBuffer(BufferTarget.ArrayBuffer, buffer);
			GL.BufferData(BufferTarget.ArrayBuffer,
			              new IntPtr(vertices.Length * VertexPosTexNormal.Size),
			              vertices, BufferUsageHint.StaticDraw);
			DebugGL.CheckGL();
 
			GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
 
			return buffer;
		}
 
		public static int CreateIndex(uint[] indices)
		{
			int buffer = 0;
 
			GL.GenBuffers(1, out buffer);
			GL.BindBuffer(BufferTarget.ElementArrayBuffer, buffer);
			GL.BufferData(BufferTarget.ElementArrayBuffer,
			              new IntPtr(indices.Length * sizeof(uint)),
			              indices, BufferUsageHint.StaticDraw);
 
			DebugGL.CheckGL();
 
			GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
 
			return buffer;
		}
	}
 
	public static class BufferViewer
	{
		public static void ViewVertexPosTex(int vbo, int vao, int ebo,
		                                    int shaderLocPos, int shaderLocTex,
		                                    Texture texture, int indexLength)
		{
			GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
			GL.BindVertexArray(vao);
			GL.EnableVertexAttribArray(shaderLocPos);
			GL.EnableVertexAttribArray(shaderLocTex);
 
			texture.Bind();
 
			GL.BindBuffer(BufferTarget.ElementArrayBuffer, ebo);
			GL.DrawElements(BeginMode.Triangles, indexLength, DrawElementsType.UnsignedInt, 0);
			GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
 
			GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
			GL.BindVertexArray(0);
			GL.DisableVertexAttribArray(shaderLocPos);
			GL.DisableVertexAttribArray(shaderLocTex);
 
			texture.Unbind();
		}
 
		public static void ViewVertexPosTexNormal(int vbo, int vao, int ebo,
		                                    int shaderLocPos, int shaderLocTex, int shaderLocNorm,
		                                    Texture texture, int indexLength)
		{
			GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
			GL.BindVertexArray(vao);
			GL.EnableVertexAttribArray(shaderLocPos);
			GL.EnableVertexAttribArray(shaderLocTex);
			GL.EnableVertexAttribArray(shaderLocNorm);
 
			texture.Bind();
 
			GL.BindBuffer(BufferTarget.ElementArrayBuffer, ebo);
			GL.DrawElements(BeginMode.Triangles, indexLength, DrawElementsType.UnsignedInt, 0);
			GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
 
			GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
			GL.BindVertexArray(0);
			GL.DisableVertexAttribArray(shaderLocPos);
			GL.DisableVertexAttribArray(shaderLocTex);
 
			texture.Unbind();
		}
	}
 
	public static class NormalsCalculator
	{
		public static void Calculate(ref VertexPosTexNormal[] vertices, uint[] indices)
		{
			for (int i = 0; i < indices.Length; i += 3)
			{
				uint index0 = indices[i];
				uint index1 = indices[i + 1];
				uint index2 = indices[i + 2];
 
				Vector3 v1 = vertices[index1].Position - vertices[index0].Position;
				Vector3 v2 = vertices[index2].Position - vertices[index0].Position;
				Vector3 n = Vector3.Cross(v1, v2);
				n.Normalize();
 
				vertices[index0].Normal.Add(n);
				vertices[index1].Normal.Add(n);
				vertices[index2].Normal.Add(n);
			}
 
			for (int i = 0; i < vertices.Length; i++)
			{
				vertices[i].Normal.Normalize();
			}
		}
	}
}

Known problems:
[] 3D Camera: It sucks :)

Ideas for the improvement:
[] Uniform/Attribute management: Very hard coded and exposed, normally I would like it to be more declarative (key-value map from an xml file, all contained in a Hashmap list)
[] More dynamic vertex structures: I want to make vertex structures on-the-fly (like ordering a custom pizza) rather than sticking with monolithic vertex declarations. Instead of creating 20 vertex declaration, I will avoid repeated code and use dynamic lists instead to manage byte sizes, handle pointers and relevant.
[] OpenGL Vertex Buffer Objects: A better way of handling buffer objects (goes with the feature above).

P.S. (The codebase is based on tutorials found in http://ogldev.atspace.co.uk/index.html), but modified to work with OpenTK. So if you find anything you don't understand read there so you can get an idea.

Images

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
lid6j86's picture

any way you could isolate the texture specific part for me? this looks like great code, but I'd rather isolate pieces of your framework so that I can look at one concept at a time. Are you putting each concept into its own class/file?

flopoloco's picture

This example is very complex, because there is: Texturing, buffer objects, vertex structs, shaders, vertex array objects, uniforms and attributes. It's indeed very scary!

Regarding the Texture part:

In order to gain a better understanding of this you have to study the good old Immediate Mode example code so you can focus only on texturing part, note that OpenGL 1.0 is seriously deprecated now, but it's great for studying because it's very simple.
Go to this example found in the OpenTK directory in you disk: ...\opentk\Source\Examples\OpenGL\1.x\Textures.cs

Generally things go like this.

0. When you need to work with textures you have to turn on this OpenGL capability manually
GL.Enable(EnableCap.Texture2D);

1. You load a texture (how to send an image file, from the filesystem to the OpenGL memory).

GL.GenTextures(1, out texture);
            GL.BindTexture(TextureTarget.Texture2D, texture);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
 
            BitmapData data = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height),
                ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
 
            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0,
                OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
 
            bitmap.UnlockBits(data);

Now you have a texture loaded in OpenGL memory, in you application you will have access to it with the help of the "texture" variable, which is an integer handle (a handle that matches a region in memory).

2. In this specific example you can use the texture to apply it to surfaces (a quad shape). In order to do this you provide the appropriate texture coordinates that match perfectly each vertex.

 GL.BindTexture(TextureTarget.Texture2D, texture); // You need to make sure that you are actually using this texture
 
GL.Begin(BeginMode.Quads);
 
            GL.TexCoord2(0.0f, 1.0f); GL.Vertex2(-0.6f, -0.4f);
            GL.TexCoord2(1.0f, 1.0f); GL.Vertex2(0.6f, -0.4f);
            GL.TexCoord2(1.0f, 0.0f); GL.Vertex2(0.6f, 0.4f);
            GL.TexCoord2(0.0f, 0.0f); GL.Vertex2(-0.6f, 0.4f);
 
            GL.End();

That's all!

If you go to the Mini Framework example I made, at the Texture class, the Load method you see there has a sequence of commands that are very standard to load textures, you noticed almost the same code at the OpenTK texture example. The real difference is that instead of using GL.TexCoord2 or GL.Vertex commands, you use buffer objects to set all the raw data, and then explain to OpenGL how they can be used in order to render.

Cheers. For any other questions or comments I am here to respond.

P.S. If you need to dive into buffer objects then start from here
http://www.opentk.com/node/2291
Also have a look in the search field or Google to make sure if there are better examples available.

lid6j86's picture

thanks for breaking it down, it really helped. I'm also working on doing a blog both for myself to kind of better understand (I've found trying to explain something helps cement the knowledge), and maybe for any new folk as well so they can come along the journey and maybe avoid some stumbling blocks i had