Collection of usefull classes to create OpenGL projects in FreePascal

bergmann ad21b40fda * fixed some compiler warnings in linux 1 year ago
examples 7c766d93b9 * refactored share context to be able to support function for gtk2/glx 3 years ago
.gitignore ebe5b36684 * added gitignore 3 years ago
README.md 35b02c3657 Proofread README.md 1 year ago
bitSpaceOpenGL.lpk ecd1cfe2ec * created package 2 years ago
bitSpaceOpenGL.pas ecd1cfe2ec * created package 2 years ago
dglOpenGL-LICENSE 445883943c Merge remote-tracking branch 'dglOpenGL/master' 1 year ago
dglOpenGL.pas ad21b40fda * fixed some compiler warnings in linux 1 year ago
dglOpenGLES.pas 309069cd27 * changed error to warning for unknown build system 3 years ago
glBitmapConf.inc f6ca47eeb2 * updated glBitmap 4 years ago
uglcArrayBuffer.pas ff16b4dd09 * ArrayBuffer: fixed some small bugs for OpenGL ES 4 years ago
uglcBitmap.pas 0526459942 * [glBitmap] Always generate mipmaps using correct method for GL Version 2 years ago
uglcCamera.pas 88f4552e82 * added invert camera position 3 years ago
uglcContext.pas 7c766d93b9 * refactored share context to be able to support function for gtk2/glx 3 years ago
uglcContextGtk2GLX.pas 2d9ca0694c * [glcContext] implemented share context for gtk2/glx 3 years ago
uglcContextGtkCustomVisual.pas 07d4b69c6a * added glcContext 4 years ago
uglcContextWGL.pas 7c766d93b9 * refactored share context to be able to support function for gtk2/glx 3 years ago
uglcFrameBufferObject.pas 861226e1af * [glcContext] check return value from wglShareLists 3 years ago
uglcLight.pas 38723f4532 * added OpenGL ES support 4 years ago
uglcShader.pas e47495aacf * [glcShader] disabled log message fpr uniforms 3 years ago
uglcTypes.pas 38723f4532 * added OpenGL ES support 4 years ago
uglcVertexArrayObject.pas f6ca47eeb2 * updated glBitmap 4 years ago
ugluMatrix.pas 4e1207880c * Initial Commit 4 years ago
ugluMatrixEx.inc dce2082126 * [ugluMatrixEx] implemented Create(Vector) 2 years ago
ugluMatrixEx.pas 4eb98c1c03 * [ugluVectorEx] implemented SqrLength and Cross 2 years ago
ugluMatrixExHelper.pas dce2082126 * [ugluMatrixEx] implemented Create(Vector) 2 years ago
ugluQuaternion.pas 92d8121bd9 * fixed typo 4 years ago
ugluVector.pas 88f4552e82 * added invert camera position 3 years ago
ugluVectorEx.inc dce2082126 * [ugluMatrixEx] implemented Create(Vector) 2 years ago
ugluVectorEx.pas 00fe5b8c23 * added color convert functions for vector 2 years ago
ugluVectorExHelper.pas dce2082126 * [ugluMatrixEx] implemented Create(Vector) 2 years ago

README.md

Overview

OpenGL is a useful collection of classes for creating OpenGL projects in Free Pascal. It wraps the most common OpenGL objects (such as RenderContext, Textures, VertexArray, FrameBuffer, ...) into simple classes. It also comes with some data types all around graphic programming.

  1. dglOpenGL and dglOpenGLES
  2. The render context
  3. OpenGL object classes
    1. TglcArrayBuffer
    2. TglcBitmap
    3. TglcFrameBufferObject
    4. TglcShader
    5. TglcVertexArrayObject
  4. Helper classes
    1. TglcCamera
    2. TglcLight and TglcMaterial
  5. Helper types

1. dglOpenGL and dglOpenGLES

