【A4】Delphi/C++テクニカルセッション

FireMonkey の仕組み
慶應義塾大学 藤代研究室 ＆ 玉泉山 安国院
中山 雅紀

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

サンプルプログラム• 講演内で紹介するプログラムはダウンロード可能– Delphi XE7 のプロジェクト

http://www.luxidea.net/XAUYEZPG

2

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

0
3

アジェンダ

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

アジェンダ• FireMonkey とは？
• シーンの作り方･･･• ３Ｄモデルの作り方• 点・線・面 のモデル･･･• 座標変換～ベクトルと行列～• 太陽系･･･• イレギュラーテクニック• 複数視点･･･• マテリアルの作り方• HLSL言語 によるシェーダ実装･･･
4

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

*
5

自己紹介

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

身分

自己紹介．

• 慶應義塾大学 藤代研究室– 社会人研究生

慶應義塾大学• コンピュータ･グラフィックスの研究• 和洋女子大学 山本研究室– 講師＆共同研究員• アパレル設計の授業＆研究• 日蓮宗 玉泉山 安国院

和洋女子大学– 住職• 心の平穏の研究

6

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

安国院

専門

自己紹介．

• 球面上の信号処理• 写実的なレンダリング• 立体視映像• ３Dアパレル設計• ３Ｄプリンタ

7

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

趣味

自己紹介．

• ＣＧアート（）制作– プロシージャル• フラクタル幾何学• カオス理論8

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

連絡

自己紹介．

• 個人
– サイト ： http://www.luxidea.net– メール ： contact@luxidea.net– Facebook： http://www.facebook.com/luxidea– Twitter ： @luxidea• 慶應義塾大学 藤代研究室– サイト ： http://www.fj.ics.keio.ac.jp• 日蓮宗 玉泉山 安国院– サイト ： http://www.ankokuin.or.jp
9

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

1
10

FireMonkey とは？

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

FireMonkey とは？ ．

意義• 2D/3D のコンポーネント・フレームワーク– 主にGUIの表示• マルチプラットフォーム– OS固有APIからの脱却• CG と GUI の融合– グラフィックス関連のアプリ制作が容易に！

11

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

FireMonkey とは？ ．

構造
床• シーングラフで表現– CG シーンへ転用可能– CG の概念で UI設計

前40cm

後40cm
陶器

机

上70cm

上70cm

コップ

ガラス

12

椅子

上40cm

皿

右10cm

人間

左10cm

リンゴ
出典「FireMonkeyファーストインプレッション」

木材

赤

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

肌(インスタンス)緑FireMonkey とは？ ．

解放• リアルタイムCG の定番は GLUT
– GUI が作れない– 低レベル実装が辛い• 高機能ゲームライブラリ– 大規模すぎてブラックボックス– 細かいところを弄れない• 俺々ライブラリ– マルチプラットフォーム化辛い– API バージョンの差異吸収辛い– GPU の対応レベルの確認辛い
13

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

FireMonkey とは？ ．

14

シーンの作り方.プロジェクト

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

FireMonkey とは？ ．

15

シーンの作り方.テンプレート

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

FireMonkey とは？ ．

シーンの作り方.表示窓

TViewport3D

Color := TAlphaColorRec.Dimgray

16

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

FireMonkey とは？ ．

シーンの作り方.モデル

TRoundCube

材質が未適用17

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

FireMonkey とは？ ．

シーンの作り方.マテリアル

TLightMaterialSource

RoundCube1.MaterialSource := LightMaterialSource1
18

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

FireMonkey とは？ ．

シーンの作り方.照明

TLightシェーディング(Shading)される

19

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

2
20

３Ｄモデルの作り方

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

３Ｄモデルの作り方．

ポリゴン

法線
連続面
面

• 三角面の集合面

面– ＧＰＵ は 点・線・三角形
しか描けない不連続面

座標• 構成要素
– 頂点

0,1

0,0

0,1

0,0

• 位置座標• 法線– 面の垂直ベクトル• テクスチャ座標

0.5,0

–面
• ３つの頂点への参照
1,0
21

1,1

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

1,0

1,1

３Ｄモデルの作り方．

クラス構造
TCameraTControl3D

TlightTCube

TProxyObject

TPlaneTDummyTModel3D

TGrid3D

TShape3D

TDisk

TMesh

TCustomMesh

TSphereTFmxObject
TStrokeCube

TCylinder

TColorMaterialSource

TRoundCube
TRectangle3D

TMaterialSource

TTextureMaterialSource

TCone
TEllipse3D

TLightMaterialSource

TExtrudedShape3D
TText3D

モデルを作るならここ！
22

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

TPath3D３Ｄモデルの作り方．

転送バッファ

• TShape3D = class( TControl3D )– property MaterialSource :TMaterialSource• マテリアルコンポーネントへの参照• TCustomMesh = class( TShape3D )– property Data :TMeshData• ポリゴンデータを保持する• TMeshData = class( TPersistent )– property VertexBuffer :TVertexBuffer• 頂点配列– property IndexBuffer :TIndexBuffer
• 面配列
23

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

３Ｄモデルの作り方．

