mieki256's diary



2026/01/30(金) [n年前の日記]

#1 [lazarus] LazarusでOpenGLの勉強中。その2

Windows11 x64 25H2 + Lazarus 4.4 で OpenGL が使えそうか実験中。

必要なパッケージのインストール :

Lazarus で OpenGL を使いたい場合、lazopenglcontext というパッケージをインストールすると TOpenGLControl というコントロール(GUI部品)が使えるようになって、比較的楽に OpenGL を利用できるようになるらしい。インストールの仕方をメモしておく。

プロジェクトに lazopenglcontext を追加したい場合は、プロジェクト → プロジェクトインスペクタ。

use_opengl_ss01.png


追加 → 新規の要求。

use_opengl_ss02.png


パッケージ名に「opengl」と打ち込むとリストアップされるので、lazopenglcontext を選択して「OK」。

use_opengl_ss03.png


あるいは Lazarus IDE に lazopenglcontext をインストールしてしまう手もありそう。パッケージ → パッケージをインストールもしくはアンインストール。

use_opengl_ss04.png


インストール可能、の側で「opengl」と打ち込めばリストアップされるので、lazopenglcontext を選択して、「選択対象をインストール」をクリック。その後、Rebuild IDE をクリック。ビルドが始まって、数分して終了したらIDEが自動で再起動する。

use_opengl_ss05.png

サンプルソース1 :

_OpenGL Tutorial - Free Pascal wiki

上記ページの一番最初のサンプルを動かしてみた。

_unit1.pas
_unit1.lfm
_OpenGLTest1.lpr

_unit1.pas
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils,
  FileUtil,
  Forms, Controls, Graphics, Dialogs,
  OpenGLContext, gl;  // OpenGLを使う時はコレを追加

type

  { TForm1 }

  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure GLboxPaint(Sender: TObject);
  private
    GLBox: TOpenGLControl;
  public

  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  // フォーム生成時

  // TOpenGLControl を新規作成してフォームの子にする
  GLbox := TOpenGLControl.Create(Self);
  GLbox.AutoResizeViewport := True;
  GLBox.Parent := Self;
  GLBox.MultiSampling := 4;
  GLBox.Align := alClient;  // フォームのクライアント領域全体に広げる

  // 描画処理をするプロシージャを割り当て
  // "mode delphi" の場合は "GLBox.OnPaint := GLboxPaint" にする
  GLBox.OnPaint := @GLboxPaint;

  GLBox.invalidate;
end;

procedure TForm1.GLboxPaint(Sender: TObject);
begin
  // GLBox (TOpenGLControl) 描画処理

  // 背景を消去
  glClearColor(0.27, 0.53, 0.71, 1.0); // Set blue background
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);

  glLoadIdentity;

  // 三角形を描画
  glBegin(GL_TRIANGLES);
  glColor3f(1, 0, 0);
  glVertex3f(0.0, 1.0, 0.0);
  glColor3f(0, 1, 0);
  glVertex3f(-1.0, -1.0, 0.0);
  glColor3f(0, 0, 1);
  glVertex3f(1.0, -1.0, 0.0);
  glEnd;

  // ダブルバッファ切り替え
  GLbox.SwapBuffers;
end;

end.

実行すると以下の見た目になる。OpenGL で三角形を描画できている。

opengltest1_ss01.png


Lazarus は TOpenGLControl というコントロールを使うと OpenGL を扱うのが簡単になるわけだけど、このソース内でも TOpenGLControl を新規作成してフォームに貼り付けて利用してる。また、GLBox.Align := alClient; でフォームのクライアント領域全体に TOpenGLControl が表示されるようにしている。

後は、TOpenGLControl の OnPaint で呼ばれるプロシージャ内に、OpenGLを使った描画処理を書いておけばいい。

サンプルソース2 :

Google Gemini に尋ねながら別のサンプルを書いてみた。

_Unit1.pas
_Unit1.lfm
_OpenGLTest2.lpr

_Unit1.pas
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs,
  LCLType, ExtCtrls,
  Windows, MMSystem,        // timeBeginPeriod を使うために追加
  OpenGLContext, GL, glu;   // OpenGLを使うために追加

