Since Delphi's own OpenGL.pas is version 1.0, and what is actually used now is at least version 1.1, and the Windows pure software simulation method is also version 1.1, so you have to import some necessary functions yourself. Some open source free units are also available, such as Mike Lischke's OpenGL12.pas. Of course, writing it yourself can make the design more concise, and you don't have to find errors in a huge code that is too advanced and complete.
First introduce the necessary units Windows, Messages, OpenGL
To add some necessary extensions.
const
//GL_EXT_bgra
GL_BGR_EXT = $80E0;
GL_BGRA_EXT = $80E1;
// polygon offset
GL_POLYGON_OFFSET_UNITS = $2A00;
GL_POLYGON_OFFSET_POINT = $2A01;
GL_POLYGON_OFFSET_LINE = $2A02;
GL_POLYGON_OFFSET_FILL = $8037;
GL_POLYGON_OFFSET_FACTOR = $8038;
PRocedure glBindTexture(target: GLEnum; texture: GLuint); stdcall; external opengl32;
procedure glDeleteTextures(n: GLsizei; textures: PGLuint); stdcall; external opengl32;
procedure glGenTextures(n: GLsizei; textures: PGLuint); stdcall; external opengl32;
function glIsTexture(texture: GLuint): GLboolean; stdcall; external opengl32;
procedure glPolygonOffset(factor, units: GLfloat); stdcall; external opengl32;
// This statement is used to correct a bug in OpenGL.pas
function gluBuild2DMipmaps(target: GLEnum; components, width, height: GLint; format, atype: GLEnum; Data: Pointer): GLint; stdcall; external opengl32;
Now the interface has been basically upgraded to version 1.1. If other extensions are needed, they can be added similarly.
Next, you need to create the OpenGL drawing context RC, for which you need the device context DC of the GDI window. The TForm.Handle property or other Handle properties of TWinControl are DC. You can use the following function to create RC from DC, and the return value is the handle of RC. You can then draw using OpenGL. Generally can be used within the OnCreate event of the Form. The options of this function mean depth buffer, template buffer, accumulation buffer, and generate Alpha channel value.
type
TRCOptions = set of (roDepth, roStencil, roAccum, roAlpha);
function CreateRC(dc: HDC; opt: TRCOptions): HGLRC;
var
PFDescriptor: TPixelFormatDescriptor;
PixelFormat: Integer;
begin
FillChar(PFDescriptor, SizeOf(PFDescriptor), 0);
with PFDescriptor do
begin
nSize := SizeOf(PFDescriptor);
nVersion := 1;
dwFlags := PFD_SUPPORT_OPENGL or PFD_DRAW_TO_WINDOW or PFD_DOUBLEBUFFER;
iPixelType := PFD_TYPE_RGBA;
cColorBits := GetDeviceCaps(DC, BITSPIXEL) * GetDeviceCaps(DC, PLANES);
if roDepth in opt then cDepthBits := 24;
if roStencil in opt then cStencilBits := 8;
if roAccum in opt then cAccumBits := 64;
iLayerType := PFD_MAIN_PLANE;
end;
PixelFormat := ChoosePixelFormat(DC, @PFDescriptor);
Assert(PixelFormat <> 0);
Assert(SetPixelFormat(DC, PixelFormat, @PFDescriptor));
Result := wglCreateContext(DC);
Assert(Result <> 0);
wglMakeCurrent(dc, Result);
end;
Draw in the Form's OnPaint event. Remember, after the drawing is completed, you need to use SwapBuffers (dc: HDC) to exchange the drawing buffer and the display buffer so that the image will be displayed. Also remember to call glViewport(0, 0, ClientWidth, ClientHeight); in the Form's OnResize event so that RC and DC can be synchronized.
Destroy RC in the Form's OnDestroy event.
procedure DestroyRC(rc: HGLRC);
begin
if rc = 0 then Exit;
wglMakeCurrent(0, 0);
wglDeleteContext(rc);
end;
At this point, the framework of an OpenGL program has roughly taken shape. But there are still problems to be solved.
First, prevent Windows from erasing the background and affecting speed. Add member functions to Form
private
procedure WMEraseBkgnd(var Message: TWmEraseBkgnd); message WM_ERASEBKGND;
procedure TGLWindow.WMEraseBkgnd(var Message: TWmEraseBkgnd);
begin
Message.Result := 1;
end;
Second, to be safer. Add the following member functions.
protected
procedure CreateParams(var Params: TCreateParams); override;
procedure TGLWindow.CreateParams(var Params: TCreateParams);
begin
inherited;
with Params do
begin
Style := Style or WS_CLIPCHILDREN or WS_CLIPSIBLINGS;
WindowClass.Style := CS_VREDRAW or CS_HREDRAW or CS_OWNDC;
end;
end;
Okay, now you can forget about these troublesome things and write your wonderful 3D display :)
I have to say a few words, don't create multiple RCs in one thread, as this will seriously affect performance. Some personal OpenGL window control demonstrations put multiple controls on a Form, which is actually not a good idea. Multiple views should be displayed with one OpenGL window. Also, don't access OpenGL functions across threads.
Also, when Windows automatically installs the graphics card driver, it will not install OpenGL hardware acceleration. You must install the graphics card manufacturer's driver yourself!
In addition, a free full-screen display function is provided:)
function FullScreen(win: TWinControl; width, height, bitdepth: integer): boolean;
var displaymode: DEVMODE;
begin
FillChar(displaymode, sizeof(displaymode), 0);
with displaymode do
begin
dmSize := sizeof(displaymode);
dmPelsWidth:= width;
dmPelsHeight := height;
dmBitsPerPel := bitdepth;
dmFields := DM_BITSPERPEL or DM_PELSWIDTH or DM_PELSHEIGHT;
end;
if ChangeDisplaySettings(displaymode, CDS_FULLSCREEN) = DISP_CHANGE_SUCCESSFUL
then begin
ShowWindow(win.Handle, WS_MAXIMIZE);
result := true;
end
else result := false;
end;
procedure RestoreDisplay(win: TWinControl);
begin
ChangeDisplaySettings(PDEVMODE(0)^, 0);
ShowWindow(win.Handle, SW_RESTORE);
end;