Using OpenGL

The first example below shows how to render a single triangle. The procedure glVertex3f takes three parameters of type float (real). In this example the z parameter is 0 for each vertex so we could have supplied instead only the x and y coordinates with procedure glVertex2f.

The modern way to use OpenGL is to make use of shader programs that run on the graphics card. See Max Foster's MrSnugglekins for impressive examples of shader programs. Much "traditional" code using the alternative fixed pipeline method has been effective. On this website student programs such as TesterGameEngine by Steven Binns use OpenGL within SDL. The second demonstration reuses some of Steven's code within PasSFML to render tetrahedra.

The third and fourth demonstrations show you how to render one image and multiple images, respectively, on faces of a cube.

See the following page for examples of using OpenGL for motion graphics.

Triangle Demonstration

The code follows a captured image of the output. You can replace the triangle code with OpenGL code of your own to experiment and "learn by doing". We recommend as an introduction the official OpenGL Programming Guide, which explains with the aid of helpful diagrams the underlying concepts and terminology. (As with many sources of OpenGL information, the sections of OpenGL code are within C programs rather than within Pascal. If necessary, see our tutorial C/C++ after Pascal to help you to read C code). We are extending our Graphics tutorial to include the use of OpenGL in Lazarus.

Output

Output

program OpenGL_TriangleDemo;
{$Apptype GUI}
uses
  SysUtils, GL, SfmlGraphics, SfmlSystem, SfmlWindow;
var
  OutputWindow: TSfmlRenderWindow;
  OutputImage: PsfmlImage;
begin
  // Create the main window
  OutputWindow := TSfmlRenderWindow.Create(SfmlVideoMode(100, 100),
    'SFML window with  OpenGL', [sfTitleBar, sfClose]);

  OutputWindow.SetActive(True); // Makes it the active window for OpenGL calls
  glClearColor(0, 0, 0, 1);   // Sets the color clear value
  glOrtho(-50, 50, -50, 50, -1, 1); // Sets up an orthographic projection
  glClear(GL_COLOR_BUFFER_BIT);  // Clears the background
  // Draw a triangle
  glBegin(GL_TRIANGLES);
    glColor4f(1, 0, 0, 0.5);
    glVertex3f(-50, -50, 0);

    glColor4f(0, 1, 0, 0.5);
    glVertex3f(50, -50, 0);

    glColor4f(0, 0, 1, 0.5);
    glVertex3f(0, 50, 0);
  glEnd;

  OutputImage := OutputWindow.Capture;
  SfmlImageSaveToFile(OutputImage, 'TriangleOutputImage.png');

  OutputWindow.Display;
  sfmlsleep(sfmlSeconds(5));
end.

    

Tetrahedra Demonstration

The code follows a cropped captured image showing the tetrahedra.

Tetrahedra

Tetrahedra

program OpenGL_Demo2;
{$Apptype GUI}
uses
  SysUtils, GL, SfmlGraphics, SfmlSystem, SfmlWindow;
var
  OutputWindow: TSfmlRenderWindow;
  OutputImage: PsfmlImage;

procedure GLTetrahedron(x1, z1, x2, z2, x3, z3, tx, ty, tz, basey, r, g, b: real);
begin
  glBEGIN(GL_TRIANGLES);
  //Base face
  glCOLOR3f(r, g, b);
  glVERTEX3f(x2, basey, z2);
  glVERTEX3f(x1, basey, z1);
  glVERTEX3f(x3, basey, z3);
  //1-2 face
  glVERTEX3f(tx, ty, tz);
  glVERTEX3f(x1, basey, z1);
  glVERTEX3f(x2, basey, z2);
  //2-3 face
  glVERTEX3f(tx, ty, tz);
  glVERTEX3f(x2, basey, z2);
  glVERTEX3f(x3, basey, z3);
  //3-1 face
  glVERTEX3f(tx, ty, tz);
  glVERTEX3f(x3, basey, z3);
  glVERTEX3f(x1, basey, z1);
  glEND;
end;