type

  { TForm1 }

  TForm1 = class(TForm)
    OpenGLControl1: TOpenGLControl;
    Timer1: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
    procedure FormShow(Sender: TObject);
    procedure OpenGLControl1Paint(Sender: TObject);
    procedure OpenGLControl1Resize(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    fAngle: double;
    fLastTime: DWORD;
    procedure DrawCube;
    procedure ResizeGL;
    procedure SetFullscreen;
  public

  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

{ フォームが生成された時の処理 }
procedure TForm1.FormCreate(Sender: TObject);
begin
  // タイマー精度を1msにする。(Windows Only)
  timeBeginPeriod(1);
  fLastTime := timeGetTime;

  fAngle := 0.0;

  // タイマーの時間間隔を設定
  Timer1.Interval := 15;
  Timer1.Enabled := True;

  KeyPreview := True;
end;

{ フォームが破棄された時の処理 }
procedure TForm1.FormDestroy(Sender: TObject);
begin
  // タイマー精度を元に戻す。(Windows Only)
  timeEndPeriod(1);

  OpenGLControl1.Cursor := crDefault;
end;

{ フォームが表示された時の処理}
procedure TForm1.FormShow(Sender: TObject);
begin
  // フルスクリーン表示を指定
  //SetFullscreen;
  //OpenGLControl1.Cursor := crNone;

  ResizeGL;
end;

{ フルスクリーン表示を設定 }
procedure TForm1.SetFullscreen;
begin
  BorderStyle := bsNone;
  WindowState := wsFullScreen;
  //WindowState := wsMaximized;
  //BoundsRect := Screen.Monitors[0].BoundsRect;
  OpenGLControl1.Align := alClient;
end;

{ キーが押された時の処理 }
procedure TForm1.FormKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
begin
  // ESCキーで終了
  //if Key = VK_ESCAPE then
  //  Application.Terminate;

  Application.Terminate;
end;

{ 一定時間毎に呼ばれる処理。Timer1のOnTimerに割り当て }
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  // TOpenGLControlの再描画を要求
  OpenGLControl1.Invalidate;
end;

{ 箱を描画 }
procedure TForm1.DrawCube;
const
  D: double = 0.5;
begin
  glBegin(GL_QUADS);

  glColor3f(1.0, 0.0, 0.0);
  glVertex3f(-D, -D, D);
  glVertex3f(D, -D, D);
  glVertex3f(D, D, D);
  glVertex3f(-D, D, D);

  glColor3f(0.0, 1.0, 0.0);
  glVertex3f(-D, -D, -D);
  glVertex3f(-D, D, -D);
  glVertex3f(D, D, -D);
  glVertex3f(D, -D, -D);

  glColor3f(0.0, 0.0, 1.0);
  glVertex3f(-D, D, -D);
  glVertex3f(-D, D, D);
  glVertex3f(D, D, D);
  glVertex3f(D, D, -D);

  glColor3f(1.0, 1.0, 0.0);
  glVertex3f(-D, -D, -D);
  glVertex3f(D, -D, -D);
  glVertex3f(D, -D, D);
  glVertex3f(-D, -D, D);

  glColor3f(1.0, 0.0, 1.0);
  glVertex3f(D, -D, -D);
  glVertex3f(D, D, -D);
  glVertex3f(D, D, D);
  glVertex3f(D, -D, D);

  glColor3f(0.0, 1.0, 1.0);
  glVertex3f(-D, -D, -D);
  glVertex3f(-D, -D, D);
  glVertex3f(-D, D, D);
  glVertex3f(-D, D, -D);
  glEnd();
end;

{ TOpenGLControlの描画処理 }
procedure TForm1.OpenGLControl1Paint(Sender: TObject);
var
  ct: DWORD;
  dt: double;
begin
  // 前回からの時間差を取得
  ct := timeGetTime;
  dt := (ct - fLastTime) / 1000.0;  // 秒単位にする
  fLastTime := ct;

  // 角度を変更
  fAngle := fAngle + (90.0 * dt);
  //if fAngle >= 360.0 then
  //  fAngle := fAngle - 360.0;

  glEnable(GL_DEPTH_TEST);
  glShadeModel(GL_SMOOTH);

  // 背景を消去
  glClearColor(0.0, 0.0, 0.0, 1.0);
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);

  glLoadIdentity();

  // カメラを少し後ろに下げる
  glTranslatef(0.0, 0.0, -2.5);

  // 回転させる
  glRotatef(fAngle * 0.3, 1, 0, 0);
  glRotatef(fAngle, 0, 1, 0);

  // 立方体を描画
  DrawCube;

  // ダブルバッファ切り替え
  OpenGLControl1.SwapBuffers;
