Delphi与OpenGL基础应用问题:窗体未显示预期深色背景
我用Delphi 10.4开发结合OpenGL的基础应用,要在屏幕上绘制2D图像。代码运行无报错,但窗体始终是灰色,没出现预期的深绿色背景。已在IDE和独立程序测试,Windows 10系统,确认System32里有opengl32.dll。请问问题出在哪?
原代码
unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, OpenGL; type TForm1 = class(TForm) procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FormPaint(Sender: TObject); procedure FormResize(Sender: TObject); private { Private declarations } GLContext : HGLRC; glDC: HDC; errorCode: GLenum; openGLReady: Boolean; public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); var pfd: TPixelFormatDescriptor; FormatIndex: Integer; begin FillChar(pfd,SizeOf(pfd),0); with pfd do begin nSize := SizeOf(pfd); nVersion := 1; {The current version of the desccriptor is 1} dwFlags := PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL; iPixelType := PFD_TYPE_RGBA; cColorBits := 24; {support 24-bit color} cDepthBits := 32; {depth of z-axis} iLayerType := PFD_MAIN_PLANE; end; glDC := getDC(handle); FormatIndex := ChoosePixelFormat(glDC,@pfd); SetPixelFormat(glDC,FormatIndex,@pfd); GLContext := wglCreateContext(glDC); wglMakeCurrent(glDC,GLContext); OpenGLReady := true; end; procedure TForm1.FormDestroy(Sender: TObject); begin wglMakeCurrent(Canvas.Handle,0); wglDeleteContext(GLContext); end; procedure TForm1.FormPaint(Sender: TObject); begin if not openGLReady then exit; {background} glClearColor(0.1,0.0,0.1,0.0); glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity; // Reset The View glTranslatef(0.0, 0, 0.0); glRotateF (360, 0.0, 0.0, 1.0); glBegin( GL_POLYGON ); // start drawing a polygon glColor3f( 1.0, 0.0, 0.0); glVertex3f( 0.0, 0.5, 0.0 ); // Top glColor3f(0.0, 1.0, 0.0); glVertex3f( 0.5, -0.5, 0.0 ); // Bottom Right glColor3f(0.0, 0.0, 1.0); glVertex3f( -0.5, -0.5, 0.0 ); // Bottom Left glEnd; glFlush; {error checking} errorCode:=glGetError; if errorCode<>GL_NO_ERROR then raise Exception.Create('Error in Paint'#13+gluErrorString(errorCode)); SwapBuffers(wglGetCurrentDC); glFlush(); end; procedure TForm1.FormResize(Sender: TObject); begin if not openGLReady then exit; glViewPort(0,0,ClientWidth,ClientHeight); glOrtho(-1.0,1.0,-1.0,1.0,-1.0,1.0); errorCode := glGetError; if errorCode<>GL_NO_ERROR then raise Exception.Create('FormResize:'+gluErrorString(errorCode)); end; procedure GLInit; begin // set viewing projection glMatrixMode(GL_PROJECTION); glFrustum(-0.1, 0.1, -0.1, 0.1, 0.3, 15.0); // position viewer glMatrixMode(GL_MODELVIEW); glEnable(GL_DEPTH_TEST); end; end.
问题分析与修正方案
你的代码存在多处细节错误,导致OpenGL绘制内容无法正常显示:
初始化函数未调用
定义了GLInit用于设置投影矩阵,但从未在FormCreate中调用,导致OpenGL矩阵状态异常,绘制内容不在可视区域。
修正:在wglMakeCurrent后添加GLInit;调用。销毁上下文时DC错误
FormDestroy中使用Canvas.Handle作为DC来释放上下文,应该用创建时保存的glDC,否则会导致上下文清理异常。
修正:procedure TForm1.FormDestroy(Sender: TObject); begin wglMakeCurrent(glDC, 0); wglDeleteContext(GLContext); end;glViewport拼写错误
FormResize中的glViewPort是笔误,正确函数名是glViewport(小写p),这个错误会导致视口设置失败,内容无法映射到窗体。
同时设置glOrtho前需要切换到投影矩阵并重置,否则会叠加之前的矩阵变换:
修正:procedure TForm1.FormResize(Sender: TObject); begin if not openGLReady then exit; glViewport(0,0,ClientWidth,ClientHeight); glMatrixMode(GL_PROJECTION); glLoadIdentity; glOrtho(-1.0,1.0,-1.0,1.0,-1.0,1.0); glMatrixMode(GL_MODELVIEW); errorCode := glGetError; if errorCode<>GL_NO_ERROR then raise Exception.Create('FormResize:'+gluErrorString(errorCode)); end;SwapBuffers与glFlush冗余/错误
FormPaint中重复调用glFlush,且SwapBuffers使用wglGetCurrentDC不如直接用保存的glDC可靠,应该只在交换缓冲区前或后调用一次glFlush。同时要清空深度缓冲区,避免绘制被遮挡:
修正:procedure TForm1.FormPaint(Sender: TObject); begin if not openGLReady then exit; glClearColor(0.1,0.0,0.1,0.0); glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); glLoadIdentity; glTranslatef(0.0, 0, 0.0); glRotateF (360, 0.0, 0.0, 1.0); glBegin( GL_POLYGON ); glColor3f( 1.0, 0.0, 0.0); glVertex3f( 0.0, 0.5, 0.0 ); glColor3f(0.0, 1.0, 0.0); glVertex3f( 0.5, -0.5, 0.0 ); glColor3f(0.0, 0.0, 1.0); glVertex3f( -0.5, -0.5, 0.0 ); glEnd; errorCode:=glGetError; if errorCode<>GL_NO_ERROR then raise Exception.Create('Error in Paint'#13+gluErrorString(errorCode)); glFlush; SwapBuffers(glDC); end;深度缓冲区冲突
你请求了32位深度缓冲区,且GLInit启用了GL_DEPTH_TEST,但2D绘图不需要深度测试,若为纯2D应用,可以在GLInit中去掉glEnable(GL_DEPTH_TEST),减少不必要的性能开销。
额外建议
- 2D绘图时,
GLInit中的glFrustum(透视投影)可以替换为glOrtho(正交投影),更符合2D场景需求。 - 每次矩阵操作后记得切换回
GL_MODELVIEW矩阵,避免后续绘制使用错误的矩阵模式。
内容的提问来源于stack exchange,提问作者brane