begin
  // Create the main window
  OutputWindow := TSfmlRenderWindow.Create(SfmlVideoMode(500, 500),
    'SFML window with  OpenGL', [sfTitleBar, sfClose]);

  // Make it the active window for OpenGL calls
  OutputWindow.SetActive(True);

  // Set the color and depth clear values
  glClearDepth(1);

  // Set up a perspective projection
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity;
  glFrustum(-1, 1, -1, 1, 1, 500);
  glClearColor(0.3, 0.8, 0.6, 0.0);
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity;
  glTranslatef(-0.5, -3, -10);

  GLTetrahedron(-5.0, -1.0, -6.0, 0.0, -5.0, 1.0, -5.5, 2.0, 0.0, 0.0, 1.0, 0.0, 0.0);
  GLTetrahedron(5.0, -1.0, 6.0, 0.0, 5.0, 1.0, 5.5, 2.0, 0.0, 0.0, 0.0, 1.0, 0.0);
  GLTetrahedron(3.0, -6.0, 4.0, -5.0, 3.0, -4.0, 3.5, 2.0, -5.0, 0.0, 0.54, 0.54, 0.54);
  GLTetrahedron(3.0, 4.0, 4.0, 5.0, 3.0, 6.0, 3.5, 2.0, 5.0, 0.0, 0.0, 0.0, 1.0);
  GLTetrahedron(9.0, -1.0, 10.0, 0.0, 9.0, 1.0, 9.5, 2.0, 0.0, 0.0, 1.0, 0.0, 0.0);

  OutputImage := OutputWindow.Capture;
  SfmlImageSaveToFile(OutputImage, 'TetrahedraOutputImage.png');

  OutputWindow.Display;
  sfmlsleep(sfmlSeconds(20));
end.

Rendering an Image on Faces of a Cube

This example is designed to be the equivalent of our Smart Pascal WebGL demonstration and it produces a similar output to the one shown on that page. This SFML page on Game Development Stack Exchange was most useful.

program OpenGL_3D_TextureDemo;
{$Apptype GUI}
uses
  SysUtils, GL, GLU, SfmlGraphics, SfmlSystem, SfmlWindow;

const
  CCube: array [0..71] of GLfloat = (
   //front face: 0, 1, 2, 0, 2, 3
   -100.0, -100.0,  100.0, //0
    100.0, -100.0,  100.0, //1
    100.0,  100.0,  100.0, //2
   -100.0, -100.0,  100.0, //0
    100.0, 100.0,  100.0,  //2
   -100.0,  100.0,  100.0, //3
   //back face: 4, 5, 6,  4, 6, 7
   -100.0, -100.0, -100.0, //4
   -100.0,  100.0, -100.0, //5
    100.0,  100.0, -100.0, //6
   -100.0, -100.0, -100.0, //4
    100.0,  100.0, -100.0, //6
    100.0, -100.0, -100.0, //7
   //right face: 16, 17, 18, 16, 18, 19
    100.0, -100.0, -100.0, //16 same as 7
    100.0,  100.0, -100.0, //17 same as 6
    100.0,  100.0,  100.0, //18 same as 2
    100.0, -100.0, -100.0, //16 same as 7
    100.0,  100.0,  100.0, //18 same as 2
    100.0, -100.0,  100.0, //19 same as 1
   //left face: 20, 21, 22, 20, 22, 23
   -100.0, -100.0, -100.0, //20 same as 4
   -100.0, -100.0,  100.0, //21 same as 0
   -100.0,  100.0,  100.0, //22 same as 3
   -100.0, -100.0, -100.0, //20 same as 4
   -100.0,  100.0,  100.0, //22 same as 3
   -100.0,  100.0, -100.0  //23 same as 5
  );

  CubeTexCoords : array [0.. 47] of GLfloat = (
    0.0, 1.0,
    1.0, 1.0,
    1.0, 0.0,
    0.0, 1.0,
    1.0, 0.0,
    0.0, 0.0, // end triangle 2 face 1
    1.0, 1.0,
    1.0, 0.0,
    0.0, 0.0,
    1.0, 1.0,
    0.0, 0.0,
    0.0, 1.0, // end triangle 2 face 2
    1.0, 1.0,
    1.0, 0.0,
    0.0, 0.0,
    1.0, 1.0,
    0.0, 0.0,
    0.0, 1.0,
    0.0, 1.0,
    1.0, 1.0,
    1.0, 0.0,
    0.0, 1.0,
    1.0, 0.0,
    0.0, 0.0
  );

var
  ContextSettings: TSfmlContextSettings;
  OutputWindow: TSfmlWindow;
  CubeFace: TSfmlImage;
  ImageSize: TSfmlVector2u;