end;

procedure TForm1.OpenGLControl1Resize(Sender: TObject);
begin
  ResizeGL;
end;

{ ウインドウリサイズ時に行うべき処理 }
procedure TForm1.ResizeGL;
var
  Aspect: double;
begin
  if OpenGLControl1.Height <= 0 then
    Exit;

  // 透視変換をするように設定
  glViewport(0, 0, OpenGLControl1.Width, OpenGLControl1.Height);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  Aspect := OpenGLControl1.Width / OpenGLControl1.Height;
  gluPerspective(45.0, Aspect, 0.1, 100.0);
  glMatrixMode(GL_MODELVIEW);
end;

end.

実行すると以下のような見た目になる。




  • フォームには、TOpenGLControl と TTimer を貼り付けてある。
  • TTimer を使って、一定の時間間隔で TOpenGLControl の再描画を要求してる。これでアニメーションをさせることができる。
  • TOpenGLControl の再描画時、前回からの時間差を取得して、回転角度の変化量に加味している。

サンプルソース3 :

テクスチャ画像を貼ってみた。

_Unit1.pas
_Unit1.lfm
_OpenGLTest3.lpr
_texture.png
_texture2.png
_texture3.png

_Unit1.pas
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Dialogs,
  LCLType, ExtCtrls,
  Windows, MMSystem,        // timeBeginPeriod を使うために必要
  Graphics, GraphType,
  FPImage, FPReadPNG, IntfGraphics,   // 画像読み込みに必要
  OpenGLContext, GL, glu,   // OpenGLを使うために必要
  GLext;                    // GL_BGRA を記述するために必要

type

  { TForm1 }

  TForm1 = class(TForm)
    OpenGLControl1: TOpenGLControl;
    Timer1: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
    procedure FormShow(Sender: TObject);
    procedure OpenGLControl1Paint(Sender: TObject);
    procedure OpenGLControl1Resize(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    fAngle: double;
    fLastTime: DWORD;
    fTexID: GLuint;
    procedure DrawCube;
    procedure DrawPlane;
    function LoadTexture(const FileName: string): GLuint;
    function LoadTexturePx(const FileName: string): GLuint;
    procedure ResizeGL;
    procedure SetFullscreen;
  public

  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

const
  //TEXTURE_NAME: string = 'texture2.png';
  //TEXTURE_NAME: string = 'TEXTURE';
  TEXTURE_NAME: string = 'TEXTURE2';
  //TEXTURE_NAME: string = 'TEXTURE3';

{ フォームが生成される時の処理 }
procedure TForm1.FormCreate(Sender: TObject);
begin
  // タイマー精度を1msにする。(Windows Only)
  timeBeginPeriod(1);
  fLastTime := timeGetTime;

  fAngle := 0.0;

  // タイマーの時間間隔を設定
  Timer1.Interval := 15;
  Timer1.Enabled := True;

  KeyPreview := True;
end;

{ フォームが表示される時の処理 }
procedure TForm1.FormShow(Sender: TObject);
begin
  // フルスクリーン表示
  //SetFullscreen;
  //OpenGLControl1.Cursor := crNone;

  ResizeGL;

  // これを呼んでおかないとテクスチャが反映されない。ハマった…
  OpenGLControl1.MakeCurrent();

  // テクスチャを読み込み
  fTexID := LoadTexture(TEXTURE_NAME);
  //fTexID := LoadTexturePx(TEXTURE_NAME);
  if fTexID = 0 then
  begin
    ShowMessage('Error: Texture image loading failure.');
    Application.Terminate;
  end;
end;

{ フルスクリーン表示を設定 }
procedure TForm1.SetFullscreen;
begin
  BorderStyle := bsNone;
  WindowState := wsFullScreen;

  //WindowState := wsMaximized;
  //BoundsRect := Screen.Monitors[0].BoundsRect;

  OpenGLControl1.Align := alClient;
end;

{ キーが押された時の処理 }
procedure TForm1.FormKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
begin
  case Key of
    VK_ESCAPE: Application.Terminate;
    VK_R: fAngle := 0.0;
    else
      Application.Terminate;
  end;
end;

{ フォームが破棄される際の処理 }
procedure TForm1.FormDestroy(Sender: TObject);
begin
  // テクスチャを解放
  if fTexID <> 0 then
    glDeleteTextures(1, @fTexID);

  // タイマー精度を元に戻す。(Windows Only)
  timeEndPeriod(1);

  OpenGLControl1.Cursor := crDefault;
end;

{ 一定時間毎に呼ばれる処理。Timer1のOnTimerイベントに割り当て }
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  // TOpenGLControlの再描画を要求
  OpenGLControl1.Invalidate;
end;

{ 板を描画 }
procedure TForm1.DrawPlane;
begin
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, fTexID);

  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

  //glEnable(GL_ALPHA_TEST);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glEnable(GL_BLEND);
  glColor3f(1.0, 1.0, 1.0);

  glBegin(GL_QUADS);
  glTexCoord2f(0, 0);
  glVertex2f(0, 1);
  glTexCoord2f(1, 0);
  glVertex2f(1, 1);
  glTexCoord2f(1, 1);
  glVertex2f(1, 0);
  glTexCoord2f(0, 1);
  glVertex2f(0, 0);
  glEnd;

  glDisable(GL_TEXTURE_2D);
