2017-11-10 266 views
0

我正在使用我的光手在一些手機上面臨一些性能問題。 着色器根據正常圖計算線條和點光線。 這也表明命中目標(這是一個2D自上而下的射手遊戲)。OpenGL ES着色器在較舊的手機上出現巨大的性能問題

我正在開發索尼Xperia Z5 Compact,其中一切正常 就好。然後我在我非常古老的三星Galaxy S2上試了一下,其中 真的很慢。由於電話的年齡我不在乎。

現在我嘗試了Galaxy S4和Galaxy S5,但它似乎並沒有比Galaxy S2更快地運行 。我也有編輯時間(大約90秒),但我設法通過優化代碼將它簡化爲幾秒鐘(儘管我認爲它仍然很雜亂,但我並不真正注意着色器)。

我真的不知道這裏的瓶頸是什麼,以及爲什麼它沒有在S4和S5上跑得更快。

這是我的着色器:

#ifdef GL_ES 
#define LOWP lowp 

precision highp float; 

#else 
    #define LOWP 
#endif 


#define MAX_NUM_POINTLIGHTS 16 


uniform vec3 lpos[MAX_NUM_POINTLIGHTS]; // Light position 
uniform vec3 foff[MAX_NUM_POINTLIGHTS]; // Light falloff 
uniform vec4 acol[MAX_NUM_POINTLIGHTS]; // Ambient color 
uniform vec4 lcol[MAX_NUM_POINTLIGHTS]; // Light color 



//textures 
uniform sampler2D u_texture1; // diffuse texture 
uniform sampler2D u_texture2; // normalmap 


varying vec4 vColor; 
varying vec2 vTexCoord; 
varying float vFlags; 


uniform vec2 Resolution;  //resolution of screen 

const float WORLD_WIDTH = 1440.0; 
const float WORLD_HEIGHT = 2560.0; 

vec3 getPointLightColor(const vec4); 
vec3 rotateVector(const vec3 vector, const float angle); 
vec2 screenCoordToWorldCoord(const vec2 screencoord); 
vec3 calculatePointLight(const vec4 DiffuseColor, vec3 LightPos, const vec3 Falloff, const vec4 LightColor, const vec4 AmbientColor); 



const float stdratio = WORLD_HEIGHT/WORLD_WIDTH; 
vec2 worldFragCoord; 
const float worldRatio_W_DIV_H = WORLD_WIDTH/WORLD_HEIGHT; 
const vec2 worldSize = vec2(WORLD_WIDTH, WORLD_HEIGHT); 



// Light variables 
vec3 NormalMap; 
vec2 worldFragCoordNormalized; 
vec3 N; 


void main() { 

    worldFragCoord = screenCoordToWorldCoord(gl_FragCoord.xy); 

    // Common light calculations 
    NormalMap = texture2D(u_texture2, vTexCoord).rgb; 
    worldFragCoordNormalized = worldFragCoord/vec2(1440.0, 2560.0); 
    N = normalize(NormalMap * 2.0 - 1.0); 


    vec4 DiffuseColor = texture2D(u_texture1, vTexCoord); 
    vec2 fragcoord = gl_FragCoord.xy; 

    vec3 pointcolor = getPointLightColor(DiffuseColor); 



    vec4 finalColor; 

    // green channel of vColor indicates hit 

    if (vColor.g > 0.0 && vColor.a == 0.0) { 
     vec4 fragcol = vec4(pointcolor, DiffuseColor.a); 
     vec4 addColor; 
     if (vColor.g > 0.67) 
      addColor = vec4(1.0,1.0,1.0, DiffuseColor.a*vColor.g); 
     else if (vColor.g > 0.52) 
      addColor = vec4(1.0,0.0,0.0, DiffuseColor.a*vColor.g); 
     else if (vColor.g > 0.37) 
      addColor = vec4(0.0,0.0,1.0, DiffuseColor.a*vColor.g); 
     else if (vColor.g > 0.22) 
      addColor = vec4(1.0,1.0,0.0, DiffuseColor.a*vColor.g); 
     else 
      addColor = vec4(0.0,1.0,1.0, DiffuseColor.a*vColor.g); 

     finalColor = addColor*addColor.a + fragcol*(1.0-addColor.a); 
    } 
    else 
     finalColor = vec4(pointcolor, DiffuseColor.a); 



    gl_FragColor = finalColor; 

} 



vec3 rotateVector(const vec3 vector, const float angle){ 

    float degree = radians(360.0*angle); // Angle is normalized to 0 - 1 

    float cos_ = cos(degree); 
    float sin_ = sin(degree); 

    return vec3(vector.x*cos_ - vector.y*sin_, vector.x*sin_ + vector.y*cos_, vector.z); 
} 



