【问题标题】:OpenGL Rapid Flickering Issues - and now not showing imageOpenGL 快速闪烁问题 - 现在不显示图像
【发布时间】:2012-05-30 13:36:30
【问题描述】:

我在使用 OpenGL 时遇到了一些问题。首先,我有一个正在构建的自定义控件,它封装了 OpenGL。我遇到了快速闪烁的问题,就像它在“闪烁”一样。该控件太大且太复杂,无法在此处发布,因此我制作了一个新的小型演示应用程序来演示和重新创建相同的场景。

现在的问题:我没有得到任何图像。在我的自定义控件中情况会很好,但由于我已复制代码并将其剥离到一个小型演示应用程序中,因此它不会显示图像。所以这里我有两个问题:闪烁(或闪烁),现在图像甚至没有显示。闪烁在过去没有发生,但在一些主要的代码修改后,它开始闪烁。代码更改太多,无法解释具体更改了什么,几乎所有内容。

背景显示,所以我知道它正在绘制一些东西(它是彩色的)。出于演示目的,它应该只绘制一个立方体,但我什么也没看到。我不得不将它从大约 1,000 行代码减少到甚至不到 300 行。

这不是您通常所说的闪烁,它实际上更像是一种闪烁或闪烁,想象一下汽车闪光灯的闪烁。这肯定和定时器有关,因为我设置的定时器间隔越大,它闪烁的速度就越慢。

为什么我什么都没看到?而一旦解决了这个问题,为什么它会闪烁这么多?

这是一个表单的代码,不需要 DFM:

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.

现在上面的代码与我的原始代码的工作方式有很大不同。原始的Draw 过程通过对象列表进行迭代,每个对象都包含它自己的Draw 过程。所以控件的绘制过程准备好整个场景,然后一个一个地绘制每一个'item',像这样:

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;

这是它绘制的项目之一......

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;

【问题讨论】:

  • 作为 Windows 编程的一般规则,除了响应 WM_PAINT 消息(Delphi 的 OnPaint 事件)外,您永远不应该对 DC(设备上下文)进行任何输出。这两个问题可能是由于您的代码与 Window 的默认绘图冲突造成的。您可以通过将所有绘图代码移动到 OnPaint 事件处理程序(或简单地将调用 Draw 移动到该事件)来解决此问题。
  • @KenWhite 刚刚尝试过,但这让情况变得更糟。它曾经使用计时器方法工作得很好,平滑且无闪烁。现在就好比画完就马上清空。
  • VCL 使用双缓冲(绘制到屏幕外位图,然后通过复制该位图立即更新整个表面)以避免闪烁。我自己不是 OpenGL 人,但您可能会考虑这样做。我确信某些开源代码有一个演示,您可以将其用作开始。也许罗德里戈(@RRUZ)会停下来;我认为他对 OpenGL IIRC 非常了解。
  • 谢谢,其实 DoubleBuffered 是我尝试的第一件事,但没有成功。
  • 我没有说要使用 DoubleBuffered,就像在TForm上实现的那样。我建议寻找一个做同样事情的OpenGL示例。 :)

标签: delphi opengl delphi-xe2 flicker


【解决方案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);

首先将 () 添加到您对glLoadIdentity的调用中

您加载单位矩阵,然后推送它,然后渲染和弹出它,而无需实际进行任何转换。

要么删除 3 行

glLoadIdentity;
glPushMatrix();

glPopMatrix();

或将glPushMatrix() 移动到glLoadIdentity() 之前的行

我还建议关闭着色和剔除以确保它们不会干扰。以稍微错误的顺序声明的四边形,背面剔除只是在您不希望从前面剔除它们时剔除它们。

【讨论】:

  • 是的。 :) 评论已删除。编辑为strike out关于添加()的备注;当不需要参数时,括号在 Delphi 中是可选的。
  • 说起来容易做起来难,考虑到原始代码 - glLoadIdentity 在一个地方,而另外两个在一个完全不同的地方,实际上是一个不同的单元。原始代码实际上在glLoadIdentity 之后和其他 2 之间做了更多的事情。我正在尝试移动这些代码行以查看结果.....
  • 试过了,还是不行,现在连图纸都不显示了。我添加了一些原始代码来演示此控件的实际工作方式。每个项目的 Draw 过程在场景中绘制一个单独的对象。
【解决方案2】:

已经发现了闪烁的问题,这是一个非常大的错误,我不希望您在没有整个项目的情况下就知道。问题是我创建了两个这些控件,并且它们相互干扰/相互争斗。实际上,因为 OpenGL 在逐个线程的基础上工作(或在一个“上下文”内),所以有两个不同的控件试图进行自己的绘图会干扰 - 导致两个控件同时发生相同的事情.所以闪烁是因为每个控件都试图轮流进行绘图。我提出的关于将这张图放入线程中的新问题将完美地解决这个问题。这是一个愚蠢的错误,很抱歉浪费了任何人的时间。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-07-28
    • 1970-01-01
    • 1970-01-01
    • 2018-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多