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

來源:互聯網
上載者:User
渲染到紋理(Render To Texture, RTT)詳解

標籤:hlsl 渲染 圖形學 
分類:遊戲編程
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)。 擷取BackBuffer地址的問題.

偽碼描述如下:
1:D3Dpp.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;  // 允許鎖住後備緩衝
2:Device->GetBackBuffer( 0, D3DBACKBUFFER_TYPE_MONO, &Surface)
3:D3DLOCKED_RECT d3dl_rect; (這個是32位色的版本,改為16位色很簡單)
     Surface->LockRect(&d3dl_rect, NULL, NULL );
     int Pitch32 = d3dl_rect.Pitch / 4;
     DWORD* p = (DWORD*) d3dl_rect.pBits;  // 這個P就是想要的東東了,盡情F吧!
     Surface->UnlockRect(); 多通道渲染一般SetRenderTarget(0,pRT0);SetRenderTarget(1,pRT1);SetRenderTarget(2,pRT2);就會與colorbuffer 對應,由COLOR0,COLOR1,COLOR2分別對應之 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.