拡大・縮小

演算を用いたポリゴンの拡大・縮小

ポリゴンを拡大・縮小させるには、テクスチャのサイズよりも大きな/小さなポリゴンを作成すればよい。ポリゴンの大きさは4つの頂点座標の位置によって決定されるため、ポリゴンの拡大/縮小を行うには頂点座標を変化させればよい。
例えば、下図のように頂点A、B、C、Dで構成されるポリゴンを拡大するプログラムを考える。

例えば現在のポリゴンの各頂点座標が次のような値であるとする。

頂点Aの(X, Y) = (100, 100)
頂点Bの(X, Y) = (200, 100)
頂点Cの(X, Y) = (200, 200)
頂点Dの(X, Y) = (100, 200)

このポリゴンを拡大する場合、例えば左上(頂点A)を原点として考えると、次のような値になる。

頂点A’の(X, Y) = (100, 100)
頂点B’の(X, Y) = (300, 100)
頂点C’の(X, Y) = (300, 300)
頂点D’の(X, Y) = (100, 300)

また、ポリゴンの中心を原点として考えると、次のような値になる。

頂点A’の(X, Y) = (50, 50)
頂点B’の(X, Y) = (250, 50)
頂点C’の(X, Y) = (250, 250)
頂点D’の(X, Y) = (50, 250)

どちらのやり方でも、プログラムは面倒そうである・・・

行列による拡大・縮小

前頁で、行列を使えば移動、拡大/縮小、回転が1回の乗算だけで行うことができると説明した。
詳しい説明は省略するが、行列を使って拡大/縮小を行う式は次のようになる。

倍率は1.0で元の大きさと同じサイズであり、1.0を超えると拡大、1.0未満で縮小となる。

例えば、下記のポリゴンを2倍の大きさに拡大してみる。

4つの頂点それぞれに対して拡大行列を乗算する。例えば頂点Aの場合、拡大行列を乗算すると、次のようになる。

同様に、頂点B〜Dの拡大後の値B’〜D’を求めると、次のようになる。

※4つの頂点のX,Y座標それぞれに対して拡大が行われていることがわかる。

これをプログラムで表すと、次のようになる。

1.変数の宣言・初期化

// 行列の定義
D3DXMATRIX PosMatrix;  // 頂点行列
D3DXMATRIX ZoomMatrix; // 拡大行列

// 行列の初期化(単位行列生成)
D3DXMatrixIdentity(&PosMatrix);
D3DXMatrixIdentity(&ZoomMatrix);

《POINT》

2.拡大行列に倍率を設定

// 拡大行列に倍率を設定
ZoomMatrix._11 = 2.0f; // X倍率
ZoomMatrix._22 = 2.0f; // Y倍率
ZoomMatrix._33 = 1.0f; // Z倍率

《POINT》

3.拡大/縮小処理(MyChara.v[0〜3]に、現在のポリゴンの座標がセットされているものとする)

// 拡大/縮小処理
int i;
for ( i=0 ; i < 4 ; i++ ) {
    // 現在の頂点座標を格納
    D3DXMatrixTranslation(&PosMatrix, MyChara.v[i].x, MyChara.v[i].y, MyChara.v[i].z);
    // 演算
    PosMatrix *= ZoomMatrix;
    // 演算結果を戻す
    MyChara.v[i].x = PosMatrix._41; // 演算結果をポリゴンのX座標に格納
    MyChara.v[i].y = PosMatrix._42; // 演算結果をポリゴンのY座標に格納
    MyChara.v[i].z = PosMatrix._43; // 演算結果をポリゴンのZ座標に格納
}

《POINT》

D3DXMatrixScaling()関数による倍率の設定

上記のやり方では、拡大行列への倍率のセットは、行列に値を直接代入している。

// 拡大行列に倍率を設定
ZoomMatrix._11 = 2.0f; // X倍率
ZoomMatrix._22 = 2.0f; // Y倍率
ZoomMatrix._33 = 1.0f; // Z倍率

これは、D3DXMatrixScaling()関数を使って次のように記述できる。

// 拡大行列に倍率を設定
D3DXMatrixScaling(&ZoomMatrix, x, y, z);

実際のプログラム

移動処理と同様、拡大/縮小処理も関数化したほうが使いやすい。拡大/縮小を行う関数を次のように作成する。NKC_Public.cppに作成するのがよいだろう。

ZoomMatrix()関数の作成

//----------------------------------------------------------------------------------------
// 関数名 : ZoomMatrix()
// 機能概要: 各頂点に対し、行列を使用して拡大/縮小を行う
//----------------------------------------------------------------------------------------
void ZoomMatrix(LPTLVERTEX v, float x, float y, float z)
{
    D3DXMATRIX PosMatrix, ZoomMatrix;
    int i;

    // 行列の初期化(単位行列生成)
    D3DXMatrixIdentity(&PosMatrix);
    D3DXMatrixIdentity(&ZomeMatrix);

    // 拡大行列に倍率を設定
    D3DXMatrixScaling(&ZoomMatrix, x, y, z);

    // 拡大/縮小処理
    for ( i=0 ; i<4 ; i++ ) {
        // 現在の頂点座標を格納
        D3DXMatrixTranslation(&PosMatrix, v[i].x, v[i].y, v[i].z);
        // 演算
        PosMatrix *= ZoomMatrix;
        // 結果を戻す
        v[i].x = PosMatrix._41;
        v[i].y = PosMatrix._42;
        v[i].z = PosMatrix._43;
    }

}