begin
  CubeFace := TSfmlImage.Create('Bricks0.png');
  // Request a 32-bits depth buffer when creating the window
  ContextSettings.DepthBits := 32;
  // Create the main window
  OutputWindow := TSfmlWindow.Create(SfmlVideoMode(500, 500),
    'SFML window with 3D OpenGL', [sfTitleBar, sfClose], @ContextSettings);
  // Make it the active window for OpenGL calls
  OutputWindow.SetActive(True);
  // Set the color and depth clear values
  glClearDepth(1);
  glClearColor(0.0, 0.0, 0.25, 1.0); // Set clear colour to blue, fully opaque
  // Enable Z-buffer read and write
  glEnable(GL_DEPTH_TEST);
  glDepthMask(GL_TRUE);
  // Disable lighting and enable texturing
  glDisable(GL_LIGHTING);
  glEnable(GL_TEXTURE_2D);
  // Configure viewport to size of window
  glViewport(0, 0, OutputWindow.Size.X, OutputWindow.Size.Y);
  // Set up perspective projection with 45 degree field of view
  glMatrixMode(GL_PROJECTION);
  gluPerspective(45, 1, 0.1, 1000);
  // Upload the image
  ImageSize := SfmlImageGetSize(CubeFace.Handle);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ImageSize.X, ImageSize.Y, 0,
               GL_RGBA, GL_UNSIGNED_BYTE, SfmlImageGetPixelsPtr(CubeFace.Handle));
  // Set parameters to render any size image
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  // Enable position and texture coordinates vertex components
  glEnableClientState(GL_VERTEX_ARRAY);
  glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  // Link array data
  glVertexPointer(3, GL_FLOAT, 3 * sizeof(GLfloat), @CCube);
  glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), @CubeTexCoords);
  // Disable colour and normal vertex component
  glDisableClientState(GL_COLOR_ARRAY);
  glDisableClientState(GL_NORMAL_ARRAY);
  // Clear color and depth buffers
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
  // Apply some transformations to cube
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity;
  glTranslatef(0, 0, -330);
  glRotatef(0.5 * 180 / pi, 1, 0, 0);
  glRotatef(0.9 * 180 / pi, 0, 1, 0);
  glRotatef(0.2 * 180 / pi, 0, 0, 1);
  glScalef(0.7, 0.7, 0.7);
  // Draw the cube
  glDrawArrays(GL_TRIANGLES, 0, 36);
  OutputWindow.Display;
  sfmlSleep(sfmlSeconds(10));
end.    

Rendering Different Images on Faces of a Cube

This example is designed to be the equivalent of our Smart Pascal WebGL demonstration of using multiple textures and it produces a similar output to the one shown on that page.

program OpenGL_3D_TexturesDemo;
{$Apptype GUI}
uses
  SysUtils, GL, GLU, SfmlGraphics, SfmlSystem, SfmlWindow;

const
  CCube: array [0..71] of GLfloat = (
   //front face: 0, 1, 2, 0, 2, 3
   -100.0, -100.0,  100.0, //0
    100.0, -100.0,  100.0, //1
    100.0,  100.0,  100.0, //2
   -100.0, -100.0,  100.0, //0
    100.0, 100.0,  100.0,  //2
   -100.0,  100.0,  100.0, //3
   //back face: 4, 5, 6,  4, 6, 7
   -100.0, -100.0, -100.0, //4
   -100.0,  100.0, -100.0, //5
    100.0,  100.0, -100.0, //6
   -100.0, -100.0, -100.0, //4
    100.0,  100.0, -100.0, //6
    100.0, -100.0, -100.0, //7
   //right face: 16, 17, 18, 16, 18, 19
    100.0, -100.0, -100.0, //16 same as 7
    100.0,  100.0, -100.0, //17 same as 6
    100.0,  100.0,  100.0, //18 same as 2
    100.0, -100.0, -100.0, //16 same as 7
    100.0,  100.0,  100.0, //18 same as 2
    100.0, -100.0,  100.0, //19 same as 1
   //left face: 20, 21, 22, 20, 22, 23
   -100.0, -100.0, -100.0, //20 same as 4
   -100.0, -100.0,  100.0, //21 same as 0
   -100.0,  100.0,  100.0, //22 same as 3
   -100.0, -100.0, -100.0, //20 same as 4
   -100.0,  100.0,  100.0, //22 same as 3
   -100.0,  100.0, -100.0  //23 same as 5
  );

  CubeTexCoords : array [0.. 47] of GLfloat = (
    0.0, 1.0,
    1.0, 1.0,
    1.0, 0.0,
    0.0, 1.0,
    1.0, 0.0,
    0.0, 0.0, // end triangle 2 face 1
    1.0, 1.0,
    1.0, 0.0,
    0.0, 0.0,
    1.0, 1.0,
    0.0, 0.0,
    0.0, 1.0, // end triangle 2 face 2
    1.0, 1.0,
    1.0, 0.0,
    0.0, 0.0,
    1.0, 1.0,
    0.0, 0.0,
    0.0, 1.0,
    0.0, 1.0,
    1.0, 1.0,
    1.0, 0.0,
    0.0, 1.0,
    1.0, 0.0,
    0.0, 0.0
  );

  Filepaths : array[0..3] of string = ('crazypaint.jpg', 'destination.png', 'Screenshot-5.jpg', 'adventure.png');