The files dglOpenGL.pas and dglOpenGLES.pas contain all necessary constants, types and functions to make calls to the OpenGL or OpenGLES library. Also it takes care of dynamically loading the required function pointers from the library. If you want to write pure OpenGL code, these two files are all you need. The rest of this project is just to make your life easier.

dglOpenGL.pas and gdlOpenGLES.pas are maintained by the Delphi OpenGL Community.

2. The render context

The render context class is used to create an OpenGL render context. It is the first step you have to do before you continue writing your OpenGL App.

First we need to know wich context class we have to create, because every operating system (e.g. Windows, Linux) and every UI toolkit (e.g. pure X11, Gtk2) has its own way to create a render context. Luckily this project has a simple way to do this.

var ContextClass: TglcContextClass;
ContextClass := TglcContext.GetPlatformClass();

The second thing we need is a suitable pixel format descriptor that will change the pixel format of our device context to our needs. This is as simple as the last step.

var cpfs: TglcContextPixelFormatSettings;
cpfs := ContextClass.MakePF();

Now we can create our context object. This will not create the render context! The context object is a simple object to manage the render context. The render context will be created later.

var Context: TglcContext;
Context := ContextClass.Create(aWinControl, cpfs);

Now everything is ready to use. We can create our render context.

!Note: This step can be done in a separate thread, if yout want to do some offscreen rendering. But be careful: the render context can only be used in the thread were it was created.

Context.BuildContext();

Congratulations, you have created a valid render context and can start normal rendering now. After you are finished you should cleanup everything. Never mind if you forget to do this, the TglcContext will care about that for you.

Context.CloseContext();

Last step: you have to free the context object.

FreeAndNil(Context);

3. OpenGL object classes

i. TglcArrayBuffer

Array buffers, also known as vertex buffer objects, are used to store vertex data in video memory, so you can render your geometry very fast. Before you upload your data to the video memory you need to define what data you want to upload. We will define a vertex that has a three dimensional position, a two dimensional texture coordinate and a three dimensional normal vector.

type
TVertex = packed record
  pos: TgluVector3f; // vertex position
  tex: TgluVector2f; // texture coordinates
  nor: TgluVector3f; // normal vector
end;
PVertex = ^TVertex;

Once you have defined your data, you can create an array buffer and upload your data to the video memory. In this example we use indexed vertex rendering. Each vertex is only stored once in our vertex array and then we will define an index array that describes which vertices have to be rendered.

varv
  VertexBuffer: TglcArrayBuffer;
  IndexBuffer: TglcArrayBuffer;
  p: Pointer;

{ create our buffer objects }
VertexBuffer := TglcArrayBuffer.Create(TglcBufferTarget.btArrayBuffer);
IndexBuffer  := TglcArrayBuffer.Create(TglcBufferTarget.btElementArrayBuffer);

{ write vertex data to vertex buffer }
vBuffer.Bind;

// allocate 4 * sizeof(TVertex) bytes of video memory and set its usage to StaticDraw
vBuffer.BufferData(4, SizeOf(TVertex), TglcBufferUsage.buStaticDraw, nil);

// memory map video memory to our application
p := vBuffer.MapBuffer(TglcBufferAccess.baWriteOnly);

// write our data to the mapped video memory
try
  PVertex(p).pos := gluVertex3f(0.0, 0.0, 0.0);
  PVertex(p).tex := gluVertex2f(0.0, 0.5);
  PVertex(p).nor := gluVertex3f(0.0, 1.0, 0.0);
  inc(p, SizeOf(TVertex));

  { ... more vertices following }

finally
  vBuffer.UnmapBuffer;
  vBuffer.Unbind;
end;

{ write indices to index buffer }
iBuffer.Bind;
iBuffer.BufferData(4, SizeOf(GLuint), TglcBufferUsage.buStaticDraw, nil);
p := iBuffer.MapBuffer(TglcBufferAccess.baWriteOnly);
try
  PGLuint(p) := 0;
  inc(p, sizeof(GLuint));

  { ... more indices following }

finally
  iBuffer.UnmapBuffer;
  iBuffer.Unbind;