《POINT》

ZoomMatrix()関数の使用

MoveMatrix()関数を作成したら、この関数を使用してポリゴンの拡大/縮小が行われるかを確認する。例えば'Z'キーを押すと拡大し、'X'キーを押すと縮小するようにプログラムすると、次のようになる。

//--------------------------------------------------- 拡大縮小テスト
if ( gl_KeyTbl['Z'] & 0x80 ) ZoomMatrix(MyChara.Vertex, 1.1f, 1.1f, 1.0f);
if ( gl_KeyTbl['X'] & 0x80 ) ZoomMatrix(MyChara.Vertex, 0.9f, 0.9f, 1.0f);

《POINT》

※任意のプログラムに上記修正を行い、'Z'キーを押すと拡大、'X'キーを押すと縮小されることを確認すること。

キャラクタの中心を原点として拡大/縮小が行われるように修正する

確かにポリゴンの拡大/縮小は行われるようになったが、拡大/縮小を行うとポリゴンが元の場所から移動してしまうことがわかる。

これは、原点(0, 0)を中心にして拡大/縮小が行われるからである。よって、ポリゴンを中心に拡大/縮小を行うためには、次のように拡大/縮小処理を行う必要がある。

  1. ポリゴンを「原点」に移動させる。
  2. 拡大/縮小処理を行う。
  3. ポリゴンを元の場所に戻す。

上記のとおり、拡大/縮小関数を修正すると、次のようになる。

修正後の拡大/縮小処理関数

//----------------------------------------------------------------------------------------
// 関数名 : ZoomMatrix()
// 機能概要: 各頂点に対し、行列を使用して拡大/縮小を行う
//----------------------------------------------------------------------------------------
void ZoomMatrix(LPTLVERTEX v, float x, float y, float z)
{
    D3DXMATRIX PosMatrix, ZoomMatrix;
    float x1, y1
    int i;

    // ポリゴンの中心を求め、原点へ移動させる
    x1 = (v[2].x - v[0].x) / 2.0f + v[0].x;
    y1 = (v[3].y - v[1].y) / 2.0f + v[1].y;
    MoveMatrix(v, -x1, -y1, 0.0f);

    // 行列の初期化(単位行列生成)
    D3DXMatrixIdentity(&PosMatrix);
    D3DXMatrixIdentity(&ZomeMatrix);

    // 拡大行列に倍率を設定
    D3DXMatrixScaling(&ZoomMatrix, x, y, z);

    // 拡大/縮小処理
    for ( i=0 ; i<4 ; i++ ) {
        // 現在の頂点座標を格納
        D3DXMatrixTranslation(&PosMatrix, v[i].x, v[i].y, v[i].z);
        // 演算
        PosMatrix *= ZoomMatrix;
        // 結果を戻す
        v[i].x = PosMatrix._41;
        v[i].y = PosMatrix._42;
        v[i].z = PosMatrix._43;
    }

    // ポリゴンを元の位置へ戻す
    MoveMatrix(v, x1, y1, 0.0f);

}

《POINT》

※拡大/縮小処理関数(ZoomMatrix関数)を修正し、ポリゴンの中心点が変わることなく拡大/縮小が行われることを確認する。

アンチエイリアスの設定

テクスチャはポリゴンの大きさに合わせて拡大/縮小される。この時、アンチエイリアスを掛けると、拡大/縮小された画像がある程度滑らに、綺麗に表示されるよう調整される。
アンチエイリアスの指定は、描画の設定を行うところで指定するのがいいだろう。描画の設定はNKC_DGraphics.cppにあるInitRender()関数で行っている。InitRender()関数を次のように修正する。

//-----------------------------------------------------------------------------
// 関数名 : InitRender()
// 機能概要: 描画の設定など
//-----------------------------------------------------------------------------
void InitRender(void)
{
    // テクスチャの設定
    gl_lpD3ddev->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
    gl_lpD3ddev->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
    gl_lpD3ddev->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
    // アンチエイリアスの指定
    gl_lpD3ddev->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);

    // アルファ・ブレンディングを行う
    gl_lpD3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
    // 透過処理を行う
    gl_lpD3ddev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
    // 半透明処理を行う
    gl_lpD3ddev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
}

※上記修正を行い、拡大した画像がぼや〜っとした感じになることを確認する。

補足

多少の拡大/縮小ならアンチエイリアスで十分かもしれないが、元の大きさよりもはるかに大きく/小さくなるような拡大/縮小を行うと、画像が汚く表示されたり、縮小した時につぶれてしまったりする。また、小さな形状を描画するのに、わざわざ大きな画像を使うと無駄な処理が発生することもある。
このような場合、ミップ・マップという技術を使うことにより、ポリゴンの大きさに応じてテクスチャに使用する画像を切り替えることができる。
興味がある人は、書籍やネットで検索してみよう。なお、授業では取り扱わないことにする。


BACK(行列を用いた移動) NEXT(回転)