3Dのように背景をスクロールする

DirectX の D3DXLoadSurfaceFromFile で画像をロードして、下側(手前側)を拡大して3Dのように背景をスクロールします。
画像の変形は DirectX の LockRect で Surface のイメージデータへのポインタを取得して、ドットラインごとに縮小計算します。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プログラムの説明

  1. 背景画像を3Dのようにスクロールして、その上から3Dモデルを回転しながら描画します。
    このプログラムは LPDIRECT3DSURFACE9 のイメージ領域をロックして、直接画像を生成しています。
  2. g_pBackBuffer は Back Buffer の Surface です。
    g_pSurface は、元の画像を入力する Surface です。
    g_pSurface2 は、3Dのように加工した画像を生成する Surface です。
    g_Pos はスクロールの位置で 「0~479」を繰り返します。
    ImgFile[] は JPEG の背景画像で 640 * 480 のサイズです。
        /*******************************************************/
        /*  DX9 Surface で背景画像を3Dスクロール    前田 稔  */
        /*******************************************************/
        #define     NAME        "3D Scroll"
        #include    "MyD3D.h"
        #define     S_WIDTH     640     //Surface 幅
        #define     S_HEIGHT    480     //Surface 高さ
    
        // Global Area
        HWND                g_hWnd;
        MyD3D               *myd3d        = NULL;   // MyD3D Object Class
        LPDIRECT3DDEVICE9   g_pDEV        = NULL;   // 描写用デバイス
        LPDIRECT3DSURFACE9  g_pBackBuffer = NULL;   // バックバッファ
        LPDIRECT3DSURFACE9  g_pSurface    = NULL;   // 画像サーフェース
        LPDIRECT3DSURFACE9  g_pSurface2   = NULL;   // 変形サーフェース
        LPD3DXMESH          g_pMesh       = NULL;   // ティーポットの Object
        bool                g_bActive     = false;  // アクティブ状態
        long                g_Pos         = 0;
        char                ImgFile[]     = "c:\\data\\星空.jpg";
        D3DXVECTOR3         ViewForm(0.0f,0.0f,-5.0f);
        
  3. DirectX Graphics を初期化する関数 InitGraphics() です。
    myd3d を生成して DirectX のデバイスを取得します。
    ティーポットを生成して Back Buffer を取得します。
    元の画像を入力する Surface と、加工した画像を格納する Surface を生成します。
    PathCheck() で画像ファイルのパスを確認して、背景画像を入力します。
        // DirectX Graphics 初期化
        HRESULT  InitGraphics(void)
        {   HRESULT  hr;
    
            myd3d= new MyD3D(g_hWnd);
            myd3d->InitD3D(&g_pDEV);
    
            //ティーポットの生成
            D3DXCreateTeapot(g_pDEV,&g_pMesh,NULL);
    
            //Back Buffer を取得
            hr= g_pDEV->GetBackBuffer(0,0,D3DBACKBUFFER_TYPE_MONO,&g_pBackBuffer);
            if (FAILED(hr))
            {   MessageBox(NULL,"GetBackBuffer Error","Error Message", MB_OK);
                return FALSE;
            }
    
            //OffscreenSurface の作成
            if (FAILED(g_pDEV->CreateOffscreenPlainSurface(S_WIDTH,S_HEIGHT,D3DFMT_X8R8G8B8,
                               D3DPOOL_DEFAULT,&g_pSurface,NULL)))
            {   MessageBox(NULL,"OffscreenSurface の作成に失敗","Error Message", MB_OK);
                return FALSE;
            }
            if (FAILED(g_pDEV->CreateOffscreenPlainSurface(S_WIDTH,S_HEIGHT,D3DFMT_X8R8G8B8,
                               D3DPOOL_DEFAULT,&g_pSurface2,NULL)))
            {   MessageBox(NULL,"OffscreenSurface の作成に失敗","Error Message", MB_OK);
                return FALSE;
            }
    
            //画像ファイルの読み込み
            if (myd3d->PathCheck(ImgFile)==-1)
            {   MessageBox(NULL,ImgFile,"Img File Error",MB_OK);
                return FALSE;
            }
            hr= D3DXLoadSurfaceFromFile(g_pSurface,NULL,NULL,ImgFile,NULL,D3DX_FILTER_NONE,0,NULL);
            if (FAILED(hr))
            {   MessageBox(NULL,ImgFile,"IMG File Open Error",MB_OK);
                return FALSE;
            }
            return TRUE;
        }
        
  4. 背景画像をスクロールしながらティーポットを描画する Render() 関数です。
    Scroll3D() 関数で背景画像を3Dのように加工して g_pSurface2 に生成します。
    StretchRect() で g_pSurface2 の画像を BackBuffer に転送します。
    3Dの描画環境を設定して、背景画像の上からティーポットを回転しながら描画します。
        // OffscreenSurface に3Dの背景を生成して BackBuffer に転送
        VOID  Render()
        {   RECT  Rect;
    
            g_pDEV->Clear(0,NULL,D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,D3DCOLOR_XRGB(0,0,0),1.0f,0);
            // 画像の変形
            Scroll3D(g_Pos,4.0f,1.0f);
            // バックバッファへ描画
            SetRect(&Rect,0,0,S_WIDTH,S_HEIGHT);
            g_pDEV->StretchRect(g_pSurface2,&Rect,g_pBackBuffer,&Rect,D3DTEXF_NONE);
            // ティーポットを描画
            if (SUCCEEDED(g_pDEV->BeginScene()))
            {   g_pDEV->BeginScene();
                SetupMatrices();        //描画環境の設定
                g_pMesh->DrawSubset(0);
                g_pDEV->EndScene();
            }
            g_pDEV->Present(NULL,NULL,NULL,NULL);
        }
        
  5. 描画環境を設定する SetupMatrices() 関数です。
    timeGetTime() で背景画像をスクロールする g_Pos とティーポットを回転するワールド座標を計算します。
    ティーポットを描画するために、マテリアルとライトを設定します。
        //描画環境の設定
        void  SetupMatrices(void)
        {   RECT            rect;
            D3DXMATRIX      matWorld;
            D3DXMATRIX      matView;
            D3DXMATRIX      matProj;
    
            GetClientRect(g_hWnd,&rect);
            g_Pos= (S_HEIGHT-1)-(long)(timeGetTime()/5)%S_HEIGHT;
            //World 座標をY軸を中心に自動回転
            D3DXMatrixRotationY(&matWorld,timeGetTime()/500.0f);
            g_pDEV->SetTransform(D3DTS_WORLD,&matWorld);
            //View 座標の設定
            D3DXMatrixLookAtLH(&matView,&ViewForm,&D3DXVECTOR3(0.0f,0.0f,0.0f),&D3DXVECTOR3(0.0f,1.0f,0.0f));
            g_pDEV->SetTransform(D3DTS_VIEW,&matView);
            //透視変換の設定
            D3DXMatrixPerspectiveFovLH(&matProj,D3DXToRadian(45.0f),(float)rect.right/(float)rect.bottom,1,100);
            g_pDEV->SetTransform(D3DTS_PROJECTION,&matProj);
            //Material と Light の設定
            myd3d->SetMaterial(0.9f,0.7f,0.2f);
            myd3d->SetLight();
        }
        
  6. このプログラムのテーマである、背景画像を3Dのように加工する関数です。
    g_pSurface の領域を読み出し専用でロックして、その領域を S_Rect に取得します。
    g_pSurface2 の領域を書き込み用でロックして、その領域を D_Rect に取得します。
    S_Rect 及び D_Rect 構造体の pBits がイメージデータへのポインタです。
    Pitch には1ドットラインのバイト数が格納されています。
    計算速度を上げるために、ピクセル単位(DWORD)で計算しています。
    画像が生成できれば、ロックした領域を解除して下さい。
        // Surface の画像を加工して Surface2 に格納する
        HRESULT Scroll3D(long pos,float rate,float side)
        {   D3DLOCKED_RECT  D_Rect,S_Rect;
            RECT            Rect;
            DWORD           *pDBits,*pSBits;
            int             PitchDWORD,dx,dy,xp,yp;
            static  float   width,height,newwx,a;
    
            SetRect(&Rect,0,0,S_WIDTH,S_HEIGHT);
            if (g_pSurface->LockRect(&S_Rect,&Rect,D3DLOCK_READONLY)!=D3D_OK)
            {   MessageBox(NULL,"LockRect Error","Surface Error",MB_OK);
                return FALSE;
            }
            //SetRect(&Rect,0,0,S_WIDTH,S_HEIGHT);
            if (g_pSurface2->LockRect(&D_Rect,&Rect,D3DLOCK_DISCARD)!=D3D_OK)
            {   MessageBox(NULL,"LockRect Error","Surface Error",MB_OK);
                return FALSE;
            }
    
            pDBits= (DWORD*)D_Rect.pBits;
            pSBits= (DWORD*)S_Rect.pBits;
            PitchDWORD= S_Rect.Pitch/4;
    
            // 図形変形処理
            width = (float)S_WIDTH;
            height= (float)S_HEIGHT;
            newwx = width/rate;
            for(dy=0; dy<S_HEIGHT; dy++)
            {   yp= (pos+dy)%S_HEIGHT;
                a= (width-newwx) * (S_HEIGHT-dy) / height + newwx;
                for(dx=0; dx<PitchDWORD; dx++)
                {   xp= (int)(a * dx / width + (width-a)/side);
                    *(pDBits+dy*PitchDWORD+dx)= *(pSBits+yp*PitchDWORD+xp);
                }
            }
            g_pSurface->UnlockRect();
            g_pSurface2->UnlockRect();
            return TRUE;
        }
        
  7. Scroll3D() 関数のパラメータの説明です。
    名前説明
    Scroll3D3Dのように変形する関数
    pos スクロールの位置(描画を開始するY座標)
    rate 拡大率
    size 拡大するX座標の中心(2.0 で中央)
  8. size の値は指数関数のように効いてきます。

超初心者の方のために全ソースコードを掲載します。 (^_^;)
全ソースコード

前田稔(Maeda Minoru)の超初心者のプログラム入門

超初心者のプログラム入門(DirectX9 game program)

超初心者のプログラム入門(DirectX10 game program)