2017-01-06 78 views
0

我需要幫助,在我的遊戲中組織着色器。HLSL - 組織着色器和技術,不會迷路

遊戲使用頂點和像素着色器進行紋理和照明。一些物體紋理,其他只是有色,然後有幾個照明算法 - 光照貼圖,漫反射和鏡面光照,一些只有一些物體得到的影子等。

我的第一個想法是有一個大的,漂亮的着色技術它可以根據參數處理所有事情。這是偉大的,以保持(僞代碼給想法):

float4 PixelShaderFunction(input) 
{ 
    if (UseTexture) 
     objectColor = tex2D(...) 
    else 
     objectColor = ObjectColor; 

    if (UseAmbient) 
     ... 

    if (UseDiffuseLight) 
     ... 

    // etc... 
} 

但是表現得很差。

然後我創建了多種技術來避免if分支,所以每種技術只使用特定對象所需的東西。所以如果對象不接受陰影,那裏沒有代碼。如果它沒有使鏡面光線,沒有代碼。另外我將通用功能分組到功能中。像這樣(真實代碼現在):

float4 Textured_PixelShader(Textured_VsOut input) : COLOR0 
{ 
    float4 lightLevel = 0; 
    float4 objectColor = GetObjColorTex(input.TexUV); 

    // calculate main table lighting 
    PsAddTableLight(input.WorldPosition, input.Normal, lightLevel); 

    // calculate cloth lighting 
    PsAddClothLight(input.WorldPosition, input.Normal, lightLevel); 

    // calculate fill light - do we need it? 
    PsAddFillLight(input.Normal, lightLevel); 

    // add ambient light 
    PsAddAmbientLight(lightLevel); 

    // apply light 
    PsApplyDiffuseLight(lightLevel, objectColor); 

    // add speculars 
    PsAddSpecularBlurred(input.WorldPosition, input.Normal, objectColor); 

    // final work 
    return PsFinalize(objectColor); 
} 

性能的提高是巨大的。

但是我在維護着色器時迷路了。每隔一天我需要添加一項新技術,因爲還沒有組合texture + lightmap + this_kind_of_shadow + specular或任何我需要的。他們中的一些人得到這樣的名字,另一些人則以他們所使用的對象命名,因爲只有一個具有這種組合的對象。它變得一團糟。

我有那麼兩個問題:

  • 爲什麼我不能有那些if報表?我讀了很多關於條件執行如何傷害GPU的內容,但是我的ifs只依賴於所有渲染像素(或vert)具有相同值的着色器參數。爲什麼他們不能很快?我真的很想念他們。
  • 將此代碼分成不同的着色器/技術/文件的最佳方式是什麼?有沒有好的標準或規則?

回答

0

爲什麼我不能有那些if語句?我讀了很多關於條件執行如何傷害GPU的內容,但是我的ifs只依賴於所有渲染像素(或vert)具有相同值的着色器參數。爲什麼他們不能很快?我真的很想念他們。

思考:

主要的問題是在這裏,編譯器只看到一個布爾表達式,而不是語義信息,你的變量是着色器常量。這只是一種特殊情況,並且可能會變得複雜,例如,如果您使用帶有函數的更復雜的布爾表達式,它也只使用着色器常量。我認爲要防止着色器編譯器開發人員遇到很多麻煩,他們會選擇讓它們像現在一樣普通。

事實:

HLSL-Documentation for if所述,有兩種模式,如果,無論是flattenbranch。通過編譯器flatten,編譯器會翻出if的兩側,所以首先計算它們,然後從右側獲取結果。由於branch只執行右側,因爲首先評估布爾值,但只有在不使用像tex2D這樣的任何漸變函數時才能使用此模式,因爲它們依賴於相鄰片段,因此需要在每個片段不得跳過。在你的情況下,我非常確定你正在使用這樣的函數,所以編譯器爲你的ifs選擇了flatten,這導致了一個完全執行和緩慢的着色器。

將此代碼分成不同着色器/技術/文件的最佳方法是什麼?有沒有好的標準或規則?

就我所知沒有很好的標準,但我認爲像Unity或Unreal這樣的引擎是一個很好的地方有更深入的瞭解,他們如何管理着色器。上一次查看虛幻時,它們會在需要時動態生成每個着色器,因此着色器代碼將從其着色器生成器生成,然後編譯該着色器生成器。

在我自己的小引擎中,我使用了與您類似的方法,但不使用動態分支,而是使用預處理器指令#if, #elif, #else, and #endif。如果引擎遇到需要渲染的着色器,它會設置正確的定義,然後用D3DCompile動態編譯它們。爲了防止口吃,我將編譯的着色器保存爲光盤,並在編譯之前,我正在尋找着色器,如果它已經被編譯過。

+0

不幸的是,我似乎無法按照自己的方式(動態編譯),因爲monogame不支持它,即使它只會限於某些平臺。但這是我認可的偉大而有價值的答案 - 謝謝。 – Arek

+0

至於爲什麼'如果'很慢 - 我仍然不確定,但很快就會做一些複驗來驗證性能放緩。根據所有來源,它應該是快速的,因爲對於所有呈現的東西,「if」條件是相同的。 – Arek