頂点バッファ

• TVertexBuffer = class( TPersistent )– property Length :Integer• 頂点数– property Vertices[ AIndex:Integer ] :TPoint3D• 頂点の位置座標– property Normals[ AIndex:Integer ] :TPoint3D• 頂点の法線ベクトル– property TexCoord0[ AIndex:Integer ] :TPointF• 頂点のテクスチャ座標24

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

３Ｄモデルの作り方．

面バッファ• TIndexBuffer = class( TPersistent )– property Length ：Integer• 面数×３ ：ポリゴン• 線数×２ ：ワイヤーフレーム• 点数×１ ：ポイントクラウド– property Indices[ AIndex:Integer ] :Integer• N番目のポリゴンの属する頂点のインデックス番号• Indices[ 3×N＋0 ] = 頂点①• Indices[ 3×N＋1 ] = 頂点②• Indices[ 3×N＋2 ] = 頂点③25

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

３Ｄモデルの作り方．

実装

不連続面
面

26

with Data do面
begin
with VertexBuffer do
begin
Length := 7{頂点数};
Vertices [ 0 ] := TPoint3D.Create( *, *, * ){位置座標};
Normals [ 0 ] := TPoint3D.Create( *, *, * ){法線ベクトル};分
TexCoord0[ 0 ] := TPointF .Create( *, *){テクスチャ座標};
解～
Vertices [ 6 ] := TPoint3D.Create( *, *, * );Normals [ 6 ] := TPoint3D.Create( *, *, * );TexCoord0[ 6 ] := TPointF .Create( *, *
);
end;
(6)
with IndexBuffer do
begin
Length := 3{頂点数/ポリゴン} * 3{ポリゴン数};<1>
Indices[ 3*0+0 ] := 6;
Indices[ 3*0+1 ] := 1;
Indices[ 3*0+2 ] := 0;<0>Indices[ 3*1+0 ] := 6;(1)
Indices[ 3*1+1 ] := 5;(2)
Indices[ 3*1+2 ] := 1;
Indices[ 3*2+0 ] := 4;(0)
Indices[ 3*2+1 ] := 3;
Indices[ 3*2+2 ] := 2;
end;
本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。
end;

面

(5)
(4)
<2>

(3)

３Ｄモデルの作り方．

点と線

<0>

<1>

<2>

<3>

(0)

(1)

(2)

(3)

• 点

<0>

with IndexBuffer do
begin
Length := 1{頂点数/点} * 4{点数};Indices[ 0 ] := 0;
Indices[ 1 ] := 1;
Indices[ 2 ] := 2;
Indices[ 3 ] := 3;
～
end;

(0)

<1>
(1)

• 線
with IndexBuffer do
begin
Length := 2{頂点数/線} * 3{線数};Indices[ 2*0+0 ] := 0;
Indices[ 2*0+1 ] := 1;
Indices[ 2*1+0 ] := 1;
Indices[ 2*1+1 ] := 2;
Indices[ 2*2+0 ] := 2;
Indices[ 2*2+1 ] := 3;
～
end;
27

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

<2>
(2)

(3)

３Ｄモデルの作り方．

レンダリング• Render メソッドによって描画される。
procedure TCustomMesh.Render;
begin
Context.SetMatrix( TMatrix3D.CreateScaling( TPoint3D.Create( Width, Height, Depth ) ) * AbsoluteMatrix );Context.DrawTriangles( Data.VertexBuffer,
Data.IndexBuffer,
TMaterialSource.ValidMaterial( FMaterialSource ),AbsoluteOpacity );end;• 点や線を描く場合は override による変更が必要– Context.DrawPoints：点描画– Context.DrawLines：線描画– Context.DrawTriangles ：面描画

28

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

3
29

座標変換
～ベクトルと行列～

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

FireMonkey

座標系

座標変換．

Z

• 反転した右手座標系– Ｘ： 親指– Ｙ： 人差し指– Ｚ： 中指

X
右手 座標系

左手 座標系

Y

Y
Y

Z

X

X
Z

30
Direct3D

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

OpenGL

物と世界

座標変換．

• グローバル座標

ワールド

球体

立方体

– ワールド座標– 絶対座標差分• ローカル座標– オブジェクト座標– 相対座標– ≒ 差分

ローカル
ローカル

差分

グローバル
31

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

同時座標系

座標変換．

• 座標変換は行列（４×４）の演算で表現できる– 基底ベクトルを並べたもの– 複数の変換を合成して一つの行列で表せる
TVector3D

1 ×
1

=

0
0
0
1
TMatrix3D

Z

Y

差分変換行列

X 子
P

子
基準
変換

変換
前
行列

32

変換
後
行列 本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

親
基準
変換

親

ＴControl3D.LocalMatrix

座標変換．

• 親に対する差分変換行列を保持• クラスヘルパーを用いて入力可能とする– Position, RotationAngle などのプロパティが不整合化
interface
type
HControl3D = class helper for TControl3D
protected
function GetLocalMatrix :TMatrix3D;
procedure SetLocalMatrix( const LocalMatrix_:TMatrix3D ); virtual;publicproperty LocalMatrix :TMatrix3D read GetLocalMatrix write SetLocalMatrix;
end;
implementation
function HControl3D.GetLocalMatrix :TMatrix3D;
begin
Result := FLocalMatrix;
end;

