Delphi FMXでTMeshにテクスチャを貼る方法|3Dメッシュ描画の実装例
DelphiのFMX環境では、TMeshコンポーネントを使って3Dメッシュにテクスチャを貼り付けることができます。
本ページでは、Viewport3D上にTMeshを配置し、LightMaterialSourceを使ってテクスチャ画像をマッピングする方法を詳しく解説します。
WrapModeの設定や頂点バッファの生成、テクスチャ座標の指定方法など、3D描画に必要な手順をサンプルコード付きで紹介しています。
Delphiで3Dグラフィックスを扱いたい方は、ぜひ参考にしてください。
Delphiを起動して新規作成を行い、必要なコンポーネントをドラッグ&ドロップする
Delphi起動⇒ファイル⇒新規作成⇒マルチデバイスアプリケーションを選択し、「空のアプリケーション」を選択してOKボタンをクリックします。
TTimer、TViewPort3D、TColorMaterialSource、TLightMaterialSourceをフォームへドラッグ&ドロップします。
ViewPort3D1にTLightをドラッグ&ドロップし、Light1の方向を適当に左下へ向けます。
ViewPort3D1にTMeshをドラッグ&ドロップします。
Timer1のプロパティ[Interval]を33にします。
ソースコードを記述する
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Types3D,
System.Math.Vectors, FMX.Objects3D, FMX.Controls3D, FMX.MaterialSources,
FMX.Viewport3D, System.RTLConsts;
type
TForm1 = class(TForm)
Timer1: TTimer;
Viewport3D1: TViewport3D;
ColorMaterialSource1: TColorMaterialSource;
LightMaterialSource1: TLightMaterialSource;
Light1: TLight;
Mesh1: TMesh;
procedure Timer1Timer(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ private 宣言 }
public
{ public 宣言 }
phase:single; //位相
procedure DrawWire(Sender: TObject; Context: TContext3D);
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
procedure TForm1.DrawWire(Sender: TObject; Context: TContext3D);
begin
//線を描画する
Context.DrawLines(
TMesh(Sender).Data.VertexBuffer,
TMesh(Sender).Data.IndexBuffer,
ColorMaterialSource1.Material, 1
);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
phase:=0;
//マテリアル(材質)設定
Mesh1.MaterialSource:=LightMaterialSource1;
//テクスチャの読み込み
LightMaterialSource1.Texture.LoadFromFile('..\..\texture.jpg');
//両面にする場合
//Mesh1.TwoSide:=true;
//TMeshWrapMode.Fit
// TMeshのWidth,Height,Depthの中に収まるようにフィット
// 比率は保たれ、原点は自動計算される
//TMeshWrapMode.Original
// TMeshのWidth,Height,Depth倍された大きさになり、原点は0,0,0
//TMeshWrapMode.Resize
// 基本はFitと同じだが原点は0,0,0
//TMeshWrapMode.Stretch
// TMeshのWidth,Height,Depthの中に収まるようにストレッチされ
// 比率は保たれない、原点は自動計算される
Mesh1.WrapMode:=TMeshWrapMode.Resize;
Mesh1.Width:=4;
Mesh1.Depth:=4;
Mesh1.Height:=4;
Mesh1.Position.X:=0;
Mesh1.Position.Y:=-2;//少し上の位置にする
Mesh1.Position.Z:=0;
Mesh1.RotationAngle.X:=30;//30度回転
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var xp,zp:integer;
x,z:integer;
y:single;
idx:integer;
fTexCoordinates:String;//テクスチャの貼り方を入れる
begin
xp:=20;//x方向のメッシュの数+1
zp:=15;//z方向のメッシュの数+1
Mesh1.Data.VertexBuffer.Length := xp*zp;
Mesh1.Data.IndexBuffer.Length := (xp-1)*(zp-1)*3*2;
fTexCoordinates:='';
//頂点バッファの作成
for z := 0 to zp-1 do
begin
for x := 0 to xp-1 do
begin
//y座標(高さ)を適当にCos*Sinにする
y:=Cos(x/xp*pi()*4+phase)*Sin(z/zp*pi()*4+phase)*2;
//頂点バッファに値を設定
Mesh1.Data.VertexBuffer.Vertices[x+z*xp]:=
Point3D(x-xp/2, y, z-zp/2);
//各頂点に対してテクスチャの位置(割合)を指定していく(x:0~1 y:0~1)
fTexCoordinates:=fTexcoordinates+
Format('%4.4f %4.4f,',[x/(xp-1),1-z/(zp-1)]);
end;
end;
//頂点に対するテクスチャの位置を指定する
Mesh1.Data.TexCoordinates:=fTexCoordinates;
//メッシュの作成
idx:=0;
for z := 0 to zp-2 do
begin
for x := 0 to xp-2 do
begin
//3角形ポリゴン作成(頂点を結ぶ)
Mesh1.Data.IndexBuffer.Indices[idx]:=x+0+(z+0)*xp;
inc(idx);
Mesh1.Data.IndexBuffer.Indices[idx]:=x+0+(z+1)*xp;
inc(idx);
Mesh1.Data.IndexBuffer.Indices[idx]:=x+1+(z+0)*xp;
inc(idx);
//3角形ポリゴン作成(頂点を結ぶ)
Mesh1.Data.IndexBuffer.Indices[idx]:=x+1+(z+0)*xp;
inc(idx);
Mesh1.Data.IndexBuffer.Indices[idx]:=x+0+(z+1)*xp;
inc(idx);
Mesh1.Data.IndexBuffer.Indices[idx]:=x+1+(z+1)*xp;
inc(idx);
end;
end;
//法線ベクトルの自動計算
//メッシュのすべての面に対する法線ベクトルを計算します
Mesh1.Data.CalcFaceNormals();
//メッシュのすべての面の滑らかな法線ベクトルを計算します
//Mesh1.Data.CalcSmoothNormals();
//メッシュのすべての面の接線ベクトルと従法線ベクトルを計算します
//Mesh1.Data.CalcTangentBinormals;
//稜線(ワイヤーフレーム)の描画、必要であればコメントを外す
//Mesh1.OnRender:=DrawWire;
//位相を変える
phase:=phase+0.1;
//再描画
Viewport3D1.Repaint;
end;
end.
実行する
実行ボタンを押して実行します。(デバッグ実行でもOKです。)
3Dメッシュにテクスチャ画像が適用されます。
