Проблема с производительностью GLSL во фрагментном шейдере при добавлении vec4

У меня есть довольно простой фрагментный шейдер, используемый для обработки ситуации с несколькими источниками света (код ниже урезан для ясности, показаны только два источника света).

Общая идея состоит в том, чтобы суммировать различные вклады освещения для каждого фрагмента, и это работает нормально, однако я обнаружил, что это нестабильно на моем оборудовании (Android HTX Desire X).

Измеряя FPS, становится очевидным, что есть одна дополнительная строка vec4, из-за которой FPS падает на 10.

Что может быть причиной такого снижения производительности при такой, казалось бы, простой операции?

void main (void)
{
    vec4 v = u_ViewModelMatrix * vec4(v_Vertex, 1.0);
    vec3 nv = normalize(-v.xyz);
    vec3 normalVector = normalize((u_ViewModelTransposeMatrix * vec4(normalize(v_Normal), 0.0)).xyz);

    vec4 finalColour = vec4(0.0, 0.0, 0.0, 1.0);

    // LIGHT 0
    lightPosition = vec4(u_LightData[2], u_LightData[3], u_LightData[4], 1);
    lightColour = vec4(u_LightData[5], u_LightData[6], u_LightData[7], 1.0) * u_LightData[0];

    lightVector = normalize((u_ViewMatrix * lightPosition).xyz - v.xyz);
    halfwayVector = normalize(lightVector + nv);

    facing = dot(normalVector, lightVector);
    if (facing >= 0.0) {
        finalColour = finalColour + diffuseColour * facing * lightColour;
    }

    // LIGHT 1
    lightPosition = vec4(u_LightData[LIGHTS_FLOATS_PER_LIGHT*1+2],
                         u_LightData[LIGHTS_FLOATS_PER_LIGHT*1+3],
                         u_LightData[LIGHTS_FLOATS_PER_LIGHT*1+4],
                         1);
    lightColour = vec4(u_LightData[LIGHTS_FLOATS_PER_LIGHT*1+5],
                       u_LightData[LIGHTS_FLOATS_PER_LIGHT*1+6],
                       u_LightData[LIGHTS_FLOATS_PER_LIGHT*1+7],
                       1.0) * u_LightData[LIGHTS_FLOATS_PER_LIGHT*1];

    lightVector = normalize((u_ViewMatrix * lightPosition).xyz - v.xyz);
    halfwayVector = normalize(lightVector + nv);

    facing = dot(normalVector, lightVector);
    if (facing >= 0.01) {
        vec4 qwe = diffuseColour * facing * lightColour;
// HERE .............
        finalColour = finalColour + qwe;  // takes 10 fps
// HERE ^^^^^^^^^^^^^
    }

    gl_FragColor = finalColour;
}

person bevis    schedule 10.05.2013    source источник


Ответы (1)


Ветвление вызывает это. Избегайте использования циклов if и for. Заменять

if (facing >= 0.0) {
    finalColour = finalColour + diffuseColour * facing * lightColour;
}

с участием

finalColour += max(0.0, facing) * diffuseColour * lightColour;

а также

if (facing >= 0.01) {
    vec4 qwe = diffuseColour * facing * lightColour;
    // HERE .............
    finalColour = finalColour + qwe;  // takes 10 fps
    // HERE ^^^^^^^^^^^^^
}

с участием

finalColour += step(0.01, facing) * facing * diffuseColour * lightColour;

Не беспокойтесь, если вы будете вычислять некоторые значения, даже если они вам не нужны. Поскольку шейдеры выполняются параллельно, вы не сможете работать намного быстрее, чем самый медленный экземпляр.

Также вы должны переместить как можно больше вещей в вершинный шейдер, поскольку он будет выполняться только один раз для каждой вершины, а не для каждого пикселя во фрагментном шейдере; в основном вы вычисляете все, что (tri) хорошо интерполируется в вершинном шейдере, и передаете его как вариации:

  • Расположение и цвет огней
  • Векторы L, V и H (по крайней мере, в этом примере)
person defhlt    schedule 18.11.2013
comment
Не волнуйтесь, если вы будете вычислять некоторые значения, даже если они вам не нужны. Поскольку шейдеры выполняются параллельно, вы не сможете стать намного быстрее самого медленного экземпляра. — Да, точно так же, как это сделали бы версии с ветвлениями. Дело не в том, что ветвление сразу становится злом, просто могут выполняться ненужные вычисления, как в вашей версии без ветвей, за исключением того, что версия на основе ветвей может помешать вычислениям, если весь блок не принимает ветвь, в то время как ваша версия без ветвей будет всегда делать все операции. - person Christian Rau; 19.11.2013
comment
Это было очень полезно — перенос части избыточного кода из фраг-шейдера в вершинный шейдер имел большое значение. В меньшей степени удаление вызовов if/then. - person bevis; 20.11.2013