33

procedure HControl3D.SetLocalMatrix( const LocalMatrix_:TMatrix3D );begin
FLocalMatrix := LocalMatrix_; RecalcAbsolute; Repaint;
本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。
end;

行列系関数

座標変換．

・TPoint3D = 1
・TVector3D = 1• System.Math.Vectors.TMatrix3D– 生成• const Identity :TMatrix3D

2 3
2 3 4

1
0
0
0

0 0
1 0
0 1
0 0

– 無変換（単位行列）• class function CreateScaling(～) :TMatrix3D;• class function CreateTranslation(～) :TMatrix3D;• class function CreateRotationX/Y/Z(～) :TMatrix3D;• class function CreateRotation(～) :TMatrix3D;– 任意軸での回転– 演算• class operator Multiply( TMatrix3D×TMatrix3D ) :TMatrix3D;• class operator Multiply( TPoint3D×TMatrix3D ) :TPoint3D;• class operator Multiply( TVector3D×TMatrix3D ) :TVector3D;
34

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

0
0
0
1

例

座標変換．

位置を初期化月.LocalMatrix := TMatrix3D.CreateTranslation( TPoint3D.Create( 5, 0, 0 ) );月.LocalMatrix := TMatrix3D.CreateRotationX( DegToRad( 2 ) )* 月.LocalMatrix* TMatrix3D.CreateRotationY( DegToRad( 1 ) )ワールド座標系でＹ軸回転

35

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

ローカル座標系で
Ｘ軸回転

絶対座標変換行列

座標変換．

• TControl3D

M×M

=M

× M = I（単位行列）– LocalMatrix :TMatrix3D;• 親のローカル座標系に対する座標変換行列– AbsoluteMatrix :TMatrix3D;• ワールド座標系に対する座標変換行列
自分.LocalMatrix × 親.AbsoluteMatrix = 自分.AbsoluteMatrix• 絶対座標変換行列を直接指定するには？
– 親の位置から逆算して自分の LM を求める
自.LM × 親.AM ＝ 自.AM
行列に割り算はない
自.LM＝ 自.AM ÷ 親.AM
自.LM
＝ 自.AM × 親.AM.Inverse
逆行列：行列の逆数
36

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

ＴControl3D.AbsoluteMatrix

座標変換．

• ワールド座標系に対する差分変換行列を保持• クラスヘルパーを用いて入力可能とする– Position, RotationAngle などのプロパティが不整合化
interface
type
HControl3D = class helper for TControl3D
protected
function GetAbsolMatrix :TMatrix3D;
procedure SetAbsoluteMatrix( const AbsoluteMatrix_:TMatrix3D );publicproperty AbsoluteMatrix :TMatrix3D read GetAbsolMatrix write SetAbsoluteMatrix;
implementation
function HControl3D.GetAbsoluteMatrix :TMatrix3D;
begin
Result := Self.GetAbsoluteMatrix;
end;
procedure HControl3D.SetAbsoluteMatrix( const AbsoluteMatrix_:TMatrix3D );begin
LocalMatrix := AbsoluteMatrix_ * TControl3D( Parent ).AbsoluteMatrix.Inverse;
end;

37

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

438

イレギュラーテクニック

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

平行投影

イレギュラーテクニック．

• 遠近法を廃した平行線の交わらない表示– CADなどの製図用途– ２次元を意識したアニメ表現

オリジナルライブラリの書き換え
39

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

平行投影．実装

イレギュラーテクニック．

• 投影変換行列を変更– TMatrix3D に生成関数• CreateOrthoRH(～)• CreateOrthoOffCenterRH(～)

procedure TCamera.SetAngleOfView(const Value: Single);
begin
if FAngleOfView <> Value then
画角ゼロを許可
begin
FAngleOfView := Value;
if FAngleOfView <0 then FAngleOfView :=0;
if FAngleOfView > 179 then FAngleOfView := 179;～end;

– 中心をずらした平行投影• CreatePerspectiveFovRH(～)– 透視投影function TContext3D.GetProjectionMatrix: TMatrix3D;
var
M: TMatrix3D;
begin
平行投影
if FRecalcProjectionMatrix then
出典：床井研究室
begin
if FCurrentAngleOfView = 0 then
Result := TMatrix3D.CreateOrthoRH( {表示高}, {表示高} * FWidth / FHeight, 1.0, 1000.0 )else
if SameValue(FHeight, 0.0, Epsilon) then
Result := TMatrix3D.CreatePerspectiveFovRH(DegToRad(FCurrentAngleOfView), 1.0, 1.0, 1000.0)
else
Result := TMatrix3D.CreatePerspectiveFovRH(DegToRad(FCurrentAngleOfView), FWidth / FHeight, 1.0, 1000.0);～
end;
本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

40

複数視点

イレギュラーテクニック．

