问题
I'm having some troubles with OpenGL. First of all, I have a custom control which I'm building, encapsulating OpenGL. I'm having a problem where it rapidly flickers, like it's 'flashing'. The control was too large and complex to post here, so I made a new small demo app to demonstrate and recreate the same scenario.
The problem now: I'm not getting any image. Things would show fine in my custom control, but since I've copied the code and stripped it down in a small demo app, it won't show the image. So here I have two issues at hand: Flickering (or flashing), and now the image is not even showing. The flickering didn't occur in the past, but after some major code revision, it started flickering. It was too much code change to explain what exactly was changed, just about everything.
The background shows, so I know it's drawing something (it is colored). It should be drawing just a single cube for demo purposes, but I don't see anything. I had to strip this down from around 1,000 lines of code to not even 300.
It's not what you would normally call a flicker, it's actually more of a flashing, or blinking, imagine a car blinker flashing on and off. It definitely has to do with the timer, because the higher interval I put on the timer, the slower it flashes.
Why am I not seeing anything? And once this is fixed, why is it flickering so much?
Here's a single form's code, no DFM necessary:
unit uMain;
interface
uses
Winapi.Windows, Winapi.Messages, Winapi.OpenGL,
System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure FormResize(Sender: TObject);
private
FDrawing: Bool;
FDC: HDC;
FRC: HGLRC;
FDL: glUint;
FTimer: TTimer;
procedure Draw;
procedure SetDC(const Value: HDC);
procedure SetRC(const Value: HGLRC);
procedure SetDL(const Value: glUint);
public
property DC: HDC read FDC write SetDC;
property RC: HGLRC read FRC write SetRC;
property DL: glUint read FDL write SetDL;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
PixelFormat: glUint;
pfd: TPIXELFORMATDESCRIPTOR;
begin
FDrawing := False;
FDC := GetDC(Handle);
with pfd do begin
nSize := SizeOf(TPIXELFORMATDESCRIPTOR);
nVersion := 1; // The version of this data structure
dwFlags := PFD_DRAW_TO_WINDOW // Buffer supports drawing to window
or PFD_SUPPORT_OPENGL // Buffer supports OpenGL drawing
or PFD_DOUBLEBUFFER; // Supports double buffering
iPixelType := PFD_TYPE_RGBA; // RGBA color format
cColorBits := 32; // OpenGL color depth
cRedBits := 0; // Number of red bitplanes
cRedShift := 0; // Shift count for red bitplanes
cGreenBits := 0; // Number of green bitplanes
cGreenShift := 0; // Shift count for green bitplanes
cBlueBits := 0; // Number of blue bitplanes
cBlueShift := 0; // Shift count for blue bitplanes
cAlphaBits := 0; // Not supported
cAlphaShift := 0; // Not supported
cAccumBits := 0; // No accumulation buffer
cAccumRedBits := 0; // Number of red bits in a-buffer
cAccumGreenBits := 0; // Number of green bits in a-buffer
cAccumBlueBits := 0; // Number of blue bits in a-buffer
cAccumAlphaBits := 0; // Number of alpha bits in a-buffer
cDepthBits := 16; // Specifies the depth of the depth buffer
cStencilBits := 0; // Turn off stencil buffer
cAuxBuffers := 0; // Not supported
iLayerType := PFD_MAIN_PLANE; // Ignored
bReserved := 0; // Number of overlay and underlay planes
dwLayerMask := 0; // Ignored
dwVisibleMask := 0; // Transparent color of underlay plane
dwDamageMask := 0; // Ignored
end;
PixelFormat := ChoosePixelFormat(FDC, @pfd);
SetPixelFormat(FDC, PixelFormat, @pfd);
FRC := wglCreateContext(FDC);
wglMakeCurrent(FDC, FRC);
FormResize(nil);
wglMakeCurrent(FDC, FRC);
glClearColor(0.8, 0.8, 0.9, 0.0);
glShadeModel(GL_FLAT);
glClearDepth(1.0);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.4);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glEnable(GL_TEXTURE_2D);
glNewList(FDL, GL_COMPILE);
glBegin(GL_QUADS);
// Front Face
glTexCoord2f(0.0, 0.0);
glVertex3f(-2.0, -1.0, 1.0);
glTexCoord2f(2.0, 0.0);
glVertex3f(2.0, -1.0, 1.0);
glTexCoord2f(2.0, 1.0);
glVertex3f(2.0, 1.0, 1.0);
glTexCoord2f(0.0, 1.0);
glVertex3f(-2.0, 1.0, 1.0);
// Back Face
glTexCoord2f(2.0, 0.0);
glVertex3f(-2.0, -1.0, -1.0);
glTexCoord2f(2.0, 1.0);
glVertex3f(-2.0, 1.0, -1.0);
glTexCoord2f(0.0, 1.0);
glVertex3f(2.0, 1.0, -1.0);
glTexCoord2f(0.0, 0.0);
glVertex3f(2.0, -1.0, -1.0);
// Top Face
glTexCoord2f(0.0, 1.0);
glVertex3f(-2.0, 1.0, -1.0);
glTexCoord2f(0.0, 0.0);
glVertex3f(-2.0, 1.0, 1.0);
glTexCoord2f(2.0, 0.0);
glVertex3f(2.0, 1.0, 1.0);
glTexCoord2f(2.0, 1.0);
glVertex3f(2.0, 1.0, -1.0);
// Bottom Face
glTexCoord2f(2.0, 1.0);
glVertex3f(-2.0, -1.0, -1.0);
glTexCoord2f(0.0, 1.0);
glVertex3f(2.0, -1.0, -1.0);
glTexCoord2f(0.0, 0.0);
glVertex3f(2.0, -1.0, 1.0);
glTexCoord2f(2.0, 0.0);
glVertex3f(-2.0, -1.0, 1.0);
// Left Face
glTexCoord2f(0.0, 0.0);
glVertex3f(2.0, -1.0, -1.0);
glTexCoord2f(1.0, 0.0);
glVertex3f(2.0, -1.0, 1.0);
glTexCoord2f(1.0, 1.0);
glVertex3f(2.0, 1.0, 1.0);
glTexCoord2f(0.0, 1.0);
glVertex3f(2.0, 1.0, -1.0);
glEnd();
glEndList();
FTimer:= TTimer.Create(nil);
FTimer.OnTimer:= Timer1Timer;
FTimer.Interval:= 100;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FTimer.Free;
if (not wglMakeCurrent(FDC, 0)) then
MessageBox(0, 'Release of DC and RC failed!', 'Error', MB_OK or MB_ICONERROR);
if (not wglDeleteContext(FRC)) then begin
MessageBox(0, 'Release of rendering context failed!', 'Error', MB_OK or MB_ICONERROR);
FRC := 0;
end;
if ((FDC > 0) and (ReleaseDC(Handle, FDC) = 0)) then begin
MessageBox(0, 'Release of device context failed!', 'Error', MB_OK or MB_ICONERROR);
FDC := 0;
end;
end;
procedure TForm1.Draw;
var
I: Integer;
begin
if not FDrawing then begin
FDrawing := TRUE;
try
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glEnable(GL_NORMALIZE);
glShadeModel(GL_FLAT);
glCullFace(GL_BACK);
glLoadIdentity;
glPushMatrix();
glCallList(DL);
glPopMatrix();
SwapBuffers(wglGetCurrentDC);
finally
FDrawing := False;
end;
end;
end;
procedure TForm1.FormResize(Sender: TObject);
begin
glViewport(0, 0, Width, Height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, Width / Height, 0.1, 500.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
end;
procedure TForm1.SetDC(const Value: HDC);
begin
FDC := Value;
end;
procedure TForm1.SetDL(const Value: glUint);
begin
FDL := Value;
end;
procedure TForm1.SetRC(const Value: HGLRC);
begin
FRC := Value;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Draw;
end;
end.
Now the above code is rather far different from how my original code works. The original Draw
procedure does an iteration through a list of objects, each object containing its own Draw
procedure. So the control's draw procedure prepares the overall scene, then draws each 'item' one by one, like this:
procedure TGLImage.Draw;
var
X: Integer;
begin
if not FDrawing then begin
FDrawing := TRUE;
try
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glEnable(GL_NORMALIZE);
glShadeModel(GL_FLAT);
glCullFace(GL_BACK);
glLoadIdentity();
glRotatef(FElapsedTime / 70, 0, 0, 1);
glRotatef(90, 0, 1, 0);
glTranslatef(-FElapsedTime / 400, 0, 0);
if FInitialized then begin
for X := 0 to FItems.Count - 1 do begin
FItems[X].Draw;
end;
end;
SwapBuffers(wglGetCurrentDC);
finally
FDrawing := False;
end;
end;
end;
And here's one of the items it draws...
constructor TGLBeam.Create(AOwner: TGLItems);
begin
inherited;
glNewList(DL, GL_COMPILE);
glBegin(GL_QUADS);
// Front Face
glTexCoord2f(0.0, 0.0);
glVertex3f(-2.0, -1.0, 1.0);
glTexCoord2f(2.0, 0.0);
glVertex3f(2.0, -1.0, 1.0);
glTexCoord2f(2.0, 1.0);
glVertex3f(2.0, 1.0, 1.0);
glTexCoord2f(0.0, 1.0);
glVertex3f(-2.0, 1.0, 1.0);
// Back Face
glTexCoord2f(2.0, 0.0);
glVertex3f(-2.0, -1.0, -1.0);
glTexCoord2f(2.0, 1.0);
glVertex3f(-2.0, 1.0, -1.0);
glTexCoord2f(0.0, 1.0);
glVertex3f(2.0, 1.0, -1.0);
glTexCoord2f(0.0, 0.0);
glVertex3f(2.0, -1.0, -1.0);
// Top Face
glTexCoord2f(0.0, 1.0);
glVertex3f(-2.0, 1.0, -1.0);
glTexCoord2f(0.0, 0.0);
glVertex3f(-2.0, 1.0, 1.0);
glTexCoord2f(2.0, 0.0);
glVertex3f(2.0, 1.0, 1.0);
glTexCoord2f(2.0, 1.0);
glVertex3f(2.0, 1.0, -1.0);
// Bottom Face
glTexCoord2f(2.0, 1.0);
glVertex3f(-2.0, -1.0, -1.0);
glTexCoord2f(0.0, 1.0);
glVertex3f(2.0, -1.0, -1.0);
glTexCoord2f(0.0, 0.0);
glVertex3f(2.0, -1.0, 1.0);
glTexCoord2f(2.0, 0.0);
glVertex3f(-2.0, -1.0, 1.0);
// Left Face
glTexCoord2f(0.0, 0.0);
glVertex3f(2.0, -1.0, -1.0);
glTexCoord2f(1.0, 0.0);
glVertex3f(2.0, -1.0, 1.0);
glTexCoord2f(1.0, 1.0);
glVertex3f(2.0, 1.0, 1.0);
glTexCoord2f(0.0, 1.0);
glVertex3f(2.0, 1.0, -1.0);
glEnd();
glEndList();
end;
procedure TGLBeam.Draw;
var
I: Integer;
begin
glRotatef(Directions.X, 1.0, 0.0, 0.0);
glRotatef(Directions.Y, 0.0, 1.0, 0.0);
glRotatef(Directions.Z, 0.0, 0.0, 1.0);
for I := 1 to 10 do begin
//Main Center
glPushMatrix();
glTranslatef(I * 4 + Owner.Owner.ClockTime * 4, 0, 0);
glCallList(DL);
glPopMatrix();
//Above
glPushMatrix();
glTranslatef(I * 4 + Owner.Owner.ClockTime * 4, 6, 0);
glCallList(DL);
glPopMatrix();
end;
end;
回答1:
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glEnable(GL_NORMALIZE);
glShadeModel(GL_FLAT);
glCullFace(GL_BACK);
glLoadIdentity;
glPushMatrix();
glCallList(DL);
glPopMatrix();
SwapBuffers(wglGetCurrentDC);
Firstly add the () to your call to glLoadIdentity
You loading the identity matrix then pushing it then rendering and popping it without actually doing any transformations.
Either remove the 3 lines
glLoadIdentity;
glPushMatrix();
glPopMatrix();
or move the glPushMatrix()
to the line before the glLoadIdentity()
I would also suggest turning off shading and culling just to make sure they aren't interfering. Quads being declared in a slightly wrong order with backface culling on just culls them even when you don't want them culled from in front.
回答2:
The issue with the flickering was has been discovered, and it was a very huge mistake that I don't expect you to have figured out without having the entire project. The problem was that I was creating TWO of these controls, and they were interfering / fighting with each other. Actually, because OpenGL works on a thread-by-thread basis (or within one 'context'), having two different controls which are trying to do their own drawing will interfere - causing the same thing to occur on both controls at the same time. So the flashing was from the fact that each control was trying to take turns doing the drawing. My new question which I have asked related to putting this drawing inside of a Thread will perfectly cure for this issue. It was a dumb mistake and I'm sorry for wasting anyone's time.
来源:https://stackoverflow.com/questions/10729526/opengl-rapid-flickering-issues-and-now-not-showing-image