var
  ContextSettings: TSfmlContextSettings;
  OutputWindow: TSfmlWindow;
  ImageSize: TSfmlVector2u;
  i : integer;
  IDs: array [0..3] of GLuint;
  CubeFaces: array[0..3] of TSfmlImage;
begin
  for i := 0 to 3 do
    CubeFaces[i] := TSfmlImage.Create(Filepaths[i]);
  // Request a 32-bits depth buffer when creating the window
  ContextSettings.DepthBits := 32;
  // Create the main window
  OutputWindow := TSfmlWindow.Create(SfmlVideoMode(500, 500),
    'SFML window with 3D OpenGL', [sfTitleBar, sfClose], @ContextSettings);
  // Make it the active window for OpenGL calls
  OutputWindow.SetActive(True);
  // Set the color and depth clear values
  glClearDepth(1);
  glClearColor(0.4, 0.4, 0.8, 1.0); // Sets clear color to blue, fully opaque
  // Enable Z-buffer read and write
  glEnable(GL_DEPTH_TEST);
  glDepthMask(GL_TRUE);
  // Disable lighting and enable texturing
  glDisable(GL_LIGHTING);
  glEnable(GL_TEXTURE_2D);
  // Configure viewport to size of window
  glViewport(0, 0, OutputWindow.Size.X, OutputWindow.Size.Y);
  // Set up perspective projection with 45 degree field of view
  glMatrixMode(GL_PROJECTION);
  gluPerspective(45, 1, 0.1, 1000);
  // Enable position and texture coordinates vertex components
  glEnableClientState(GL_VERTEX_ARRAY);
  glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  // Disable colour and normal vertex component
  glDisableClientState(GL_COLOR_ARRAY);
  glDisableClientState(GL_NORMAL_ARRAY);
  // Link array data
  glVertexPointer(3, GL_FLOAT, 3 * sizeof(GLfloat), @CCube);
  glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), @CubeTexCoords);
  glGenTextures(4, @IDs);  // Creates IDs for the 4 textures
  for i := 0 to 3 do
    begin
      glBindTexture(GL_TEXTURE_2D, IDs[i]);  // Binds the ID to to texture to be uploaded
      // Upload an image
      ImageSize := SfmlImageGetSize(CubeFaces[i].Handle);
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ImageSize.X, ImageSize.Y, 0,
                   GL_RGBA, GL_UNSIGNED_BYTE, SfmlImageGetPixelsPtr(CubeFaces[i].Handle));
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    end;
  // Apply some transformations to cube
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity;
  glTranslatef(0, 0, -330);
  glRotatef(0.5 * 180 / pi, 1, 0, 0);
  glRotatef(0.9 * 180 / pi, 0, 1, 0);
  glRotatef(0.2 * 180 / pi, 0, 0, 1);
  glScalef(0.7, 0.7, 0.7);

  glClear(GL_COLOR_BUFFER_BIT);
  glEnable(GL_TEXTURE_2D);
  for i := 0 to 3 do
    begin
      glBindTexture(GL_TEXTURE_2D, IDs[i]); // Selects the texture
      glDrawArrays(GL_TRIANGLES, i * 6, 6); // Draws the image on a face
    end;
  OutputWindow.Display;
  sfmlSleep(sfmlSeconds(10));
end.    
Programming - a skill for life!

How to use the Simple Fast Media Library in Lazarus