• 一つのシーンを複数のカメラで撮影– それぞれのカメラは画角も解像度も個別に変更可能– 三面図などへの応用

41

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

実装．1

イレギュラーテクニック．複数視点．

• 任意サイズの TBitmap へレンダリングするクラス
TBitmapRender = classProtected_Camera:TCamera;_BMP:TBitmap;_Texture:TTexture;_Context:TContext3D;
カメラ
描画先_Color:TAlphaColor;_Multisample :TMultisample;
publicconstructor Create( const Camera_:TCamera; const BMP_:TBitmap );destructor Destroy; override;
property Color:TAlphaColor read _Colorwrite _Color;
property Multisample :TMultisample read _Multisample write _Multisample;
procedure Render;
end;
HViewport3D = class helper for TViewport3D
protected
function GetLights :TList<TLight>;
private のフィールドへ強制アクセス
function GetRenderingList :TList<TControl3D>;
publicproperty Lights:TList<TLight>
read GetLights;
property RenderingList :TList<TControl3D> read GetRenderingList;
end;
42

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

実装．2

イレギュラーテクニック．複数視点．

• 任意サイズの TTexture から TContext3D を生成
constructor TBitmapRender.Create(～);
begin_Camera := Camera_;_BMP:= BMP_;_Texture := TTexture.Create;_Texture.Style := [ TTextureStyle.RenderTarget ];_Texture.SetSize( _BMP.Width, _BMP.Height );ITextureAccess( _Texture ).TextureScale := 1;_Multisample := TMultisample.FourSamples;_Color:= TAlphaColors.White;

描画先ビットマップと同サイズ

背景色_Context := TContextManager.CreateFromTexture( _Texture, _Multisample, True );end;destructor TBitmapRender.Destroy;
begin
FreeAndNil( _Context );FreeAndNil( _Texture );
end;

43

アンチエイリアシングのサンプル数

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

実装．3

イレギュラーテクニック．複数視点．

• 描画は TViewport3D に任せる
procedure TBitmapRender.Render;
var
W, H :Integer;
C :TControl3D;
beginwith _Camera.Viewport.Context do
begin
W := Width;
参照元の元サイズを保存
参照元のサイズを
H := Height;
SetSize( _BMP.Width, _BMP.Height );描画先のサイズへ
SetCameraMatrix( _Camera.CameraMatrix );SetCameraAngleOfView( _Camera.AngleOfView );end;with _Context do
begin
BeginScene を呼んだ Context が描画対象
BeginScene;
SetContextState( TContextState.csScissorOff );Clear( [ TClearTarget.Color, TClearTarget.Depth ], _Color, 1.0, 0 );for C in _Camera.Viewport.RenderingList do C.RenderInternal;
EndScene;
結果を描画先へ転写
CopyToBitmap( _BMP, Rect( 0, 0, _BMP.Width, _BMP.Height ) );end;
_Camera.Viewport.Context.SetSize( W, H );参照元のサイズを復元
end;
44

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

545

マテリアルの作り方

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

マテリアルの作り方．

原理• FireMonkey は Direct3D と OpenGL により描画– Direct3D ： Microsoft が策定する API• Windows 環境でのみ動作– OpenGL： Khronosグループが策定する API• MacOSX / iOS / Android / Linux 環境で動作• 質感はシェーダにより表現– バーテックス（頂点）シェーダ• モデルの頂点毎に呼ばれるイベント– フラグメント（ピクセル）シェーダ• ピクセル毎に呼ばれるイベント

46

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

出典：床井研究室

マテリアルの作り方．

プログラママブル• シェーダ は シェーディング言語 により記述– Direct3D ： HLSL (High Level Shader Language) 言語• Windows SDK によりプリコンパイルしたバイナリを実行時にロード– Windows 8.1 用 Windows ソフトウェア開発キット (Windows SDK)
http://msdn.microsoft.com/ja-jp/windows/desktop/bg162891.aspx– OpenGL： GLSL (OpenGL Shading Language) 言語• ソースを直接ロードし実行時にコンパイル– ソースを秘匿できない– プリコンパイルに辛うじて対応 ： GL_ARB_get_program_binary» GPU ベンダ依存の互換性問題

47

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

出典：床井研究室

マテリアルの作り方．

クラス構造• TMaterial が TMaterialSource に内包される– 同じシェーダを複数のモデルに適用可能
FMX.Materials
TMaterial

TColorMaterial

TCustomMaterial

TTextureMaterial

シェーダ本体TLightMaterial

TColorMaterialSource

FMX.MaterialSources
TFmxObject

TMaterialSource

TTextureMaterialSource

TLightMaterialSource

モデルと関連付けるためのアダプタ
48

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

マテリアルの作り方．

初級

方向に依存しない拡散反射• ディフューズ反射のシェーダ– 石膏のような光沢のない滑らかな質感
反射率= max( cos , 0 ) = max( N · L, 0 )法線ベクトル：N

光源ベクトル：L

49

視線ベクトル：V

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

実装．クラス

マテリアルの作り方．初級．

