1. 程式人生 > >渲染到紋理(Render To Texture, RTT)詳解

渲染到紋理(Render To Texture, RTT)詳解

 RTT是現在很多特效裡面都會用到的一項很基本的技術,實現起來很簡單,也很重要。但是讓人不解的是網上搜索了半天只找到很少的文章說這個事兒,不知道是因為太簡單還是因為這項技術已經出現很長時間了。總之我是在摸索這個東西的時候繞了不少彎子。現在把具體的實現方法寫下來。

渲染到紋理,顧名思義就是把渲染目標從幀快取變成一個紋理。這樣就可以把一個場景渲染後在進行Post Process,做出現在流行的各種特效。另外在利用GPU做通用計算的時候程式也是通過RTT和GPU交換資料的。

實現步驟:
  1. 宣告變數
    LPDIRECT3DTEXTURE9 pRenderTexture = NULL; // 目標紋理
    LPDIRECT3DSURFACE9 pRenderSurface = NULL,pBackBuffer = NULL, pTempSurface;
    // pRenderSurface是pRenderTexture 對應的Surface
    // pBackBuffer用於儲存原來的Render Target

  2. 建立一個紋理作為渲染目標(Render Target)
    //注意這裡的第三個引數必須為
    D3DUSAGE_RENDERTARGET
    //第四個引數決定紋理的格式,不同的場景會要求不同的格式

    pd3dDevice->CreateTexture(TEX_WIDTH,TEX_HEIGHT,1,D3DUSAGE_RENDERTARGET,D3DFMT_R5G6B5,D3DPOOL_DEFAULT,&pRenderTexture,NULL);

    //獲得
    pRenderTexture對應的Surface
    pRenderTexture->GetSurfaceLevel(0,&pRenderSurface);
  3. 渲染場景
    //這裡儲存下原來的Render target,在做完RTT後再恢復
    pd3dDevice->GetRenderTarget(0,&pBackBuffer);
    if( SUCCEEDED( pd3dDevice->BeginScene() ) )
    {
    //設定我們的紋理為render target
    pd3dDevice->SetRenderTarget(0, pRenderSurface);
    pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DXCOLOR(0.0f,0.00f,0.00f,1.00f), 1.0f, 0);


    //重新將render target設定為幀快取
    pd3dDevice->SetRenderTarget(0, pBackBuffer);

    pd3dDevice->EndScene();
    pBackBuffer->Release();
    }
  4. 善後
    SAFE_RELEASE(pRenderSurface);
    SAFE_RELEASE(pRenderTexture);
這裡需要注意的幾點:
  • 渲染的時候要選擇正確的紋理格式。如果你要在紋理裡面儲存高精度浮點數的話。通常所用的A8R8G8B8格式每一個顏色分量只有8位,只能表示0-255。詳情可以參考DirectX SDK Help中關於D3DFORMAT的說明。
  • 如果你的紋理長寬比和幀快取的不同的話,那麼你需要在切換RenderTarget以後重新設定投影矩陣。否則渲染出來的影象會被拉伸。
  • 紋理的大小不能太大。經過試驗發現是在視窗模式下面視窗和紋理的大小不能超過螢幕減去工作列的大小。如果超過的話似乎對紋理的任何操作都不會有效果。
  • 如果想要驗證自己渲染出來的紋理是否正確,可以用D3DXSaveTextureToFile把紋理儲存為影象。
  • 如果想要直接訪問紋理中的值則要麻煩一些。按照SDK文件裡面的說法,作為RenderTarget的紋理是儲存在視訊記憶體上面的,無法lock與unlock。要向訪問其中的值需要做如下操作:
    LPDIRECT3DTEXTURE9 text;
    LPDIRECT3DSURFACE9 surf;
    D3DLOCKED_RECT lockbits;
    pd3dDevice->CreateTexture(TEX_WIDTH,TEX_HEIGHT,1,0,D3DFMT_R5G6B5, D3DPOOL_SYSTEMMEM, &text, NULL);
    text->GetSurfaceLevel(0,&surf);
    if (pd3dDevice->GetRenderTargetData(pRenderSurface, surf) == D3D_OK)
    if (surf->LockRect(&lockbits, NULL, D3DLOCK_READONLY) == D3D_OK)
    {
    pRenderSurface->UnlockRect();
    float* bits=(float*)(lockbits.pBits);
    // SAVE BITS TO TEXT FILE
    FILE* ofile = fopen("output.txt", "w");
    for (int i=0; i<64; i++)
    {
    for (int j=0; j<64; j++)
    fprintf(ofile, "(%2.2f,%2.2f,%2.2f) ", bits[i*64*4+j*4], bits[i*64*4+j*4+1], bits[i*64*4+j*4+2]);
    fprintf(ofile, "\n");
    }
    fclose(ofile);
    }
    text->Release();
    surf->Release();

這個技術可以用來在多通道渲染中傳遞渲染結果。比如可以把RTT出來的結果用來作為第二編渲染中的紋理來使用,這樣可以實現水面反射等效果。另外在通用計算中可以用來儲存資料。例如可以把GPU數值計算以後的結果儲存在紋理中,再用上面所說的方法把數字讀出來(如果真要這麼做的話別忘了把紋理格式設定為足夠大精度的格式,比如說A32B32G32R32F)。

文章引用自:http://ping.csdn.net/tag/hlsl/