Delphi 的 FireMonkey 框架提供強大的 3D 開發能力,讓開發者能輕鬆建立跨平台 3D 應用程式。本文從基礎的 3D 物件操作開始,逐步探討材質設定、攝影機控制、外部模型匯入以及效能最佳化等關鍵技術。文章中以建立彩色箭頭和星空模擬為例,提供程式碼片段與說明,解析 FireMonkey 3D 程式設計的核心概念。同時也探討瞭如何在 3D 環境中整合 2D 控制項,以及如何利用樣式和 StyleLookup 屬性開發平台原生風格的使用者介面,豐富應用程式的互動性和視覺效果。

在Delphi中使用FireMonkey建立3D應用程式:箭頭範例

在這篇文章中,我們將介紹如何使用Delphi的FireMonkey框架建立一個簡單的3D應用程式。我們將建立一個包含三個彩色箭頭的3D場景,並實作攝影機的旋轉和縮放功能。

建立3D場景

首先,我們需要在Delphi中建立一個新的FireMonkey 3D應用程式專案。然後,我們需要建立一個3D場景並新增三個箭頭。

  1. TDummy元件拖曳到表單上,並將其名稱更改為DummyScene
  2. TCylinder元件拖曳到表單上,並確保它屬於DummyScene元件。將其名稱更改為CylX
  3. 更改CylXHeight屬性為4,WidthDepth屬性為0.1,並將其RotationAngle.Z屬性更改為270,使其沿著X軸定位。
  4. TCone元件拖曳到表單上,並確保它屬於CylX元件。將其名稱更改為ConeX
  5. 更改ConeXHeight屬性為0.5,WidthDepth屬性為0.2,並將其Position.Y屬性更改為2,RotationAngle.X屬性更改為180。

程式碼範例:建立3D箭頭

// 建立CylX和ConeX元件
CylX := TCylinder.Create(Self);
CylX.Parent := DummyScene;
CylX.Name := 'CylX';
CylX.Height := 4;
CylX.Width := 0.1;
CylX.Depth := 0.1;
CylX.RotationAngle.Z := 270;

ConeX := TCone.Create(Self);
ConeX.Parent := CylX;
ConeX.Name := 'ConeX';
ConeX.Height := 0.5;
ConeX.Width := 0.2;
ConeX.Depth := 0.2;
ConeX.Position.Y := 2;
ConeX.RotationAngle.X := 180;

內容解密:

此段程式碼建立了兩個3D元件:圓柱體(CylX)和圓錐體(ConeX)。圓柱體代表箭頭的主體,而圓錐體代表箭頭的尖端。透過設定它們的位置和旋轉角度,我們可以建立一個指向特定方向的箭頭。

新增材質和顏色

接下來,我們需要為箭頭新增材質和顏色。

  1. 將三個TLightMaterialSource元件拖曳到表單上,並將其名稱更改為MaterialSourceXMaterialSourceYMaterialSourceZ
  2. 更改每個材質源的DiffuseColor屬性,分別設定為紅色、綠色和藍色。
  3. 將箭頭元件的MaterialSource屬性設定為對應的材質源。

程式碼範例:設定材質和顏色

// 建立材質源元件
MaterialSourceX := TLightMaterialSource.Create(Self);
MaterialSourceX.Parent := Self;
MaterialSourceX.Name := 'MaterialSourceX';
MaterialSourceX.DiffuseColor := TAlphaColorRec.Red;

MaterialSourceY := TLightMaterialSource.Create(Self);
MaterialSourceY.Parent := Self;
MaterialSourceY.Name := 'MaterialSourceY';
MaterialSourceY.DiffuseColor := TAlphaColorRec.Green;

MaterialSourceZ := TLightMaterialSource.Create(Self);
MaterialSourceZ.Parent := Self;
MaterialSourceZ.Name := 'MaterialSourceZ';
MaterialSourceZ.DiffuseColor := TAlphaColorRec.Blue;

// 設定箭頭元件的材質源
CylX.MaterialSource := MaterialSourceX;
ConeX.MaterialSource := MaterialSourceX;

內容解密:

此段程式碼建立了三個材質源元件,並將其顏色設定為紅色、綠色和藍色。然後,將箭頭元件的材質源屬性設定為對應的材質源,使箭頭呈現不同的顏色。

實作攝影機旋轉和縮放

最後,我們需要實作攝影機的旋轉和縮放功能。

  1. TViewport3D元件拖曳到表單上,並將其UsingDesignCamera屬性設定為False。
  2. 將攝影機元件拖曳到表單上,並將其與檢視埠的Camera屬性關聯。
  3. 在檢視埠的OnMouseDownOnMouseMove事件中新增程式碼,以實作攝影機的旋轉。
  4. 在檢視埠的OnMouseWheel事件中新增程式碼,以實作攝影機的縮放。

