2013-05-02 1 views
3

Я пытаюсь сделать raytrace на сетке в фрагментарный шейдер. Я написал шейдер ниже, чтобы сделать это (вершинный шейдер просто рисует экранное изображение).Почему этот шейдер GLSL настолько медленный?

#version 150 

uniform mat4 mInvProj, mInvRot; 
uniform vec4 vCamPos; 

varying vec4 vPosition; 

int test(vec3 p) 
{ 
    if (p.x > -4.0 && p.x < 4.0 
    && p.y > -4.0 && p.y < 4.0 
    && ((p.z < -4.0 && p.z > -8.0) || (p.z > 4.0 && p.z < 8.0))) 
     return 1; 
    return 0; 
} 

void main(void) { 
    vec4 cOut = vec4(0, 0, 0, 0); 

    vec4 vWorldSpace = mInvRot * mInvProj * vPosition; 
    vec3 vRayOrg = vCamPos.xyz; 
    vec3 vRayDir = normalize(vWorldSpace.xyz); 

    // http://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm 
    vec3 adelta = abs(vRayDir); 
    int increaser; 
    vec3 gradient, sgradient; 
    if (adelta.x > adelta.y && adelta.x > adelta.z) 
    { 
     increaser = 0; 
     gradient = vec3(vRayDir.x > 0.0? 1.0: -1.0, vRayDir.y/vRayDir.x, vRayDir.z/vRayDir.x); 
     sgradient = vec3(0.0, gradient.y > 0.0? 1.0: -1.0, gradient.z > 0.0? 1.0: -1.0); 
    } 
    else if (adelta.y > adelta.x && adelta.y > adelta.z) 
    { 
     increaser = 1; 
     gradient = vec3(vRayDir.x/vRayDir.y, vRayDir.y > 0.0? 1.0: -1.0, vRayDir.z/vRayDir.y); 
     sgradient = vec3(gradient.x > 0.0? 1.0: -1.0, 0.0, gradient.z > 0.0? 1.0: -1.0); 
    } 
    else 
    { 
     increaser = 2; 
     gradient = vec3(vRayDir.x/vRayDir.z, vRayDir.y/vRayDir.z, vRayDir.z > 0.0? 1.0: -1.0); 
     sgradient = vec3(gradient.x > 0.0? 1.0: -1.0, gradient.y > 0.0? 1.0: -1.0, 0.0); 
    } 
    vec3 walk = vRayOrg; 
    for (int i = 0; i < 64; ++i) 
    { 
     vec3 fwalk = floor(walk); 
     if (test(fwalk) > 0) 
     { 
      vec3 c = abs(fwalk)/4.0; 
      cOut = vec4(c, 1.0); 
      break; 
     } 
     vec3 nextwalk = walk + gradient; 
     vec3 fnextwalk = floor(nextwalk); 

     bool xChanged = fnextwalk.x != fwalk.x; 
     bool yChanged = fnextwalk.y != fwalk.y; 
     bool zChanged = fnextwalk.z != fwalk.z; 

     if (increaser == 0) 
     { 
      if ((yChanged && test(fwalk + vec3(0.0, sgradient.y, 0.0)) > 0) 
      || (zChanged && test(fwalk + vec3(0.0, 0.0, sgradient.z)) > 0) 
      || (yChanged && zChanged && test(fwalk + vec3(0.0, sgradient.y, sgradient.z)) > 0)) 
       { 
        vec3 c = abs(fwalk)/4.0; 
        cOut = vec4(c, 1.0); 
        break; 
       } 
     } 
     else if (increaser == 1) 
     { 
      if ((xChanged && test(fwalk + vec3(sgradient.x, 0.0, 0.0)) > 0) 
      || (zChanged && test(fwalk + vec3(0.0, 0.0, sgradient.z)) > 0) 
      || (xChanged && zChanged && test(fwalk + vec3(sgradient.x, 0.0, sgradient.z)) > 0)) 
       { 
        vec3 c = abs(fwalk)/4.0; 
        cOut = vec4(c, 1.0); 
        break; 
       } 
     } 
     else 
     { 
      if ((xChanged && test(fwalk + vec3(sgradient.x, 0.0, 0.0)) > 0) 
      || (yChanged && test(fwalk + vec3(0.0, sgradient.y, 0.0)) > 0) 
      || (xChanged && yChanged && test(fwalk + vec3(sgradient.x, sgradient.y, 0.0)) > 0)) 
       { 
        vec3 c = abs(fwalk)/4.0; 
        cOut = vec4(c, 1.0); 
        break; 
       } 
     } 

     walk = nextwalk; 
    } 

    gl_FragColor = cOut; 
} 

