Drawing a Rectangle with and without Shaders

We begin with easier (but much less versatile) ways of drawing a rectangle without shaders. The easiest way, if you want a uniformly coloured rectangle, is to use the glRectf procedure as in this example.

unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, OpenGLContext, Forms, Controls, Graphics, Dialogs, GL, GLU;
type
  TForm1 = class(TForm)
    OpenGLControl1: TOpenGLControl;
    procedure OpenGLControl1Paint(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

procedure TForm1.OpenGLControl1Paint(Sender: TObject);
begin
  glClearColor (1.0, 1.0, 1.0, 1.0); // white background
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity;
  gluOrtho2D(0, 200, 0, 200);
  glClear(GL_COLOR_BUFFER_BIT);
  glColor3f (1.0, 0.0, 0.0);  // red

  glRectf(10, 10, 190, 190);

  OpenGLControl1.SwapBuffers;
end;

end.
      

Replace the call to glRectf with either of these blocks of code to produce the same output:

glBegin(GL_QUADS);
  glVertex2i(190, 10);
  glVertex2i(10, 10);
  glVertex2i(10, 190);
  glVertex2i(190, 190);
glEnd;
    
glBegin(GL_TRIANGLES);
  glVertex2i(190, 10);
  glVertex2i(10, 10);
  glVertex2i(10, 190);

  glVertex2i(190, 10);
  glVertex2i(10, 190);
  glVertex2i(190, 190);
glEnd;
    

You could instead put the vertices into an array as demonstrated in the following shader example.

Using Shaders

This example is based on our Smart Pascal WebGL demonstration. You can download the dglOpenGL unit from this English page on the Delphi OpenGL Community wiki.

The code of the unit and of the form follow a screenshot of the output.

Output

Output

unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, OpenGLContext, Forms, Controls, Graphics, Dialogs, GLU, dglOpenGL;
type
  TForm1 = class(TForm)
    OpenGLControl1: TOpenGLControl;
    procedure FormCreate(Sender: TObject);
    procedure OpenGLControl1Paint(Sender: TObject);
  end;

var
  Form1: TForm1;
  VertexArray: array[0..11] of GLFloat = (
    -0.9, -0.9,
     0.9, -0.9,
    -0.9,  0.9,
    -0.9,  0.9,
     0.9, -0.9,
     0.9,  0.9
  );
  VertexShader, FragmentShader, ShaderProgram: glHandle;
  VertexCode: PGLchar = 'attribute vec2 a_position; ' +
                        'void main(void) {' +
                        '  gl_Position = vec4(a_position, 0.0, 1.0);' +
                        '}';

  FragmentCode: PGLchar = 'void main(void) {' +
                          '  gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);' +
                          '}';
  VertexPosAttrib: integer;

implementation

{$R *.lfm}

procedure TForm1.OpenGLControl1Paint(Sender: TObject);
begin
  glClearColor (1.0, 1.0, 1.0, 1.0); // white background
  glClear(GL_COLOR_BUFFER_BIT);
  VertexPosAttrib := glGetAttribLocation(ShaderProgram, pchar('a_position'));
  glEnableVertexAttribArray(VertexPosAttrib);
  glDeleteShader(VertexShader);
  glDeleteShader(FragmentShader);
  glEnableClientState(GL_VERTEX_ARRAY);
  glVertexAttribPointer(VertexPosAttrib, 2, GL_Float, True, 0, @VertexArray);
  glDrawArrays(GL_TRIANGLES, 0, 6);

  OpenGLControl1.SwapBuffers;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  InitOpenGL;
  OpenGLControl1.MakeCurrent;
  ReadImplementationProperties;
  ReadExtensions;
  VertexShader := glCreateShader(GL_VERTEX_SHADER);
  FragmentShader := glCreateShader(GL_FRAGMENT_SHADER);

  glShaderSource(VertexShader, 1, @VertexCode, nil);
  glCompileShader(VertexShader);
  glShaderSource(FragmentShader, 1, @FragmentCode, nil);
  glCompileShader(FragmentShader);

  ShaderProgram := glCreateProgram();
  glAttachShader(ShaderProgram, VertexShader);
  glAttachShader(ShaderProgram, FragmentShader);
  glLinkProgram(ShaderProgram);
  glUseProgram(ShaderProgram);
end;

end.
    

Code of form:

object Form1: TForm1
  Left = 316
  Height = 247
  Top = 283
  Width = 253
  Caption = 'Shader Demo'
  ClientHeight = 247
  ClientWidth = 253
  OnCreate = FormCreate
  LCLVersion = '1.4.0.4'
  object OpenGLControl1: TOpenGLControl
    Left = 10
    Height = 230
    Top = 10
    Width = 230
    OnPaint = OpenGLControl1Paint
  end
end
              

The second example, which outputs a rectangle with gradient shading, has error detection code that is useful for eliminating errors.

unit Unit1;

{$Mode Delphi}

interface

uses
  Classes, SysUtils, FileUtil, OpenGLContext, Forms, Controls, Graphics, Dialogs, GLU, dglOpenGL;
type
  TForm1 = class(TForm)
    OpenGLControl1: TOpenGLControl;
    procedure FormCreate(Sender: TObject);
    procedure OpenGLControl1Paint(Sender: TObject);
  end;

var
  Form1: TForm1;
  VertexArray: array[0..11] of GLFloat = (
     1.0, 1.0, 0.0,
    -1.0, 1.0, 0.0,
    -1.0, -1.0, 0.0,
     1.0, -1.0, 0.0
   );
  ColourArray: array[0..11] of GLFloat = (
    1.0, 0.0, 0.0, //red
    0.0, 1.0, 0.0, //green
    0.0, 0.0, 1.0, //blue
    0.0, 0.0, 0.0  //black
  );

  VertexShader, FragmentShader, ShaderProgram: glHandle;
  VertexCode: PGLchar = 'attribute vec3 a_position; ' +
                        'attribute vec3 a_colour; ' +
                        'varying vec4 vColor; ' +
                        'void main(void) {' +
                        '  gl_Position = vec4(a_position, 1.0); ' +
                        '  vColor = vec4(a_colour, 1.0); ' +
                        '}';

  FragmentCode: PGLchar =  'precision mediump float; ' +
                           'varying vec4 vColor; ' +
                           'void main(void) {' +
                           '  gl_FragColor = vColor; ' +
                           '}';
  VertexPosAttrib, ColourAttrib, Success: integer;
  { Comment added in August 2016: In order to compile this program using the current version of dglOpengl,
    declare Success as ByteBool rather than integer. }

implementation

{$R *.lfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  InitOpenGL;
  OpenGLControl1.MakeCurrent;
  ReadImplementationProperties;
  ReadExtensions;
  VertexShader := glCreateShader(GL_VERTEX_SHADER);
  FragmentShader := glCreateShader(GL_FRAGMENT_SHADER);

  glShaderSource(VertexShader, 1, @VertexCode, nil);
  glCompileShader(VertexShader);
  // If vertex shader did not compile, show a message and exit.
  glGetShaderiv(VertexShader, GL_COMPILE_STATUS, @Success);
  if Success = GL_FALSE then
    begin
      ShowMessage('Vertex shader failed to compile');
      exit;
    end;

  glShaderSource(FragmentShader, 1, @FragmentCode, nil);
  glCompileShader(FragmentShader);
  // If fragment shader did not compile, show a message and exit.
  if Success = GL_FALSE then
    begin
      Showmessage('Fragment shader failed to compile');
      exit;
    end;

  ShaderProgram := glCreateProgram();

  glAttachShader(ShaderProgram, VertexShader);
  glAttachShader(ShaderProgram, FragmentShader);

  glLinkProgram(ShaderProgram);
  // Error check
  glGetProgramiv(ShaderProgram, GL_LINK_STATUS, @Success);
  if Success = GL_FALSE then
    begin
      ShowMessage('Error with linking shader program');
      exit;
    end;
  glDeleteShader(VertexShader);
  glDeleteShader(FragmentShader);
  glUseProgram(ShaderProgram);
end;

procedure TForm1.OpenGLControl1Paint(Sender: TObject);
var
  start: integer;
begin
  glClearColor (1.0, 1.0, 1.0, 1.0); // white background
  glClear(GL_COLOR_BUFFER_BIT);
  VertexPosAttrib := glGetAttribLocation(ShaderProgram, pchar('a_position'));
  ColourAttrib := glGetAttribLocation(ShaderProgram, pchar('a_colour'));

  glEnableVertexAttribArray(VertexPosAttrib);
  glEnableVertexAttribArray(ColourAttrib);
  glEnableClientState(GL_VERTEX_ARRAY);

  glVertexAttribPointer(VertexPosAttrib, 3, GL_Float, False, sizeof(GLFloat) * 3, @VertexArray);
  glVertexAttribPointer(ColourAttrib, 3, GL_Float, False, sizeof(GLFloat) * 3, @ColourArray);
  try
    glDrawArrays(GL_QUADS, 0, 4);
  except
    ShowMessage('Error on drawing arrays.');
    exit;
  end;

  OpenGLControl1.SwapBuffers;
end;

end.
    
Programming - a skill for life!

How to use OpenGL for 2D and 3D graphics