DG-Caradチュートリアル(3)

ビルボードで装飾してみよう

野望未だ成らず

 ポリゴンメカの表示は出来ました。なにやら回転もしています。

 しかし、何かが足りない。そう、メカ物に付き物といえば、無意味に光ってるバーニアの噴射口やら、無意味に蛇行するミサイルやらじゃないですか!

 そういう訳で、メカの噴射口にフレアを入れてみましょう(強引)

 チュートリアル(2)で作ったソースに手を加える形で説明をしていきます。

 

ビルボードって何

 メカの噴射口にフレアを入れるとは言ったものの、メカの形状データに手を入れて、噴射口の後ろにフレアを描いたポリゴン一枚を置くとどうなるでしょうか? 形状データを作るとき、モデラで見ている分にはちゃんとフレアに見えても、横からみたりするとフレアがペラペラになっていきますよね。

 そんな訳で、どこに視点があっても同じ向きで描画されて、視点からの距離に従って縮小/拡大だけされてくれるような、都合のいい描かれ方をするポリゴンが必要になってきます。それがビルボードです。

 ビルボードを使うといっても簡単でして、チュートリアル(1)で説明したような画像を一枚置くのとほとんど変わらない要領で実現できます。

 

メンバ変数の追加

 では、まず変数やオブジェクトを追加しましょう。

 水色になっている部分が追加するコードです。 テクスチャオブジェクトが一つだけ。