• TMaterialSource にも同じフィールドを持たせる– 同じシェーダを複数のパラメータで使い回せる
TMyMaterial = class( TCustomMaterial )protected_DiffColor :TAlphaColor;
procedure SetDiffColor( const DiffColor_:TAlphaColor );procedure InitShaderV;procedure InitShaderP;
procedure DoInitialize; override;
procedure DoApply( const Context_:TContext3D ); override;class function DoGetMaterialProperty( const Prop_:TMaterial.TProperty ): string; override;
publicconstructor Create; override;
property DiffColor: TAlphaColor read _DiffColor write SetDiffColor;
end;
TMyMaterialSource = class( TMaterialSource )protected
function GetDiffColor :TAlphaColor;
procedure SetDiffColor( const DiffColor_:TAlphaColor );function CreateMaterial: TMaterial; override;
publicproperty DiffColor: TAlphaColor read GetDiffColor write SetDiffColor;
end;
50

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

実装．初期化

マテリアルの作り方．初級．

• シェーダのロード
procedure TMyMaterial.DoInitialize;
begin
inherited;
InitShaderV;
シェーダのステージ毎に分けた（ 後述）
InitShaderP;end;• デフォルト変数の命名
class function TMyMaterial.DoGetMaterialProperty( const Prop_:TMaterial.TProperty ): string;
begin
case Prop_ of
TProperty.ModelViewProjection: Result := 'VS_FMatrixMVP';
TProperty.ModelView: Result := 'VS_FMatrixMV';
TProperty.ModelViewInverseTranspose : Result := 'VS_TIMatrixMV';
else Result := '';
end;
end;

• プロパティ
procedure TMyMaterial.SetDiffColor( const DiffColor_:TAlphaColor );begin_DiffColor := DiffColor_; DoChange;
end;

51

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

実装．初期化．バーテックスシェーダ

マテリアルの作り方．初級．

• プラットフォーム別のシェーダを用意function FileToBytes( const FileName_:string ) :TBytes;
beginwith TMemoryStream.Create do
begin
LoadFromFile( FileName_ );SetLength( Result, Size );Read( Result, Size );
Free;
end;
end;