end;

{ 箱を描画 }
procedure TForm1.DrawCube;
const
  D: double = 0.5;
begin
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, fTexID);

  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

  //glEnable(GL_ALPHA_TEST);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glEnable(GL_BLEND);
  glColor3f(1.0, 1.0, 1.0);

  glBegin(GL_QUADS);
  glTexCoord2f(0.0, 1.0);
  glVertex3f(-0.5, -0.5, 0.5);
  glTexCoord2f(1.0, 1.0);
  glVertex3f(0.5, -0.5, 0.5);
  glTexCoord2f(1.0, 0.0);
  glVertex3f(0.5, 0.5, 0.5);
  glTexCoord2f(0.0, 0.0);
  glVertex3f(-0.5, 0.5, 0.5);
  glEnd();

  glBegin(GL_QUADS);
  glTexCoord2f(1.0, 1.0);
  glVertex3f(-0.5, -0.5, -0.5);
  glTexCoord2f(1.0, 0.0);
  glVertex3f(-0.5, 0.5, -0.5);
  glTexCoord2f(0.0, 0.0);
  glVertex3f(0.5, 0.5, -0.5);
  glTexCoord2f(0.0, 1.0);
  glVertex3f(0.5, -0.5, -0.5);
  glEnd();

  glBegin(GL_QUADS);
  glTexCoord2f(0.0, 0.0);
  glVertex3f(-0.5, 0.5, -0.5);
  glTexCoord2f(0.0, 1.0);
  glVertex3f(-0.5, 0.5, 0.5);
  glTexCoord2f(1.0, 1.0);
  glVertex3f(0.5, 0.5, 0.5);
  glTexCoord2f(1.0, 0.0);
  glVertex3f(0.5, 0.5, -0.5);
  glEnd();

  glBegin(GL_QUADS);
  glTexCoord2f(1.0, 0.0);
  glVertex3f(-0.5, -0.5, -0.5);
  glTexCoord2f(0.0, 0.0);
  glVertex3f(0.5, -0.5, -0.5);
  glTexCoord2f(0.0, 1.0);
  glVertex3f(0.5, -0.5, 0.5);
  glTexCoord2f(1.0, 1.0);
  glVertex3f(-0.5, -0.5, 0.5);
  glEnd();

  glBegin(GL_QUADS);
  glTexCoord2f(1.0, 1.0);
  glVertex3f(0.5, -0.5, -0.5);
  glTexCoord2f(1.0, 0.0);
  glVertex3f(0.5, 0.5, -0.5);
  glTexCoord2f(0.0, 0.0);
  glVertex3f(0.5, 0.5, 0.5);
  glTexCoord2f(0.0, 1.0);
  glVertex3f(0.5, -0.5, 0.5);
  glEnd();

  glBegin(GL_QUADS);
  glTexCoord2f(0.0, 1.0);
  glVertex3f(-0.5, -0.5, -0.5);
  glTexCoord2f(1.0, 1.0);
  glVertex3f(-0.5, -0.5, 0.5);
  glTexCoord2f(1.0, 0.0);
  glVertex3f(-0.5, 0.5, 0.5);
  glTexCoord2f(0.0, 0.0);
  glVertex3f(-0.5, 0.5, -0.5);
  glEnd();

  glDisable(GL_TEXTURE_2D);