vec3 calculatePointLight(const vec4 DiffuseColor, vec3 LightPos, const vec3 Falloff, const vec4 LightColor, const vec4 AmbientColor){ 


    if (LightPos.x == 0.0 && LightPos.y == 0.0) 
     return vec3(0.0); 



    LightPos.xy = LightPos.xy/worldSize; 


    //The delta position of light 
    vec3 LightDir = vec3(LightPos.xy - worldFragCoordNormalized, LightPos.z); 

    //Correct for aspect ratio 
    LightDir.x *= worldRatio_W_DIV_H; 

    //Determine distance (used for attenuation) BEFORE we normalize our LightDir 
    float D = length(LightDir); 

    //normalize our vectors 

    vec3 L = normalize(LightDir); 
    vec3 NN = N; 
    if (vColor.a == 0.0) 
     NN = normalize(rotateVector(NN, vColor.r)); 

    //Pre-multiply light color with intensity 
    //Then perform "NN dot L" to determine our diffuse term 
    vec3 Diffuse = (LightColor.rgb * LightColor.a) * max(dot(NN, L), 0.0); 

    //pre-multiply ambient color with intensity 
    vec3 Ambient = AmbientColor.rgb * AmbientColor.a; 

    //calculate attenuation 
    float Attenuation = 1.0/(Falloff.x + (Falloff.y*D) + (Falloff.z*D*D)); 

    //the calculation which brings it all together 
    vec3 Intensity = Ambient + Diffuse * Attenuation; 
    vec3 FinalColor = DiffuseColor.rgb * Intensity; 


    return FinalColor; 
} 



vec3 getPointLightColor(const vec4 DiffuseColor){ 

    vec3 sum = vec3(0.0); 

    for (int i = 0; i < MAX_NUM_POINTLIGHTS; i++) 
    { 
     sum += calculatePointLight(DiffuseColor, lpos[i], foff[i], lcol[i], acol[i]); 
    } 

    return sum; 

} 




vec2 screenCoordToWorldCoord(const vec2 screencoord){ 
    float ratio = Resolution.y/Resolution.x; 

    vec2 resCoord; 
    if (ratio == stdratio){ 
     // Ratio is standard 
     resCoord = screencoord * (WORLD_HEIGHT/Resolution.y); 
    } else if (ratio > stdratio) { 
     // Screen gets extended vertically (black bars top/bottom) 

     float screenheight = Resolution.x * stdratio; 
     float bottom = (Resolution.y - screenheight)/2.0; 
     resCoord = vec2(screencoord.x, screencoord.y - bottom); 
     resCoord *= (WORLD_WIDTH/Resolution.x); 

    } else { 
     // Screen gets extended horizontally (black bars left/right) 

     float screenwidth = Resolution.y/stdratio; 
     float left = (Resolution.x - screenwidth)/2.0; 
     resCoord = vec2(screencoord.x - left, screencoord.y); 
     resCoord *= (WORLD_HEIGHT/Resolution.y); 

    } 

    return resCoord; 
} 
+1

將嘗試找到時間來消化其他地方比我的手機,但作爲一般規則:循環和條件是麻煩,往往甚至與做不必要的工作,並通過一些組合夾具,步驟等有效地丟棄它。 – Tommy

+0

我有這些循環展開之前,但我讀了循環與常量就好了。你認爲這些條件是否會導致這種情況? – Draz

+0

是的 - 假設編譯器很聰明(而且相當安全),如果需要的話,帶有常量的循環會自動展開。添加一個'if(我 Tommy

回答

1

這是我該怎麼做才能解決它是什麼:

  1. 刪除screenCoordToWorldCoord。這是一個簡單的轉換,你可以用矩陣乘或者幾個點積來完成,或者更好的是,將工作移動到頂點着色器,並將結果傳遞給變量而不是從gl_FragCoord構造。

  2. 爲每個光照計數編譯不同版本的着色器,並展開for循環。您也可以將if放在calculatePointLight的頂部。

  3. 刪除所有剩餘的if語句 - 某些設備討厭條件。用數學來代替邏輯,step函數有幫助。

  4. 有什麼方法可以丟棄rotateVector?我無法弄清楚它在做什麼,但它很昂貴,並且覺得它在片段着色器中不應該是必需的。至少,它不需要在內部循環中,因爲無論光線如何,結果都是相同的。使用某種矩陣乘法可能會更好,而不是使用sin/cos

  5. 正確使用精度。有些設備可以在lowp/mediump比在highp更快地做數學。經驗法則 - 顏色爲lowp,法線爲mediump,職位爲highp

  6. 在CPU上做一些光線剔除。我想不是所有的燈都會影響每個像素。如果你可以將場景切成瓦片,只讓最重要的燈光數過去,那麼你可以做更少的工作。

  7. LightPos.xy/worldSize看起來你可以在CPU上做一次,而不是每個像素一次。

沒有快速修復恐怕。

+0

聽起來很合理,謝謝。我會盡力改變這一切。函數rotateVector正在旋轉法線貼圖的法線信息 – Draz

+0

我不知道任何實現'lowp'的現代設備與'mediump'有什麼不同 - 它們都傾向於映射到fp16硬件。 – solidpixel