You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi结合OpenGL加载PNG为2D纹理并绘制的问题

解决Delphi中OpenGL加载PNG纹理失败的问题

问题根源分析

你当前的PNG加载代码存在几个关键错误:

  • 错误使用png.AlphaScanline,这个属性仅返回单独的Alpha通道数据,而非完整的RGBA像素信息,导致OpenGL拿到的不是有效图像数据
  • 未开启OpenGL的透明混合功能,即使加载了带Alpha通道的纹理,透明效果也无法生效
  • 图像行顺序不匹配:Delphi的Scanline[0]对应图像顶部,而OpenGL纹理原点在左下角,直接传入会导致图像倒置
  • DrawScene中直接访问已释放的png对象(你在InitOpenGL的finally块里Free了png),属于非法内存访问

修正后的完整代码

1. 补充窗体类成员变量

在TForm1类声明中添加:

private
  hdc: HDC;
  hrc: HGLRC;
  renderImage: GLuint;
  pngWidth, pngHeight: Integer; // 保存PNG宽高,避免访问已释放对象

2. 修正InitOpenGL过程

procedure TForm1.InitOpenGL;
var
  PixelFormat: GLuint;
  pfd: TPixelFormatDescriptor;
  png: TPngImage;
  Bitmap: TBitmap;
  Buffer: PByte;
  I: Integer;
  SrcRow, DstRow: PByte;
begin
  ZeroMemory(@pfd, SizeOf(pfd));
  pfd.nSize := SizeOf(pfd);
  pfd.nVersion := 1;
  pfd.dwFlags := PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL or PFD_DOUBLEBUFFER;
  pfd.iPixelType := PFD_TYPE_RGBA;
  pfd.cColorBits := 32;
  pfd.cDepthBits := 24;
  pfd.iLayerType := PFD_MAIN_PLANE;

  hdc := GetDC(Handle);
  PixelFormat := ChoosePixelFormat(hdc, @pfd);
  SetPixelFormat(hdc, PixelFormat, @pfd);

  hrc := wglCreateContext(hdc);
  wglMakeCurrent(hdc, hrc);

  // 启用纹理和透明混合
  glEnable(GL_TEXTURE_2D);
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // 标准透明混合模式

  glGenTextures(1, @renderImage);
  glBindTexture(GL_TEXTURE_2D, renderImage);

  png := TPngImage.Create;
  Bitmap := TBitmap.Create;
  try
    png.LoadFromFile('image.png');
    pngWidth := png.Width;
    pngHeight := png.Height;

    // 将PNG转换为32位带Alpha的Bitmap,统一像素格式
    Bitmap.PixelFormat := pf32bit;
    Bitmap.Width := png.Width;
    Bitmap.Height := png.Height;
    Bitmap.Canvas.Draw(0, 0, png);

    // 分配缓冲区,转换行顺序适配OpenGL纹理原点
    GetMem(Buffer, pngWidth * pngHeight * 4);
    try
      for I := 0 to pngHeight - 1 do
      begin
        SrcRow := Bitmap.ScanLine[I]; // 源行从顶部开始
        DstRow := Buffer + (pngHeight - 1 - I) * pngWidth * 4; // 目标行从底部开始
        Move(SrcRow^, DstRow^, pngWidth * 4);
      end;

      // 加载纹理到OpenGL(Delphi 32位Bitmap是BGRA格式)
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pngWidth, pngHeight, 0,
        GL_BGRA, GL_UNSIGNED_BYTE, Buffer);
    finally
      FreeMem(Buffer);
    end;
  finally
    Bitmap.Free;
    png.Free;
  end;

  // 设置纹理过滤参数
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

  glClearColor(0.0, 0.0, 0.0, 0.0);
end;

3. 修正DrawScene过程

procedure TForm1.DrawScene;
begin
  glClear(GL_COLOR_BUFFER_BIT);

  glBindTexture(GL_TEXTURE_2D, renderImage);

  // 绘制居中的纹理四边形
  glBegin(GL_QUADS);
    glTexCoord2f(0, 0); glVertex2f(-pngWidth/Width, -pngHeight/Height);
    glTexCoord2f(1, 0); glVertex2f(pngWidth/Width, -pngHeight/Height);
    glTexCoord2f(1, 1); glVertex2f(pngWidth/Width, pngHeight/Height);
    glTexCoord2f(0, 1); glVertex2f(-pngWidth/Width, pngHeight/Height);
  glEnd;

  SwapBuffers(hdc);
end;

批量裁剪绘制优化建议

如果需要从大PNG中裁剪多个小区域绘制,建议:

  • 一次性加载大PNG到单个OpenGL纹理,避免重复加载多个小PNG
  • 提前计算每个裁剪区域的纹理UV坐标(基于大纹理的宽高比例),绘制时复用同一个纹理ID,仅修改glTexCoord2f的坐标值和顶点位置,大幅提升绘制效率

内容的提问来源于stack exchange,提问作者brane

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 09:27:07