Delphi 的 FireMonkey 框架提供強大的 3D 開發能力,讓開發者能輕鬆建立跨平台 3D 應用程式。本文從基礎的 3D 物件操作開始,逐步探討材質設定、攝影機控制、外部模型匯入以及效能最佳化等關鍵技術。文章中以建立彩色箭頭和星空模擬為例,提供程式碼片段與說明,解析 FireMonkey 3D 程式設計的核心概念。同時也探討瞭如何在 3D 環境中整合 2D 控制項,以及如何利用樣式和 StyleLookup 屬性開發平台原生風格的使用者介面,豐富應用程式的互動性和視覺效果。
在Delphi中使用FireMonkey建立3D應用程式:箭頭範例
在這篇文章中,我們將介紹如何使用Delphi的FireMonkey框架建立一個簡單的3D應用程式。我們將建立一個包含三個彩色箭頭的3D場景,並實作攝影機的旋轉和縮放功能。
建立3D場景
首先,我們需要在Delphi中建立一個新的FireMonkey 3D應用程式專案。然後,我們需要建立一個3D場景並新增三個箭頭。
- 將
TDummy元件拖曳到表單上,並將其名稱更改為DummyScene。 - 將
TCylinder元件拖曳到表單上,並確保它屬於DummyScene元件。將其名稱更改為CylX。 - 更改
CylX的Height屬性為4,Width和Depth屬性為0.1,並將其RotationAngle.Z屬性更改為270,使其沿著X軸定位。 - 將
TCone元件拖曳到表單上,並確保它屬於CylX元件。將其名稱更改為ConeX。 - 更改
ConeX的Height屬性為0.5,Width和Depth屬性為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)。圓柱體代表箭頭的主體,而圓錐體代表箭頭的尖端。透過設定它們的位置和旋轉角度,我們可以建立一個指向特定方向的箭頭。
新增材質和顏色
接下來,我們需要為箭頭新增材質和顏色。
- 將三個
TLightMaterialSource元件拖曳到表單上,並將其名稱更改為MaterialSourceX、MaterialSourceY和MaterialSourceZ。 - 更改每個材質源的
DiffuseColor屬性,分別設定為紅色、綠色和藍色。 - 將箭頭元件的
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;
內容解密:
此段程式碼建立了三個材質源元件,並將其顏色設定為紅色、綠色和藍色。然後,將箭頭元件的材質源屬性設定為對應的材質源,使箭頭呈現不同的顏色。
實作攝影機旋轉和縮放
最後,我們需要實作攝影機的旋轉和縮放功能。
- 將
TViewport3D元件拖曳到表單上,並將其UsingDesignCamera屬性設定為False。 - 將攝影機元件拖曳到表單上,並將其與檢視埠的
Camera屬性關聯。 - 在檢視埠的
OnMouseDown和OnMouseMove事件中新增程式碼,以實作攝影機的旋轉。 - 在檢視埠的
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;
內容解密:
- 檢查手勢事件是否處於開始或結束狀態,如果不是,則計算距離變化並根據變化進行縮放操作。
EventInfo.Distance表示當前手勢事件的距離,FLastDistance儲存上一次事件的距離。DoZoom(Delta > 0)根據距離變化決定是否進行縮放操作,Delta > 0表示向內縮放,否則向外縮放。
使用外部3D模型
FireMonkey支援匯入外部3D模型,如OBJ、DAE和ASE格式。這些模型可以使用專業的3D建模軟體建立,並透過TModel3D元件匯入到Delphi專案中。
匯入3D模型步驟
- 將TModel3D元件放置在表單上,並確保它被歸屬於DummyScene物件。
- 使用Mesh Collection Editor載入OBJ檔案。
- 調整模型的比例和方向,使其正確顯示。
程式碼範例:動態設定材質
procedure TFormHelmet.FormCreate(Sender: TObject);
begin
for var Mesh: TMesh in Model3D1.MeshCollection do
mesh.MaterialSource := LightMaterialSource1;
end;
內容解密:
- 在表單建立事件中,遍歷
Model3D1的所有網格(Mesh),並將其材質來源設定為LightMaterialSource1。 - 這樣可以確保匯入的3D模型具有正確的材質和光照效果。
星空模擬:最佳化多個3D物件的渲染
為了提高多個3D物件的渲染效率,FireMonkey提供了TObjectProxy元件,用於共用網格資料。我們透過建立一個星空模擬來示範這一技術。
實作步驟
- 建立一個TSphere元件作為原始物件,並將其位置設定在攝影機後方,使其不可見。
- 使用TObjectProxy元件建立多個代理物件,分享TSphere的網格資料。
- 設定代理物件的位置和材質,以模擬星空效果。
使用TObjectProxy的好處
- 減少記憶體佔用:多個代理物件共用同一個網格資料,避免了重複資料的儲存。
- 提高渲染效率:減少了需要渲染的獨立網格數量,提高了整體效能。
FireMonkey 3D 程式設計與 2D/3D 混合應用
在前一章中,我們探討了 FireMonkey 的 3D 程式設計基礎,並建立了一個簡單的星空模擬應用。本章將進一步深入 FireMonkey 的 3D 功能,並展示如何在 3D 環境中混合使用 2D 控制項,以實作更豐富的視覺效果和使用者互動體驗。
建立星空模擬應用
首先,我們將建立一個星空模擬應用,透過移動虛擬的星星來模擬太空旅行的視覺效果。
步驟一:建立 3D 場景
- 建立一個新的多裝置 Delphi 專案,並選擇空白應用程式。
- 在表單上放置一個
TDummyScene元件,用於容納我們的星空場景。 - 新增一個
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 應用範例
- 建立一個新的多裝置 Delphi 專案,並選擇空白應用程式。
- 在表單上放置一個
TViewport3D元件,並將其對齊到客戶端。 - 在
TViewport3D上放置一個TLayer3D元件,將其Projection屬性設為Screen,並對齊到客戶端。 - 在
TLayer3D上放置一個TButton元件,並設定其文字為 “Click me to twist!"。
procedure TFormTwistMe.Button1Click(Sender: TObject);
begin
TAnimator.AnimateFloat(Layer3D1, 'RotationAngle.X', 360, 1);
end;
#### 內容解密:
- 當使用者點選按鈕時,
TFormTwistMe.Button1Click事件處理器被呼叫,使用TAnimator.AnimateFloat方法來動畫化Layer3D1的RotationAngle.X屬性,使整個層繞 X 軸旋轉 360 度,持續時間為 1 秒。
使用樣式建立使用者介面
FireMonkey 控制項的外觀和感覺完全取決於樣式。相同的應用程式碼函式庫在不同平台上編譯時,會使用不同的樣式來呈現控制項。這使得相同的應用程式在 iOS 上執行時,看起來像一個標準的 iOS 應用程式,而在 Android 上編譯時,則看起來像一個 Android 應用程式。
使用內建樣式
Delphi 提供了內建的樣式。當您建立一個新的多裝置應用程式時,在設計階段,可以預覽不同樣式下的表單外觀。
嘗試使用不同樣式
- 建立一個新的多裝置空白應用程式,將主表單單元儲存為
uFormStylesTest,將整個專案儲存為StylesTest。 - 在表單設計區域上方,有一個樣式下拉式選單,可以變更用於預覽表單的樣式。
在樣式下拉式選單中選擇不同的樣式,可以將不同的樣式套用到正在使用的表單上,以便檢視在特定平台上編譯時的表單外觀。
實驗:變更樣式
- 將
TToolBar元件拖曳到表單上,然後將TSpeedButton控制項放到工具列上。 - 將快速按鈕對齊到左側,並使其稍微寬一些,以便顯示完整的標題。
當我們將樣式下拉式選單中的樣式從 Android 切換到 iOS 時,可以看到快速按鈕的外觀和感覺會相應地改變,以符合所選平台的設計。
使用 StyleLookup 屬性
FireMonkey 控制項具有 StyleLookup 屬性,可以用來套用不同的樣式定義。點選 StyleLookup 屬性旁邊的下拉按鈕,可以檢視可以套用到 TSpeedButton 控制項的不同樣式。
不同平台的 StyleLookup 選項
- 在 Android 平台上,
TSpeedButton控制項的StyleLookup選項如圖 7.3 所示。 - 在 iOS 平台上,
TSpeedButton控制項的StyleLookup選項如圖 7.4 所示。
如果選擇 drawertoolbutton 作為 StyleLookup 屬性,則快速按鈕的大小會改變,變成一個正方形。此時,它看起來像許多行動應用程式中常見的“選單”按鈕。
新增更多控制項
讓我們新增更多控制項到表單中,以進一步體驗樣式的變化:
- 將
TLabel元件拖曳到工具列上,將其對齊到客戶端,並將其StyleLookup屬性更改為toollabel。 - 將
TTabControl元件新增到表單中,並將其對齊到客戶端。 - 在
TTabControl上點選滑鼠右鍵,並三次選擇新增TTabItem選項,以新增三個索引標籤到表單中。
TTabControl 的 TabPosition 屬性
TTabControl的TabPosition屬性預設為PlatformDefault,但可以更改為Top或Bottom。- 在 iOS 上,索引標籤位於螢幕底部,而在 Android 上,索引標籤位於螢幕頂部。
新增錶盤控制項
下一步是新增三個錶盤控制項,以研究函式庫提供的不同選項:
- 將
TArcDial控制項拖曳到第一個索引標籤上,將其高度和寬度屬性更改為 100。 - 複製並貼上此控制項兩次,以便在表單上有三個弧形錶盤控制項。
- 為了展示不同的控制項,也將
TCheckBox、TSwitch和TButton元件拖曳到表單上。
圖 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 屬性來變更控制項的外觀。最終實作了跨平台應用的介面適配。