程式碼範例:實作攝影機旋轉和縮放

procedure TFormArrows3D.Viewport3D1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
begin
  FDown := PointF(X, Y);
end;

procedure TFormArrows3D.Viewport3D1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Single);
begin
  if (ssLeft in Shift) then
  begin
    DummyCamera.RotationAngle.X := DummyCamera.RotationAngle.X - ((Y - FDown.Y) * ROTATION_STEP);
    DummyCamera.RotationAngle.Y := DummyCamera.RotationAngle.Y + ((X - FDown.X) * ROTATION_STEP);
    FDown := PointF(X, Y);
  end;
end;

procedure TFormArrows3D.Viewport3D1MouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; var Handled: Boolean);
begin
  DoZoom(WheelDelta > 0);
end;

procedure TFormArrows3D.DoZoom(AIn: Boolean);
var
  NewZ: Single;
begin
  if AIn then
    NewZ := CameraZ.Position.Z + ZOOM_STEP
  else
    NewZ := CameraZ.Position.Z - ZOOM_STEP;
  if (NewZ < CAMERA_MAX_Z) and (NewZ > CAMERA_MIN_Z) then
    CameraZ.Position.Z := NewZ;
end;

內容解密:

此段程式碼實作了攝影機的旋轉和縮放功能。透過在檢視埠的滑鼠事件中新增程式碼,我們可以控制攝影機的角度和距離,從而實作場景的旋轉和縮放。

在Delphi中使用FireMonkey進行3D視覺化開發

FireMonkey是一個功能強大的跨平台UI框架,不僅支援2D應用程式的開發,也提供了豐富的3D功能,讓開發者能夠建立出色的3D視覺化效果。在本篇文章中,我們將探討如何使用FireMonkey進行3D視覺化開發,包括基本的3D物件操作、匯入外部3D模型以及最佳化多個3D物件的渲染。

互動式3D視覺化基礎

首先,我們來構建一個簡單的互動式3D視覺化應用,使用“攝影機在自拍棒上”的方法。這種方法透過移動攝影機來觀察3D場景中的物件。

程式碼範例:實作旋轉與縮放

if (not (TInteractiveGestureFlag.gfBegin in EventInfo.Flags)) 
and (not (TInteractiveGestureFlag.gfEnd in EventInfo.Flags)) 
then
begin
  Delta := EventInfo.Distance - FLastDistance;
  DoZoom(Delta > 0);
end;
FLastDistance := EventInfo.Distance;

內容解密:

  1. 檢查手勢事件是否處於開始或結束狀態,如果不是,則計算距離變化並根據變化進行縮放操作。
  2. EventInfo.Distance表示當前手勢事件的距離,FLastDistance儲存上一次事件的距離。
  3. DoZoom(Delta > 0)根據距離變化決定是否進行縮放操作,Delta > 0表示向內縮放,否則向外縮放。

使用外部3D模型

FireMonkey支援匯入外部3D模型,如OBJ、DAE和ASE格式。這些模型可以使用專業的3D建模軟體建立,並透過TModel3D元件匯入到Delphi專案中。

匯入3D模型步驟

  1. 將TModel3D元件放置在表單上,並確保它被歸屬於DummyScene物件。
  2. 使用Mesh Collection Editor載入OBJ檔案。
  3. 調整模型的比例和方向,使其正確顯示。

程式碼範例:動態設定材質

procedure TFormHelmet.FormCreate(Sender: TObject);
begin
  for var Mesh: TMesh in Model3D1.MeshCollection do
    mesh.MaterialSource := LightMaterialSource1;
end;

內容解密:

  1. 在表單建立事件中,遍歷Model3D1的所有網格(Mesh),並將其材質來源設定為LightMaterialSource1
  2. 這樣可以確保匯入的3D模型具有正確的材質和光照效果。

星空模擬:最佳化多個3D物件的渲染

為了提高多個3D物件的渲染效率,FireMonkey提供了TObjectProxy元件,用於共用網格資料。我們透過建立一個星空模擬來示範這一技術。

實作步驟

  1. 建立一個TSphere元件作為原始物件,並將其位置設定在攝影機後方,使其不可見。
  2. 使用TObjectProxy元件建立多個代理物件,分享TSphere的網格資料。
  3. 設定代理物件的位置和材質,以模擬星空效果。

使用TObjectProxy的好處

  • 減少記憶體佔用:多個代理物件共用同一個網格資料,避免了重複資料的儲存。
  • 提高渲染效率:減少了需要渲染的獨立網格數量,提高了整體效能。

FireMonkey 3D 程式設計與 2D/3D 混合應用