Пока я смотрю на близких элементов сетки, в закодированных те, фреймрейт выглядит приемлемым (400 + кадров в секунду на Geforce 680M) (хотя и ниже, чем я ожидал бы по сравнению с другими шейдерами я написал так далеко), но когда я смотрю на пустоту (так что цикл доходит до 64), частота кадров ужасная (40 кадров в секунду). Я получаю около 1200 кадров в секунду, когда смотрю так близко к сетке, что каждый пиксель попадает в один и тот же элемент сетки.

Хотя я понимаю, что выполнение этой петли для каждого пикселя - это какая-то работа, это все еще простая математическая математика, особенно теперь, когда я удалил поиск текстуры и просто использовал простой тест, поэтому я не понимаю почему это так сильно тормозит. Мой GPU имеет 16 ядер и работает на скорости 700 + МГц. Я рендеринга на 960x540, 518400 пикселей. Он должен уметь обрабатывать гораздо больше, чем я думал.

Если я удаляю часть сглаживания вышеуказанного (часть кода, где я буду проверять некоторые дополнительные смежные точки на основе значения возрастания), она немного лучше (100 кадров в секунду), но при этом, используя эти вычисления, это не должно иметь большого значения! Если я разделяю код так, чтобы приращение не использовалось, но приведенный ниже код выполняется для каждой другой части, частота кадров остается прежней. Если я изменю некоторые ints на float, ничего не изменится.

Я делал гораздо более интенсивные и/или сложные шейдеры раньше, так почему же это так ужасно медленно? Может ли кто-нибудь сказать, какой расчет я делаю, делает это так медленно?

Я не устанавливаю униформы, которые не используются, или что-то в этом роде, C-код также делает не что иное, как просто рендеринг. Это код, который я использовал успешно 100 раз.

Кто-нибудь?

+1

Conditionals хуже маты: они останавливают трубопровод. – Luca

+0

Я вижу. Я попытаюсь придумать лучший метод. Но почему? Почему условности останавливают трубопровод? Я не понимаю, почему что-либо вне шейдера должно ждать его выполнения? – scippie

+0

Другие шейдерные потоки, которые взяли кратчайший путь. – Luca

ответ

9

Короткий ответ: разветвление и зацикливание в шейдерах (может быть) зла. Но гораздо больше: читать эту тему для получения дополнительной информации: Efficiency of branching in shaders

Он приходит к этому:

Графический адаптер имеет один или несколько GPU, и графический процессор имеет несколько ядер. Каждое ядро ​​предназначено для запуска нескольких потоков, но эти потоки могут работать только с одним и тем же кодом (в зависимости от реализации).

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

То же самое с ветвями: если поток имеет if, то возможно (в зависимости от реализации), что обе ветви выполняются, и результат одного из них используется.

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

Например:

(using useLighting = 0.0f or 1.0f) 
return useLighting * cLightColor * cMaterialColor + (1.0 - useLighting) * cMaterialColor; 

может быть лучше, чем:

if (useLighting < 0.5) 
    return cMaterialColor; 
else 
    return cLightColor * cMaterialColor; 

Но иногда он не может ... Performace-тестирование является ключом ...

 Смежные вопросы

  • Нет связанных вопросов^_^