procedure TMyMaterial.InitShaderV;var
CSS :array [ 0..1 ] of TContextShaderSource;
シェーダのロードbegin
シェーダに必要な入力変数を登録する
CSS[ 0 ] := TContextShaderSource.Create( TContextShaderArch.DX9,
FileToBytes( '..¥..¥» SHADER¥HLSL¥3.0¥MyMaterial.cvs' ),[ TContextShaderVariable.Create( 'VS̲FMatrixMVP', TContextShaderVariableKind.Matrix, 00, 4 ),TContextShaderVariable.Create( 'VS̲FMatrixMV', TContextShaderVariableKind.Matrix, 04, 4 ),TContextShaderVariable.Create( VS̲TIMatrixMV', TContextShaderVariableKind.Matrix, 08, 4 ) ] );CSS[ 1 ] := TContextShaderSource.Create
変数名
変数型
入力位置 変数サイズ( TContextShaderArch.DX11̲level̲9,FileToBytes( '..¥..¥» SHADER¥HLSL¥5.0¥MyMaterial.cvs' ),[ TContextShaderVariable.Create( 'VS̲FMatrixMVP', TContextShaderVariableKind.Matrix, 000, 64 ),TContextShaderVariable.Create( 'VS̲FMatrixMV', TContextShaderVariableKind.Matrix, 064, 64 ),TContextShaderVariable.Create( 'VS̲TIMatrixMV', TContextShaderVariableKind.Matrix, 128, 64 ) ] );FVertexShader := TShaderManager.RegisterShaderFromData( 'MyMaterial.vs', TContextShaderKind.VertexShader, '', CSS );
end;

52

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

実装．初期化．ピクセルシェーダ

マテリアルの作り方．初級．

procedure TMyMaterial.InitShaderP;
バーテックスシェーダと同名の変数は渡せない
var
CSS :array [ 0..1 ] of TContextShaderSource;
begin
CSS[ 0 ] := TContextShaderSource.Create
DX9 ではレジスタ単位( TContextShaderArch.DX9,
FileToBytes( '..¥..¥» SHADER¥HLSL¥3.0¥MyMaterial.cps' ),[ TContextShaderVariable.Create( 'PS̲FMatrixMVP', TContextShaderVariableKind.Matrix, 00, 4 ),TContextShaderVariable.Create( 'PS̲FMatrixMV', TContextShaderVariableKind.Matrix, 04, 4 ),TContextShaderVariable.Create( PS̲TIMatrixMV, TContextShaderVariableKind.Matrix, 08, 4 ),TContextShaderVariable.Create( 'PS̲EyePos', TContextShaderVariableKind.Vector, 12, 1 ),TContextShaderVariable.Create( 'PS̲DiffColor', TContextShaderVariableKind.Vector, 13, 1 ),TContextShaderVariable.Create( 'PS̲Lights[0].Opt', TContextShaderVariableKind.Vector, 14, 1 ),TContextShaderVariable.Create( 'PS̲Lights[0].Pos', TContextShaderVariableKind.Vector, 15, 1 ),TContextShaderVariable.Create( 'PS̲Lights[0].Dir', TContextShaderVariableKind.Vector, 16, 1 ),TContextShaderVariable.Create( 'PS̲Lights[0].Col', TContextShaderVariableKind.Vector, 17, 1 ) ] );CSS[ 1 ] := TContextShaderSource.Create
DX11 ではバイト単位( TContextShaderArch.DX11̲level̲9,FileToBytes( '..¥..¥» SHADER¥HLSL¥5.0¥MyMaterial.cps' ),[ TContextShaderVariable.Create( 'PS̲FMatrixMVP', TContextShaderVariableKind.Matrix, 000, 64 ),TContextShaderVariable.Create( 'PS̲FMatrixMV', TContextShaderVariableKind.Matrix, 064, 64 ),TContextShaderVariable.Create( PS̲TIMatrixMV, TContextShaderVariableKind.Matrix, 128, 64 ),TContextShaderVariable.Create( 'PS̲EyePos', TContextShaderVariableKind.Vector, 192, 16 ),TContextShaderVariable.Create( 'PS̲DiffColor', TContextShaderVariableKind.Vector, 208, 16 ),TContextShaderVariable.Create( 'PS̲Lights[0].Opt', TContextShaderVariableKind.Vector, 224, 16 ),TContextShaderVariable.Create( 'PS̲Lights[0].Pos', TContextShaderVariableKind.Vector, 240, 16 ),TContextShaderVariable.Create( 'PS̲Lights[0].Dir', TContextShaderVariableKind.Vector, 256, 16 ),TContextShaderVariable.Create( 'PS̲Lights[0].Col', TContextShaderVariableKind.Vector, 272, 16 ) ] );FPixelShader := TShaderManager.RegisterShaderFromData( 'MyMaterial.ps', TContextShaderKind.PixelShader, '', CSS );
end;

53

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。
レコード型の配列も小分けにして渡せる

実装．変数型

マテリアルの作り方．初級．

• TContextShaderVariableKind

HLSLでの型名– Float ： 4 byte：1 register … float– Float2 ： 8 byte：1 register … float2– Float3 ：12 byte：1 register … float3– Vector ：16 byte：1 register … float4– Matrix ：64 byte：4 register … float4ｘ4– Texture： *：1 register … Texture2D<float4>
0 register1

F1
0 byte
54

2

F3
16

3

4

48

64

V
32

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

実装．変数入力

マテリアルの作り方．初級．

• シェーダ内で用いる定数を外部から提供• VS_FMatrixMVP，VS_FMatrixMV，VS_TIMatrixMV は標準で渡される
procedure TMyMaterial.DoApply( const Context_:TContext3D );begin
inherited;
変数名
入力値
with Context_ do
begin
SetShaderVariable( ‘PS_FMatrixMVP', CurrentModelViewProjectionMatrix );SetShaderVariable( 'PS_FMatrixMV' , CurrentMatrix );SetShaderVariable( 'PS_TIMatrixMV', CurrentMatrix.Inverse.Transpose );SetShaderVariable( 'PS_EyePos', [ CurrentCameraInvMatrix.M[ 3 ] ] );SetShaderVariable( 'PS_DiffColor' , _DiffColor );with Lights[ 0 ] do
ワールド座標系での視点位置
begin
SetShaderVariable( 'PS_Lights[0].Opt',[ TVector3D.Create( Integer( LightType ) + 1,
Cos( DegToRad( SpotCutoff ) ),ライトの数だけ
SpotExponent, 0 ) ] );繰り返す
SetShaderVariable( 'PS_Lights[0].Pos', [ Position ] );SetShaderVariable( 'PS_Lights[0].Dir', [ Direction ] );SetShaderVariable( 'PS_Lights[0].Col', Color );
end;
end;
end;
本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。
55

実装．バーテックスシェーダ

マテリアルの作り方．初級．

• モデルの頂点毎の情報を加工して出力変数位置（レジスタ単位）
float4x4
float4x4
float4x4

FMatrixMVP : register( c00 );FMatrixMV : register( c04 );IMatrixMV : register( c08 );static float4x4 _FMatrixMVP = transpose( FMatrixMVP );static float4x4 _FMatrixMV = transpose( FMatrixMV );static float4x4 _IMatrixMV = transpose( IMatrixMV );struct TSender { float4 Pos :POSITION;
float4 Nor :NORMAL;
float4 Tex :TEXCOORD; };頂点の情報
struct TResult { float4 Scr :SV_Position;
float4 Pos :TEXCOORD0;
float4 Nor :NORMAL;
ピクセルの情報
float4 Tex :TEXCOORD1; };ローカル座標系での位置
スクリーン座標系での位置
ワールド座標系での位置

TResult main( TSender _Sender ) {TResult _Result;
頂点位置をスクリーン座標系へ変換_Result.Scr = mul( _Sender.Pos, _FMatrixMVP );_Result.Pos = mul( _Sender.Pos, _FMatrixMV );頂点位置をワールド座標系へ変換_Result.Nor = mul( _Sender.Nor, _IMatrixMV );_Result.Tex =_Sender.Tex;
return _Result;}
法線ベクトルをワールド座標系へ変換

56

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

実装．ピクセルシェーダ

マテリアルの作り方．初級．

• ピクセル毎の情報を加工して出力
struct Tlight {float4 Opt;

float4 Pos;

float4x4 FMatrixMVP : register( c00 );float4x4 FMatrixMV : register( c04 );float4x4 IMatrixMV : register( c08 );float4_EyePos: register( c12 );float4_DiffColor : register( c13 );TLight_Light[1]: register( c14 );struct TSender {struct TResult {float4 Dir;

float4 Col;};static float4x4 _FMatrixMVP = transpose( FMatrixMVP );static float4x4 _FMatrixMV = transpose( FMatrixMV );static float4x4 _IMatrixMV = transpose( IMatrixMV );バーテックスシェーダの出力と同じ内容のレコード型

float4 Scr :SV_Position; float4 Pos :TEXCOORD0;
float4 Nor :NORMAL;
float4 Tex :TEXCOORD1; };float4 Col :SV_Target; };ピクセルシェーダの出力は色

TResult main( TSender _Sender ){ピクセルの法線ベクトル：NTResult _Result;float3 N = normalize( _Sender.Nor.xyz );float3 L = -_Light[0].Dir.xyz;
ピクセルの光源ベクトル：Lfloat D = max( dot( L, N ), 0.0 );_Result.Col.r = _Light[0].Col.r * D * _DiffColor.r;_Result.Col.g = _Light[0].Col.g * D * _DiffColor.g;_Result.Col.b = _Light[0].Col.b * D * _DiffColor.b;_Result.Col.a = 1.0;
return _Result;}57

レコード型も扱える

不透明度値

反射率

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

実装．コンパイル

マテリアルの作り方．初級．

• Windows SDK 付属の fxc.exe を利用– DX11版 のソースから DX9版 のバイナリも出力可能• Shader Model 2.0 ： DirectX 9.0• Shader Model 3.0 ： DirectX 9.0c
• Shader Model 4.0 ： DirectX 10

DX9 モードの強制
FMX.Types.GlobalUseDX := False;– *s_4_0_level_9_1 ： SM2.0 でコンパイル– *s_4_0_level_9_3 ： SM3.0 でコンパイル• Shader Model 5.0 ： DirectX 11
32bit版 / 64bit版
rem set FXC="C:¥Program Files (x86)¥Windows Kits¥8.1¥bin¥x86¥fxc.exe"
set FXC=“C:¥Program Files (x86)¥Windows Kits¥8.1¥bin¥x64¥fxc.exe”

58

for %%i in ( *.vs ) do ( %FXC% %%i /T vs_3_0 /Fo 3.0¥%%~ni.cvs )for %%i in ( *.ps ) do ( %FXC% %%i /T ps_3_0 /Fo 3.0¥%%~ni.cps )Shader Model 3.0

for %%i in ( *.vs ) do ( %FXC% %%i /T vs_5_0 /Fo 5.0¥%%~ni.cvs )for %%i in ( *.ps ) do ( %FXC% %%i /T ps_5_0 /Fo 5.0¥%%~ni.cps )Shader Model 5.0

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

マテリアルの作り方．

初級．完成• 全体的な陰影は TLightMaterialSource と同等
TLightMaterial

59

TMyMaterial

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

マテリアルの作り方．

中級• 光沢（スペキュラ）の表現– Phong 反射モデル
反射率 = max( cosθ ,0 )– Blinn-Phong 反射モデル
反射率 = max( cosθ ,0 )
※ ：大きいほど光沢が鋭い

出典：床井研究室

60

Phong 反射モデル

Blinn-Phong 反射モデル

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

実装．ピクセルシェーダ

マテリアルの作り方．中級．

• ピクセル毎の情報を加工して出力
TResult main( TSender _Sender ){TResult _Result;

ピクセル位置からの視線ベクトルfloat3 N = normalize( _Sender.Nor.xyz );float3 L = -_Light[0].Dir.xyz;
float3 E = normalize( _EyePos.xyz - _Sender.Pos.xyz );float3 H = normalize( L + E );
float
float

D = max( dot( L, N ), 0.0 );S = pow( max( dot( H, N ), 0.0 ), _SpecShiny );スペキュラの反射率_Result.Col.r = _Light[0].Col.r * D * ( _DiffColor.r + _SpecColor.r * S );_Result.Col.g = _Light[0].Col.g * D * ( _DiffColor.g + _SpecColor.g * S );_Result.Col.b = _Light[0].Col.b * D * ( _DiffColor.b + _SpecColor.b * S );_Result.Col.a = 1.0;
return _Result;

ディフューズ ＋ スペキュラ}
61

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

マテリアルの作り方．

中級．完成• TMyMaterialSource の方がスペキュラが綺麗？
TLightMaterial

62

TMyMaterial

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

マテリアルの作り方．

中級．ローポリ• TLightMaterialSource はポリゴン数で品質が変化
TLightMaterial

63

TMyMaterial

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

マテリアルの作り方．

バーテックス vs ピクセル

• シェーディングの構成方法は２種類– グローシェーディング･･･バーテックスシェーダベース• 頂点だけで照明計算を行う– フォンシェーディング･･･ピクセルシェーダベース• すべてのピクセルにおいて照明計算を行う
Gouraud shading

64

Phong shading

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

風景
マテリアルの作り方．

上級

緯度
経度

• 鏡面反射のシェーダ– 磨き上げた金属のような質感
R=2 N·V N−V

風景

Spherical Map

法線ベクトル：N

反射ベクトル：R

65

視線ベクトル：V

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

M.C.Escher

実装．初期化．ピクセルシェーダ

マテリアルの作り方．上級．

• テクスチャ用のレジスタは定数とは別管理
procedure TMyMaterial.InitShaderP;var
CSS :array [ 0..1 ] of TContextShaderSource;
begin
CSS[ 0 ] := TContextShaderSource.Create( TContextShaderArch.DX9,
FileToBytes( '..¥..¥» SHADER¥HLSL¥3.0¥MyMaterial.cps' ),[ TContextShaderVariable.Create( PS̲FMatrixMVP, TContextShaderVariableKind.Matrix ,TContextShaderVariable.Create( PS̲FMatrixMV, TContextShaderVariableKind.Matrix ,TContextShaderVariable.Create( PS̲TIMatrixMV, TContextShaderVariableKind.Matrix ,TContextShaderVariable.Create( PS̲EyePos, TContextShaderVariableKind.Vector ,TContextShaderVariable.Create( PS̲Texture, TContextShaderVariableKind.Texture,

00,
04,
08,
12,
0,

4 ),
4 ),
4 ),
1 ),
0 ) ] );

CSS[ 1 ] := TContextShaderSource.Create
テクスチャには別に番号を振る( TContextShaderArch.DX11̲level̲9,FileToBytes( '..¥..¥» SHADER¥HLSL¥5.0¥MyMaterial.cps' ),[ TContextShaderVariable.Create( PS̲FMatrixMVP, TContextShaderVariableKind.Matrix , 000, 64 ),TContextShaderVariable.Create( PS̲FMatrixMV, TContextShaderVariableKind.Matrix , 064, 64 ),TContextShaderVariable.Create( PS̲TIMatrixMV, TContextShaderVariableKind.Matrix , 128, 64 ),TContextShaderVariable.Create( PS̲EyePos, TContextShaderVariableKind.Vector , 192, 16 ),TContextShaderVariable.Create( PS̲Texture, TContextShaderVariableKind.Texture,0, 0 ) ] );FPixelShader := TShaderManager.RegisterShaderFromData( 'MyMaterial.ps', TContextShaderKind.PixelShader, '', CSS );
end;

66

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

実装．変数入力

マテリアルの作り方．上級．

• テクスチャとして渡せるのは TTexture のみ
procedure TMyMaterial.DoApply( const Context_:TContext3D );begin
inherited;with Context_ do
begin
SetShaderVariable( ‘PS_FMatrixMVP', CurrentModelViewProjectionMatrix );SetShaderVariable( 'PS_FMatrixMV' , CurrentMatrix );SetShaderVariable( 'PS_TIMatrixMV', CurrentMatrix.Inverse.Transpose );SetShaderVariable( 'PS_EyePos', [ CurrentCameraInvMatrix.M[ 3 ] ] );SetShaderVariable( 'PS_Texture', _Texture );
end;
end;
TTexture– TBitmap は TTextureBitmap を経由して変換
var
B :TBitmap;
TB :TTextureBitmap;
T :TTexture;
67

begin
B.LoadFromFile( ‘～’ );TB.Assign( B );T := TB.Texture;

begin
TB.LoadFromFile( ‘～’ );T := TB.Texture;

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

実装．ピクセルシェーダ

マテリアルの作り方．上級．

• ピクセル毎の情報を加工して出力
Texture2D<float4> _Texture : register( t0 );SamplerState _SamplerState {};テクスチャ用レジスタは t* で指定

テクスチャから色を採る際の諸設定

TResult main( TSender _Sender ){TResult _Result;float3 E = normalize( _EyePos.xyz - _Sender.Pos.xyz );反射ベクトルを
float3 N = normalize( _Sender.Nor.xyz );算出する標準関数
float3 R = normalize( reflect( E, N ) );_Result.Col.rgb = _Texture.Sample( _SamplerState, VectorToSky( R ) ).rgb;_Result.Col.a= 1.0;
return _Result;
テクスチャから色を取得するメソッド}
RGB をベクトルとしてベクトルの方向を
緯度/経度に換算する関数

68

float2 VectorToSky( float3 _Vector )演算することも可能{
float2 _Result;_Result.x = ( Pi - atan2( _Vector.z, _Vector.x ) ) / Pi2;
_Result.y = acos( _Vector.y ) / Pi;
return _Result;}

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

マテリアルの作り方．

69

上級．完成

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

謝辞• シェーダ構造解明の先駆者– 個人サイト「全力わはー」• http://d.hatena.ne.jp/tales– MMD4Delphi 0.0.1– LibOVR wrapper for Delphi 0.0.1

70

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

おわり