end;

All data is uploaded. Now we can render our vertices using one of the glDraw methods. Before you call glDraw you must tell OpenGL where the vertex data is stored.

// use array buffers to draw primitive
VertexBuffer.Bind;
IndexBuffer.Bind;
try
  // tell OpenGL where to find the vertex position
  // 3 float values, starting at offset 0
  glEnableClientState(GL_VERTEX_ARRAY);
  glVertexPointer(3, GL_FLOAT, SizeOf(TVertex), Pointer(0));

  // tell OpenGL where to find the texture coordinates
  // 2 float values, starting at offset 12 (= 3 * sizeof(GLfloat))
  glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  glTexCoordPointer(2, GL_FLOAT, SizeOf(TVertex), Pointer(12));

  // tell OpenGL where to find the normal vector
  // normal vector starting at offset 20 (= 5 * sozeof(GLfloat))
  glEnableClientState(GL_NORMAL_ARRAY);
  glNormalPointer(GL_FLOAT, SizeOf(TVertex), Pointer(20));

  // tell OpenGL to use our index array for indexed rendering
  glEnableClientState(GL_INDEX_ARRAY);
  glIndexPointer(GL_INT, 0, nil);

  glDrawElements(GL_QUADS, iBuffer.DataCount, GL_UNSIGNED_INT, nil);

  glDisableClientState(GL_INDEX_ARRAY);
  glDisableClientState(GL_VERTEX_ARRAY);
  glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  glDisableClientState(GL_NORMAL_ARRAY);
finally
  IndexBuffer.Unbind;
  VertexBuffer.Unbind;
end;

That's it. Don't forget to free your buffer objects when you're done.

FreeAndNil(VertexBuffer);
FreeAndNil(IndexBuffer);

ii. TglcBitmap

TglcBitmap objects are used to manage and render textures. To simply load and render a texture you need to create two objects. TglcBitmapData to load a texture from file and convert it to the format you want to use in your texture and TglcBitmap2D to manage a OpenGL texture object, upload or download texture data and of course render your texture. Sounds simple, doesn't it?

// create our texture object
var Texture: TglcBitmap2D;
Texture := TglcBitmap2D.Create;

// load texture data to texture object
var TextureData: TglcBitmapData;
TextureData := TglcBitmapData.Create;
try
  TextureData.LoadFromFile('./my-texture.png');
  // convert your TextureData if it is not supported by OpenGL
  if (TextureData.Format <> TextureData.FormatDescriptor.OpenGLFormat)  then
    TextureData.ConvertTo(TextureData.FormatDescriptor.OpenGLFormat);
  Texture.UploadData(TextureData);
finally
  FreeAndNil(TextureData);
end;

As you can see, you can destroy the TextureData object once the data is uploaded to your texture. The memory is not used any longer. Also you can do all operations to TextureData in a separate thread to have a nice load balancing and reduce render glitches.

iii. TglcFrameBufferObject

Frame buffer objects are mostly used to do some offscreen rendering. You simply create a frame buffer object, attach some color and depth buffers to it and then render some stuff. Attached buffers can be of type render buffer or texture buffer. Render buffers are just a simple buffer OpenGL stores some needed data in (like a depth buffer). A texture buffer instead can be used as a normal texture after you have rendered something to the frame buffer object. Sounds complicated, but it's easier as you imagine.

var
  fbo: TglcFrameBufferObject;
  tex: TglcTextureBuffer;
  buf: TglcRenderBuffer;

fbo := TglcFrameBufferObject.Create;
try
  // set the size of the frame buffer object
  fbo.SetSize(800, 600);

  // creating texture buffer and attach it as color buffer
  tex := TglcTextureBuffer.Create(TglcFormat.fmRGBA, TglcInternalFormat.ifRGBA16F);
  fbo.AddBuffer(tex, TglcAttachment.atColor0, true);

  // creating render buffer and attach it as depth buffer
  buf := TglcRenderBuffer.Create(TglcInternalFormat.ifDepthComponent);
  fbo.AddBuffer(buf, TglcAttachment.atDepth, true);

  // render to frame buffer object
  fbo.Bind;
    { ... do some rendering }
  fbo.Unbind;

  // use texture buffer
  tex.Bind;
    { ... do some rendering }
  tex.Unbind;