在前一章中,我們探討了 FireMonkey 的 3D 程式設計基礎,並建立了一個簡單的星空模擬應用。本章將進一步深入 FireMonkey 的 3D 功能,並展示如何在 3D 環境中混合使用 2D 控制項,以實作更豐富的視覺效果和使用者互動體驗。

建立星空模擬應用

首先,我們將建立一個星空模擬應用,透過移動虛擬的星星來模擬太空旅行的視覺效果。

步驟一:建立 3D 場景

  1. 建立一個新的多裝置 Delphi 專案,並選擇空白應用程式。
  2. 在表單上放置一個 TDummyScene 元件,用於容納我們的星空場景。
  3. 新增一個 TSphere 元件作為星星的原型,並將其命名為 SphereStar

步驟二:動態建立星星

我們將撰寫程式碼來動態建立多個星星,並將它們隨機分佈在攝影機前方。

function TFormStars.RandomLocation: TPoint3D;
const
  MAX_X = 50;
  MAX_Y = 50;
  MAX_Z = 200;
begin
  Result.X := -MAX_X + Random * 2 * MAX_X;
  Result.Y := -MAX_Y + Random * 2 * MAX_Y;
  Result.Z := Random * MAX_Z;
end;

procedure TFormStars.FormCreate(Sender: TObject);
const
  STARS_COUNT = 100;
var
  Star: TProxyObject;
begin
  Randomize;
  for var I := 0 to STARS_COUNT-1 do
  begin
    Star := TProxyObject.Create(DummyScene);
    Star.SourceObject := SphereStar;
    Star.Parent := DummyScene;
    Star.Position.Point := RandomLocation;
  end;
end;

#### 內容解密:

  • TFormStars.RandomLocation 方法用於生成一個隨機的 3D 座標,限制在定義的立方體範圍內。
  • TFormStars.FormCreate 事件處理器在表單建立時被呼叫,用於初始化星空場景,建立多個 TProxyObject 物件作為星星,並將它們隨機放置在攝影機前方。

步驟三:新增星空移動效果

為了模擬太空旅行的視覺效果,我們將使用 TTimer 元件來更新星星的位置,使其看起來像是在移動。

procedure TFormStars.Timer1Timer(Sender: TObject);
const
  DELTA_Z = 2;
var
  Ctrl: TControl3D;
  Obj: TFmxObject;
begin
  for var I := 0 to DummyScene.ChildrenCount-1 do
  begin
    Obj := DummyScene.Children[I];
    if Obj is TControl3D then
    begin
      Ctrl := TControl3D(Obj);
      Ctrl.Position.Z := Ctrl.Position.Z - DELTA_Z;
      if Ctrl.Position.Z < 0 then
        Ctrl.Position.Point := RandomLocation;
    end;
  end;
end;

#### 內容解密:

  • TFormStars.Timer1Timer 事件處理器在 TTimer 元件觸發時被呼叫,用於更新所有在 DummyScene 中的星星位置,使其沿 Z 軸移動。
  • 當星星移動到攝影機前方以外(Z 軸座標小於 0),將其重新定位到隨機位置,以維持星空的效果。

在 3D 環境中混合使用 2D 控制項

FireMonkey 允許在 3D 表單中使用 2D 使用者介面控制項,或在標準的 FireMonkey 2D 表單中嵌入特殊的 TViewport3D 元件來進行 3D 渲染。接下來,我們將展示如何建立一個應用,在其中混合使用 2D 和 3D 控制項,以實作有趣的視覺效果。

TwistMe 應用範例

  1. 建立一個新的多裝置 Delphi 專案,並選擇空白應用程式。
  2. 在表單上放置一個 TViewport3D 元件,並將其對齊到客戶端。
  3. TViewport3D 上放置一個 TLayer3D 元件,將其 Projection 屬性設為 Screen,並對齊到客戶端。
  4. TLayer3D 上放置一個 TButton 元件,並設定其文字為 “Click me to twist!"。
procedure TFormTwistMe.Button1Click(Sender: TObject);
begin
  TAnimator.AnimateFloat(Layer3D1, 'RotationAngle.X', 360, 1);
end;

#### 內容解密:

  • 當使用者點選按鈕時,TFormTwistMe.Button1Click 事件處理器被呼叫,使用 TAnimator.AnimateFloat 方法來動畫化 Layer3D1RotationAngle.X 屬性,使整個層繞 X 軸旋轉 360 度,持續時間為 1 秒。

使用樣式建立使用者介面

FireMonkey 控制項的外觀和感覺完全取決於樣式。相同的應用程式碼函式庫在不同平台上編譯時,會使用不同的樣式來呈現控制項。這使得相同的應用程式在 iOS 上執行時,看起來像一個標準的 iOS 應用程式,而在 Android 上編譯時,則看起來像一個 Android 應用程式。

