1. 程式人生 > >【D3D11遊戲編程】學習筆記九:編譯Effect的方法

【D3D11遊戲編程】學習筆記九:編譯Effect的方法

編譯工具 學習筆記 事先 end 應用程序 geb ade ive 文件名

  在D3D11應用程序中,對於寫好的Effect程序進行編譯有如下幾種常見方法:
  
  1. 在運行期編譯
  
  用這種方法,我們只管寫好Effect代碼即可,不用關心其編譯問題,而是在C++程序中,通過調用相應的API先對Effect進行編譯,得到編譯後的內容,再用之來創建Effect接口。在上個例子繪制立方體時我們用的即這個方法,其相關代碼再看一下:
  
  [cpp] view plain copy
  
  //兩個ID3D10Blob用來存放編譯好的shader及錯誤消息
  
  ID3D10Blob *shader(NULL);
  
  ID3D10Blob *errMsg(NULL);
  
  //編譯effect
  
  HRESULT hr = D3DX11CompileFromFile(L"FX/BasicDraw.fx",0,0,0,"fx_5_0",flag,0,0,&shader,&errMsg,0);
  
  //如果有編譯錯誤,顯示之
  
  if(errMsg)
  
  {
  
  MessageBoxA(NULL,(char*)errMsg->GetBufferPointer(),"ShaderCompileError",MB_OK);
  
  errMsg->Release();
  
  return FALSE;
  
  }
  
  if(FAILED(hr))
  
  {
  
  MessageBox(NULL,L"CompileShader錯誤!",L"錯誤",MB_OK);
  
  return FALSE;
  
  }
  
  我們通過使用ID3D10Blob接口來存放得到的編譯後內容及相關編譯錯誤信息,編譯後的內容存放在shader變量中,在確保編譯無誤的情況下用它來創建Effect。此外,在Debug模式下,為了便於檢查shader代碼相關問題,我們通過flag變量來指定編譯方式,如下所示:
  
  [cpp] view plain copy
  
  UINT flag(0);
  
  #if defined(DEBUG) || defined(_DEBUG)
  
  flag |= D3D10_SHADER_DEBUG;
  
  flag |= D3D10_SHADER_SKIP_OPTIMIZATION;
  
  #endif
  
  這樣在Release即可按正常模式進行。
  
  2. 使用Effect編譯工具
  
  在安裝好DirectX SDK後,在Utilities\bin目錄下(無論x64還是x86),有專門用來對Shader代碼進行編譯的工具,即“fxc”。通過它,我們可以在運行程序前先對我們寫的Effect程序進行編譯,確保無誤,並且輸出編譯好的二進制文件。在運行期,應用程序通過直接讀取二進制shader代碼來創建Effect即可。這樣的好處還是很多的,一方面,我們可以事先把可能的問題排除掉,確保shader準備無誤;另一方面,當shader代碼特別龐大的時候,可以有效避免程序運行是初始化階段因為臨時對shader進行編譯而造成的漫長的等待。如果用過SDK附帶的Sample Browser中的示例程序的話,就會很熟悉,相當多的示例在運行時的開始階段會有好幾秒甚至幾十秒的編譯shader的時間,好在微軟專門對我們進行提示,告訴我們程序正在編譯shader,否則一般的程序初始化這麽久的話真是很難容忍滴。。。還有一個好處就是,在我們不想公開自己的shader代碼時,可以事先編譯成二進制文件,讓C++程序直接讀取二進制代碼即可,這樣就不必把shader代碼連同可執行程序一起發布了。
  
  說了這麽多,來看下如何用這個編譯工具吧。
  
  fxc用法其實很簡單,常見的幾個flag就那麽幾個,很好記住。而且就算記不住,SDK也很方便查閱。首先,當然是打開命令行啦~如果沒有把該工具的目錄加到系統變量中的話就要先把命令行的工作目錄先調到fxc對應目錄。
  
  要編譯effect,首先要指定編譯所用的shader model,在我們學習d3d11過程中,一般用最高等級:fx_5_0,這跟我們effect代碼中pass內指定的是一樣的,前面跟 /T 。然後指明輸出文件名,用 /Fo 指定,接輸出文件名;最後直接註明我們要編譯的文件名。這就是一個最簡單的命令,如下:
  
  [plain] view plain copy
  
  fxc /T fx_5_0 /Fo BasicDraw.fxo BasicDraw.fx
  
  如果為了Debug目的,只要多插入幾個相應的編譯參數即可。一般而言,在Debug過程中我們要組上編譯器進行優化,通過增加 /Od 實現,顯示Debug信息,增加 /Zi 參數。 此外,如果對對應的匯編指令感興趣的話,也可以讓編譯把匯編指令輸出到一個文件當中,增加 /Fc即可。這時的命令如下:fxc /Od /Zi /T fx_5_0 /Fo BasicDraw.fxo BasicDraw.fx
  
  [plain] view plain copy
  
  fxc /Fc /Od /Zi /T fx_5_0 /Fo BasicDraw.fxo BasicDraw.fx
  
  這就是兩個我們最常用到的編譯effect時的命令。想了解更詳細的參數說明,請參考SDK。
  
  對應用程序而言,重要的只有輸出的二進制文件,通過C++的輸入流讀取該文件即可。讀取二進制文件的代碼如下:
  
  [cpp] view plain copy
  
  ifstream fin("FX/BasicDraw.fxo",ifstream::binary);
  
  fin.seekg(0,ifstream::end);
  
  int size = static_cast<int>(fin.tellg());
  
  fin.seekg(0,ifstream::beg);
  
  vector<char> compiledShader(size);
  
  fin.read(&compiledShader[0],www.dfgjyl.cn/ size);
  
  fin.close();
  
  //從編譯好的Effect創建Effect
  
  HRESULT hr = D3DX11CreateEffectFromMemory(&compiledShader[0],size,0,g_device,&g_effect);
  
  首先創建輸入流,指定二進制文件名和讀取方式,這裏顯然設置為二進制,即ifstream::binary(或 ios::binary)。然後把輸入流定位到文件末尾,保存其位置,該位置其實就是文件的大小。這時再把輸入流重新定位到文件一開始,並通過調用輸入流的read函數讀取文件內容到vector變量當中,最後關閉輸入流。這時vector當中存放的即是我們需要的編譯好的effect代碼。最後通過它來創建Effect即可。
  
  3. 讓Visual Studio在Build時自動幫我們編譯Effect
  
  如果不想每次都手動來編譯Effect文件,我們也可以指定IDE來幫我們自動完成。方法如下:
  
  1. 首先在工程目錄中把Effect文件添加進來:右鍵項目名->Add-New Filter,取個名字,比如”FX“,表示我們的Effect文件
  
  接著在該目錄下把對應的effect文件添加進來:
  
  2. 右鍵相應的Effect文件,選"Properties”(屬性),在彈出的對話框中按下圖所示選擇"Custom Build Tool“(中文版選擇對應的就行了):
  
  點右下角”應用",這時,還是該對話框,左側欄會多出一項“Custom Build Tool”,雙擊它,然後在右邊開始輸入相應內容。
  
  對應於Debug模式,在Command Line行輸入 www.thyl21.com/ fxc /Fc /Od /Zi /T fx_5_0 /Fo "%(RelativeDir)\%(Filename).fxo" "%(FullPath)"。看得出就是我們手動編譯Effect時的命令。Outputs行輸入 %(RelativeDir)www.dfgj157.com \%(Filename).fxo,即輸入目錄及文件名。如圖所示:
  
  對應Release模式很相似,只是Command Line那一行換成我們正常手動編譯Effect時對應的命令即可,即:fxc /T fx_5_0 /Fo "%(RelativeDir)\%(Filename).fxo" "%(FullPath)"。Outputs行與上面一樣。配置完成,點確定。
  
  這時,每當我們Build我們的項目時,IDE會自動幫我們編譯Effect並生成相應的輸出文件。在C++程序中直接讀取二進制文件即可。

【D3D11遊戲編程】學習筆記九:編譯Effect的方法