finally
  FreeAndNil(fbo);
end;

iv. TglcShader

What would an OpenGL application be without fancy shaders? TglcShader helps you to load and use shaders in your application. The simplest example of using the shader classes is to load a shader and then use it for rendering.

Shader Code:

/* ShaderObject: GL_VERTEX_SHADER */
#version 330
layout(location = 0) in vec3 inPos;
void main(void)
{
  gl_Position = vec4(inPos, 1.0);
}

/* ShaderObject: GL_FRAGMENT_SHADER */
#version 330
out vec4 outColor;
void main(void)
{
  outColor = vec4(1.0, 0.0, 0.0, 1.0);
}

Application Code:

var Shader: TglcShaderProgram;
{ 'Log' is a callback to write log outputs to so you can detect errors while compiling your shader. }
Shader := TglcShaderProgram.Create(@Log); 
Shader.LoadFromFile('./my-shader-file.glsl');
Shader.Compile;
Shader.Enable;
{ ... do some rendering }
Shader.Disable;

As mentioned before this is a really simple example. TglcShaderProgram has some more useful features, like setting uniform variables, binding attribute locations or completely build your shader step by step programmatically linking different TglcShaderObjects into the TglcShaderProgram.

v. TglcVertexArrayObject

Vertex array objects are used to store all information about vertex buffer objects and index buffer objects. Instad of telling OpenGL all these information in every render loop, they are stored in a single object and can be activated by simply binding the vertex array object.

var
  vbo: TglcArrayBuffer;
  vao: TglcVertexArrayObject;
{ ... create VBO and fill with data }
vao := TglcVertexArrayObject.Create;
bao.BindArrayBuffer(vbo, true);
vao.VertexAttribPointer(LAYOUT_LOCATION_POS, 3, GL_FLOAT, False, SizeOf(TVertex), GLint(@PVertex(nil)^.pos));
vao.VertexAttribPointer(LAYOUT_LOCATION_TEX, 2, GL_FLOAT, False, SizeOf(TVertex), GLint(@PVertex(nil)^.tex));
vao.Bind;
{ ... render vertices using glDrawXXX }
vao.Unbind;

4. Helper Classes

i. TglcCamera

TglcCamera is a quite simple class. It wraps the model view matrix into a useful class and provide some methods to manipulate the model view matrix such as Tilt, Turn or Move.

var Camera: TglcCamera;

Camera := TglcCamera.Create;
Camera.Perspective(45, 0.01, 100, 800/600);   // define perspective view
Camera.Move(gluVector(2, 3, -5));             // move 2 right, 3 up and 5 back
Camera.Tilt(-25);                             // turn 25 degrees down
Camera.Turn(-10);                             // turn 10 degrees left
Camera.Activate;                              // activate camera

{ ... do normal rendering }

ii. TglcLight and TglcMaterial

TglcLight and TglcMaterial are simple class wrappers of OpenGL light and material, just to manage them in an object oriented way. TglcLight is specialized in 3 different classes:

  • TglcLightGlobal - for global lighting
  • TglcLightPoint - for point lights
  • TglcLightSpot - for spot lights You can use the Bind and Unbind methods to activate and deactivate a light or material.

5. Helper Types

  • glcTypes - Contains a list of all OpenGL enum used in this project.
  • gluMatrix - Contains some useful methods to work with matrices. gluMatrixEx is an extended version of gluMatrix using FPC type helpers to add member methods to the matrix types.
  • gluVector - Contains some useful methods to work with vectors. gluVectorEx is an extended version of gluVector using FPC type helpers to add member methods to the vector types.
  • gluQuaternion - Contains some useful methods to deal with quaternions.