1.1 Hello Window - neslib/DelphiLearnOpenGL GitHub Wiki
:link: Source Code
Create new application
Let's see if we can get our first app up and running. The easiest way is to use an existing application as a template by copying over the source code from the repository and renaming the project. But if you want to start from scratch, then follow these steps:
- First, open Delphi and create a new Multi-Device Application.
- Choose the Blank Application template.
- Remove the automatically generated form from the project (eg.
Unit1.pas
). Don't save the form. - Open the project source code (Project | View Source).
- Remove the
uses
clause and everything betweenbegin
andend
. In particular, we don't want any references to FMX units. - Make sure you have downloaded or cloned the Tutorials/Common directory from the repository. Add this directory to the search path of your application, for all configurations. (Go to "Project | Options... | Delphi Compiler" and select the Target "All configurations - All platforms". Then add the directory to the "Search path" option).
Create application class
Create a new unit called App
and create a new class derived from TApplication
(from the unit Sample.App
). We must override the abstract methods Initialize
, Update
and Shutdown
. We also override the KeyDown
method so we can terminate the app when the user presses the Esc
key.
type
THelloWindow = class(TApplication)
public
procedure Initialize; override;
procedure Update(const ADeltaTimeSec, ATotalTimeSec: Double); override;
procedure Shutdown; override;
procedure KeyDown(const AKey: Integer; const AShift: TShiftState); override;
end;
This first tutorial doesn't create any OpenGL resources, so we can leave the implementations of the Initialize
and Shutdown
methods empty. The key method to override is Update
, which is called once every frame to update application state and render a frame:
procedure THelloWindow.Update(const ADeltaTimeSec, ATotalTimeSec: Double);
begin
{ Define the viewport dimensions }
glViewport(0, 0, Width, Height);
{ Render by simply clearing the color buffer }
glClearColor(0.2, 0.3, 0.3, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
end;
This method is explained below:
Viewport
Before we can start rendering we have to tell OpenGL the size of the rendering window so OpenGL knows how we want to display the data and coordinates with respect to the window. We can set those dimensions via the glViewport
function:
glViewport(0, 0, Width, Height);
The first two parameters of glViewport
set the location of the lower left corner of the window. The third and fourth parameter set the width and height of the rendering window in pixels, which are properties of the TApplication
class. On mobile devices, these properties are set to the dimensions of the device and automatically updated when the user rotates the device. On desktop platforms, these properties are initialized to the size of the client area of the window that is created.
We could actually set the viewport dimensions at values smaller than the window dimensions; then all the OpenGL rendering would be displayed in a smaller area and we could for example display other elements outside the OpenGL viewport.
:information_source: Behind the scenes OpenGL uses the data specified via
glViewport
to transform the 2D coordinates it processed to coordinates on your screen. For example, a processed point of location (-0.5,0.5) would (as its final transformation) be mapped to (200,450) in screen coordinates. Note that processed coordinates in OpenGL are between -1 and 1 so we effectively map from the range (-1 to 1) to (0, 800) and (0, 600).
Rendering
We want to place all the rendering commands in the game loop, since we want to execute all the rendering commands each iteration of the loop. So all rendering must occur inside the Update
method (or other methods that are called from the Update
method).
Just to test if things actually work we want to clear the screen with a color of our choice. At the start of each render iteration we always want to clear the screen otherwise we would still see the results from the previous iteration (this could be the effect you're looking for, but usually you don't). We can clear the screen's color buffer using the glClear
function where we pass in buffer bits to specify which buffer we would like to clear. The possible bits we can set are GL_COLOR_BUFFER_BIT
, GL_DEPTH_BUFFER_BIT
and GL_STENCIL_BUFFER_BIT
. Right now we only care about the color values so we only clear the color buffer:
glClearColor(0.2, 0.3, 0.3, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
Note that we also set a color via glClearColor
to clear the screen with. Whenever we call glClear
and clear the color buffer, the entire colorbuffer will be filled with the color as configured by glClearColor
. This will result in a dark green-blueish color.
:information_source: As you might recall from the OpenGL tutorial, the
glClearColor
function is a state-setting function andglClear
is a state-using function in that it uses the current state to retrieve the clearing color from.
Input
We also want to have some form of input control. This is accomplished by overriding one or more event methods from TApplication
, like MouseDown
, MouseMove
, KeyDown
etc. In this tutorial, we override KeyDown
to terminate the app when the Esc
key is pressed:
procedure THelloWindow.KeyDown(const AKey: Integer; const AShift: TShiftState);
begin
if (AKey = vkEscape) then
Terminate;
end;
App
Source Code
To recap, the complete source code of the App unit looks like this:
unit App;
{$INCLUDE 'Sample.inc'}
interface
uses
System.Classes,
Sample.App;
type
THelloWindow = class(TApplication)
public
procedure Initialize; override;
procedure Update(const ADeltaTimeSec, ATotalTimeSec: Double); override;
procedure Shutdown; override;
procedure KeyDown(const AKey: Integer; const AShift: TShiftState); override;
end;
implementation
uses
{$INCLUDE 'OpenGL.inc'}
System.UITypes;
{ THelloWindow }
procedure THelloWindow.Initialize;
begin
{ Not needed in this sample }
end;
procedure THelloWindow.KeyDown(const AKey: Integer; const AShift: TShiftState);
begin
{ Terminate app when Esc key is pressed }
if (AKey = vkEscape) then
Terminate;
end;
procedure THelloWindow.Shutdown;
begin
{ Not needed in this sample }
end;
procedure THelloWindow.Update(const ADeltaTimeSec, ATotalTimeSec: Double);
begin
{ Define the viewport dimensions }
glViewport(0, 0, Width, Height);
{ Render by simply clearing the color buffer }
glClearColor(0.2, 0.3, 0.3, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
end;
end.
There are a few things to note here:
- The unit includes the file
Sample.inc
. This include file contains some common defines that are used in all tutorials. For example, it sets the$SCOPEDENUMS
option and defines the conditionalMOBILE
on iOS and Android platforms. You should include this file in all tutorial sources. - In the
implementation
section, you see another include file in theuses
clause calledOpenGL.inc
. This include files adds the correct OpenGL units to theuses
clause depending on platform. For example, on Windows, OpenGL APIs are declared in the unitsWinapi.OpenGL
andWinapi.OpenGLExt
and on iOS they are defined iniOSapi.OpenGLES
. By putting these platform differences in an include file, it keeps youruses
clause clean and simple.
So right now we got everything ready to fill the game loop with lots of rendering calls, but that's for the [next tutorial](1.2 Hello Triangle). I think we've been rambling long enough here.
:arrow_left: [Creating an OpenGL App](Creating an OpenGL App) | Contents | [1.2 Hello Triangle](1.2 Hello Triangle) :arrow_right: |
---|