使用內建樣式

Delphi 提供了內建的樣式。當您建立一個新的多裝置應用程式時,在設計階段,可以預覽不同樣式下的表單外觀。

嘗試使用不同樣式

  1. 建立一個新的多裝置空白應用程式,將主表單單元儲存為 uFormStylesTest,將整個專案儲存為 StylesTest
  2. 在表單設計區域上方,有一個樣式下拉式選單,可以變更用於預覽表單的樣式。

在樣式下拉式選單中選擇不同的樣式,可以將不同的樣式套用到正在使用的表單上,以便檢視在特定平台上編譯時的表單外觀。

實驗:變更樣式

  1. TToolBar 元件拖曳到表單上,然後將 TSpeedButton 控制項放到工具列上。
  2. 將快速按鈕對齊到左側,並使其稍微寬一些,以便顯示完整的標題。

當我們將樣式下拉式選單中的樣式從 Android 切換到 iOS 時,可以看到快速按鈕的外觀和感覺會相應地改變,以符合所選平台的設計。

使用 StyleLookup 屬性

FireMonkey 控制項具有 StyleLookup 屬性,可以用來套用不同的樣式定義。點選 StyleLookup 屬性旁邊的下拉按鈕,可以檢視可以套用到 TSpeedButton 控制項的不同樣式。

不同平台的 StyleLookup 選項

  • 在 Android 平台上,TSpeedButton 控制項的 StyleLookup 選項如圖 7.3 所示。
  • 在 iOS 平台上,TSpeedButton 控制項的 StyleLookup 選項如圖 7.4 所示。

如果選擇 drawertoolbutton 作為 StyleLookup 屬性,則快速按鈕的大小會改變,變成一個正方形。此時,它看起來像許多行動應用程式中常見的“選單”按鈕。

新增更多控制項

讓我們新增更多控制項到表單中,以進一步體驗樣式的變化:

  1. TLabel 元件拖曳到工具列上,將其對齊到客戶端,並將其 StyleLookup 屬性更改為 toollabel
  2. TTabControl 元件新增到表單中,並將其對齊到客戶端。
  3. TTabControl 上點選滑鼠右鍵,並三次選擇新增 TTabItem 選項,以新增三個索引標籤到表單中。

TTabControl 的 TabPosition 屬性

  • TTabControlTabPosition 屬性預設為 PlatformDefault,但可以更改為 TopBottom
  • 在 iOS 上,索引標籤位於螢幕底部,而在 Android 上,索引標籤位於螢幕頂部。

新增錶盤控制項

下一步是新增三個錶盤控制項,以研究函式庫提供的不同選項:

  1. TArcDial 控制項拖曳到第一個索引標籤上,將其高度和寬度屬性更改為 100。
  2. 複製並貼上此控制項兩次,以便在表單上有三個弧形錶盤控制項。
  3. 為了展示不同的控制項,也將 TCheckBoxTSwitchTButton 元件拖曳到表單上。

圖 7.7:具有 iOS 樣式的表單

在圖 7.7 中,可以看到具有 iOS 樣式的表單外觀。

程式碼範例:使用 StyleLookup 屬性

// 設定 TSpeedButton 的 StyleLookup 屬性
SpeedButton1.StyleLookup := 'drawertoolbutton';

內容解密:

此程式碼設定了 TSpeedButton 控制項的 StyleLookup 屬性為 'drawertoolbutton',使其外觀變更為指定的樣式。此設定可以根據需要動態變更,以實作不同的視覺效果。

FireMonkey 控制項樣式架構

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Delphi FireMonkey 3D 應用程式開發

package "機器學習流程" {
    package "資料處理" {
        component [資料收集] as collect
        component [資料清洗] as clean
        component [特徵工程] as feature
    }

    package "模型訓練" {
        component [模型選擇] as select
        component [超參數調優] as tune
        component [交叉驗證] as cv
    }

    package "評估部署" {
        component [模型評估] as eval
        component [模型部署] as deploy
        component [監控維護] as monitor
    }
}

collect --> clean : 原始資料
clean --> feature : 乾淨資料
feature --> select : 特徵向量
select --> tune : 基礎模型
tune --> cv : 最佳參數
cv --> eval : 訓練模型
eval --> deploy : 驗證模型
deploy --> monitor : 生產模型

note right of feature
  特徵工程包含:
  - 特徵選擇
  - 特徵轉換
  - 降維處理
end note

note right of eval
  評估指標:
  - 準確率/召回率
  - F1 Score
  - AUC-ROC
end note

@enduml

圖表翻譯: 此圖表展示了 FireMonkey 控制項如何使用不同的樣式(iOS 和 Android),並透過 StyleLookup 屬性來變更控制項的外觀。最終實作了跨平台應用的介面適配。