end;

{ TOpenGLControlの描画処理 }
procedure TForm1.OpenGLControl1Paint(Sender: TObject);
const
  InitGL: boolean = False;
var
  ct: DWORD;
  dt: double;
begin
  OpenGLControl1.MakeCurrent();

  if not InitGL then
  begin
    // OpenGL関係の初期化
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
    //glShadeModel(GL_SMOOTH);
    glShadeModel(GL_FLAT);
    InitGL := True;
  end;

  // 前回からの時間差を取得
  ct := timeGetTime;
  dt := (ct - fLastTime) / 1000.0;  // 秒単位にする
  fLastTime := ct;

  // 角度を変更
  fAngle := fAngle + (90.0 * dt);
  //if fAngle >= 360.0 then
  //  fAngle := fAngle - 360.0;

  // 背景を消去
  glClearColor(0.0, 0.4, 0.2, 1.0);
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);

  glLoadIdentity();

  // カメラを少し下げる
  glTranslatef(0.0, 0.0, -2.5);

  // 回転させる
  glRotatef(fAngle * 0.3, 1, 0, 0);
  glRotatef(fAngle, 0, 1, 0);

  // 表裏チェックをしない
  glDisable(GL_CULL_FACE);

  // 透過部分のテスト種類を指定
  glAlphaFunc(GL_GREATER, 0.5);
  glEnable(GL_ALPHA_TEST);

  // 板を描画
  DrawPlane;

  // 立方体を描画
  DrawCube;

  // ダブルバッファ切り替え
  OpenGLControl1.SwapBuffers;
end;

procedure TForm1.OpenGLControl1Resize(Sender: TObject);
begin
  ResizeGL;
end;

procedure TForm1.ResizeGL;
var
  Aspect: double;
begin
  OpenGLControl1.MakeCurrent();

  if OpenGLControl1.Height <= 0 then
    Exit;

  // 透視変換をするように設定
  glViewport(0, 0, OpenGLControl1.Width, OpenGLControl1.Height);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  Aspect := OpenGLControl1.Width / OpenGLControl1.Height;
  gluPerspective(45.0, Aspect, 0.1, 100.0);
  glMatrixMode(GL_MODELVIEW);
end;

{ テクスチャ画像を読み込み。RGBA32bitにのみ対応 }
function TForm1.LoadTexture(const FileName: string): GLuint;
var
  srcimg: TPortableNetworkGraphic;
  dstimg: TLazIntfImage;
  texid: GLuint;
  rs: TResourceStream;
begin
  texid := 0;
  srcimg := TPortableNetworkGraphic.Create;
  dstimg := TLazIntfImage.Create(0, 0);
  rs := TResourceStream.Create(HINSTANCE, FileName, RT_RCDATA);

  try
    // ファイルから読み込み
    //srcimg.LoadFromFile(FileName);

    // リソースから読み込み
    srcimg.LoadFromStream(rs);

    dstimg.LoadFromBitmap(srcimg.Handle, srcimg.MaskHandle);

    glGenTextures(1, @texid);
    glBindTexture(GL_TEXTURE_2D, texid);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    if dstimg.DataDescription.Depth = 32 then
    begin
      // RGBA 32bit
      glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dstimg.Width, dstimg.Height,
        0, GL_BGRA, GL_UNSIGNED_BYTE, dstimg.PixelData);
    end
    else if dstimg.DataDescription.Depth = 24 then
    begin
      // RGB 24bit
      glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, dstimg.Width, dstimg.Height,
        0, GL_BGR, GL_UNSIGNED_BYTE, dstimg.PixelData);
    end;

    Result := texid;
  finally
    rs.Free;
    dstimg.Free;
    srcimg.Free;
  end;