type
TForm1 = class(TForm)
  DG: TDGCarad;
  ApplicationEvents1: TApplicationEvents;
  procedure FormCreate(Sender: TObject);
  procedure ApplicationEvents1Idle(Sender: TObject; var Done: Boolean);
  procedure FormResize(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
    Root:TSXFrame;      //全てのフレームの親になるフレーム
    MechFrame:TSXFrame; //メカを格納するフレーム
    Camera:TSXFrame;    //カメラの位置・姿勢を示すフレーム

    Mech:TSXMesh;       //メカの形状データ

    Lights:TSXLightGroup; //ライト管理オブジェクト

    Texture:TDGTexture; //ビルボードに貼るテクスチャ

    Tick:Integer;       //メカの回転につかうカウンタ
end;

 

初期化と解放

 次に、初期化ルーチンで、ビルボードに貼り付けるテクスチャの作成・読み込みを行います。

 水色になっている部分がチュートリアル(2)から追加される部分です。

//初期化
procedure TForm1.FormCreate(Sender: TObject);
begin
  //解像度とデプスステンシルサーフェスの設定
  DG.WindowMode(ClientWidth, ClientHeight, DGFMT_ZCheap);

  //シーンオブジェクトの生成
  Scene:=TSXScene.Create(DG);

  //ビューポートの設定
  Scene.SetProjection(Pi/2, ClientHeight/ClientWidth, 0.01, 10000.0);
  Scene.SetViewport(0,0,ClientWidth,ClientHeight);

  //フレームの作成
  Root:=TSXFrame.Create(Nil);       
  MechFrame:=TSXFrame.Create(Root);
  Camera:=TSXFrame.Create(Root);
  Scene.CameraFrame:=Camera;

  //カメラ、メカの位置・姿勢を決める
  Camera.SetTranslation(Root, Vector(0,0,0));
  Camera.SetOrientation(Root, Vector(0,0,1), Vector(0,1,0));

  MechFrame.SetTranslation(Root, Vector(0,-0.5,2));
  MechFrame.SetOrientation(Root, Vector(0,0,1), Vector(0,1,0));

  //メカ読み込み
  Mech:=TSXMesh.Create(DG);
  Mech.LoadFromFile('mech.sx');
  MechFrame.Mesh:=Mech;

  //ライトの設定
  Lights:=TSXLightGroup.Create(DG, 1);
  Lights[0].SetupDiffuse(1.0,1.0,1.0);
  Lights[0].SetupSpecular(1.0,1.0,1.0);
  Lights[0].SetupAmbient(0.3,0.3,0.3);
  Lights[0].SetupDirectional(Vector(0,0,1));
  Lights[0].Enabled:=True;

  //ビルボードに指定するテクスチャの作成
  //今回はαチャンネルは無くていい
  Texture:=TDGTexture.Create(DG, DGFMT_RGB);
  Texture.BorderColor:=$00000000;
  Texture.LoadFromFile('light.bmp');
    

  Tick:=0;
end;

  ビルボードにはフレア用の画像としてlight.bmpという画像を読み込みます。

 

 こんなの。 これを縮小してメカの噴射口の近くに貼ってやろうと。

 毎度のごとくオブジェクトの生成を書いたら、対になる解放ルーチンを、アプリケーションの終了時に書いてやる必要があります。

//解放
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  Root.Free;  //これで、Rootの子フレーム(Camera,MechFrame)も含めて解放される
  Lights.Free;
  Mech.Free;
  Texture.Free;
  Scene.Free;
end;

  増えたのはTextureだけなので、それを解放するオブジェクトに追加、と。

描画ループの修正

 サクサクと描画ループも修正して行きましょう。

 ApplicationEvents.OnIdleイベントハンドラに注目。水色になっている部分が追加したコードです

procedure TForm1.ApplicationEvents1Idle(Sender: TObject;
  var Done: Boolean);
var
  FlareSize:Single;
  VBBs:Array[0..3] of TSXvertexBB;
begin
  //Direct3Dデバイスが生きてるかテストしてから描画
  Case DG.D3DDevice.TestCooperativeLevel of
    //OK、生きてます
    D3D_OK: begin

      //フレアの大きさを計算し、ビルボード用頂点を作る
      FlareSize:=0.15 * (1+Sin(Tick/100)*0.2);
      VBBs[0]:=SXVertexBB(-FlareSize,-FlareSize,$FF40C0FF, 0,0);
      VBBs[1]:=SXVertexBB(+FlareSize,-FlareSize,$FF40C0FF, Texture.U,0);
      VBBs[2]:=SXVertexBB(-FlareSize,+FlareSize,$FF40C0FF, 0,Texture.V);
      VBBs[3]:=SXvertexBB(+FlareSize,+FlareSize,$FF40C0FF, Texture.U,Texture.V);

      //ビルボードを押しこむ
      //左右に一個ずつ
      Scene.PushBillboard(MechFrame, Vector(+0.2,0.38,-0.58),VBBs,Texture,sxbAdd);
      Scene.PushBillboard(MechFrame, Vector(-0.2,0.38,-0.58),VBBs,Texture,sxbAdd);


      //描画開始
      DG.D3DDevice.BeginScene;

      //画面のクリア、色は黒で
      Scene.Clear(D3DCLEAR_TARGET Or D3DCLEAR_ZBUFFER, $FF000000, 1.0, 0);

      //シーンの内容を全部描く
      Scene.Render(Root);

      //描画終り
      DG.D3DDevice.EndScene;

      //表示
      DG.D3DDevice.Present(Nil, Nil, 0, Nil);
    end;

    //デバイスは生きてるけど、ロスト後でリセットされてない
    D3DERR_DEVICENOTRESET: begin
      DG.Reset;
    end;
  end;

  //メカをまわせー
  Inc(Tick,20);
  MechFrame.SetOrientation(Root, NowRotY(Vector(0,0,1),Tick), Vector(0,1,0));

  //これを設定すると、他のイベントが実行されなくてもまたこのイベントが呼ばれる
  Done:=False;
end;

 まず、フレアの大きさを計算しています。カウンタであるTick変数の値に従ってSin関数で適当にボヤボヤと変化させます。

 次に、SXVertexBBメソッドを使って、ビルボードの4隅の頂点データを作ります。一枚の画像を貼ったとき同様、配列には左上、右上、左下、右下の順に格納します。 色は$FF40C0FF という事で、シアンに近い青色です。

 頂点データが出来たら、PushBillboardメソッドでビルボードとして押し込みます。第一引数で指定されたフレームの、第二引数で指定された座標を原点としてビルボードを貼ります。

 第5引数のsxbAddというらは、ビルボードの色を今まで画面に描かれていた内容に足しこむ、という指定です。光っている物体を表現するのに適しています。

 

 

できたかな?

 

 以上で噴射口にフレアが付きました。 装飾にこだわるのは楽しいですね〜

 次はいよいよポリゴンフィギュアの表示です。気合入れて行こう!