end;

{ テクスチャ画像を読み込み。RGBA32bitにのみ対応。TPicture を使う版 }
function TForm1.LoadTexturePx(const FileName: string): GLuint;
var
  px: TPicture;
  texid: GLuint;
  rs: TResourceStream;
begin
  texid := 0;
  px := TPicture.Create;
  rs := TResourceStream.Create(HINSTANCE, FileName, RT_RCDATA);

  try
    // ファイルから読み込み
    //px.LoadFromFile(FileName);

    // リソースから読み込み
    px.LoadFromStream(rs);

    glGenTextures(1, @texid);
    glBindTexture(GL_TEXTURE_2D, texid);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    if px.Bitmap.RawImage.Description.Depth = 32 then
    begin
      // RGBA 32bit
      glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, px.Width, px.Height,
        0, GL_BGRA, GL_UNSIGNED_BYTE, px.Bitmap.RawImage.Data);
    end
    else if px.Bitmap.RawImage.Description.Depth = 24 then
    begin
      // RGB 24bit
      glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, px.Width, px.Height,
        0, GL_BGRA, GL_UNSIGNED_BYTE, px.Bitmap.RawImage.Data);
    end;

    Result := texid;
  finally
    rs.Free;
    px.Free;
  end;
end;

end.

実行すると以下の見た目になる。




プロジェクトのオプションで、リソースに .png を追加した。リソース種類は RCDATA、識別名は TEXTURE、TEXTURE2、TEXTURE3 で追加された。

テクスチャが反映されなくて何時間もハマった…。原因は MakeCurrent を呼んでなかったことだった。こういう罠があったとは…。

_Qt の QOpenGLWidget::makeCurrent() を徹底解説! AI時代のエンジニアがハマる罠と解決策


Lazarus でpng画像を読み込むには、TPortableNetworkGraphic を使う方法や、TPicture を使う方法があるらしい。前者は png 画像の読み込みしかできない。後者は色々な画像フォーマットに対応している。もっとも、後者も内部的には TPortableNetworkGraphic を使ってるらしいけど…。ちなみに、どちらも使い終わったら .Free を呼んでメモリを解放しないといけない。

TPortableNetworkGraphic と TPicture で、RGB24bit のpngを読み込んだ際、glTexImage2D() に渡す値が違ってくるのがよく分からんけれど…。GL_BGRA と GL_BGR、どちらを渡せばいいのか…。

以下のソースが参考になった。

_OpenGLCoreTutorials/gltex.pas at master - neurolabusc/OpenGLCoreTutorials

また、以下のドキュメントも参考になった。

_Developing with Graphics - Free Pascal wiki


チュートリアル記事の _OpenGL Tutorial - Free Pascal wiki の中では LoadGLTextureFromFile というメソッドを使ってテクスチャ画像を読み込んでいるけれど、そのメソッドを利用するには Vampyre Imaging Library とやらが必要らしい。しかし、どうやってインストールすればいいのか分からない…。

_Vampyre Imaging Library Homepage
_Vampyre Imaging Library
_galfar/imaginglib: Object Pascal image loading, saving and manipulation library.

余談。Lazarus IDEでの補完 :

Lazarus IDE上でOpenGL関係のメソッドを記述していく際、一旦「 := 」が記述されるのがちょっと気になる…。

例えば glEnable() と書きたくて途中まで打って補完(Ctrl + Space)を使うと、一旦「glEnable := ;」と補完されてしまう。「 := 」の部分を削除してから続きを打ち込まないといけない。微妙に面倒臭い。

lazarus_ide_gl_ss01.gif

以上です。

過去ログ表示

Prev - 2026/01 - Next
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project