2013-05-02 78 views
3

我想在片段着色器的网格上做光线跟踪。我已经写了下面的着色器来做到这一点(顶点着色器只是画一个screenquad)。这个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; 
} 

只要我在看近电网项目,硬编码的,帧率看起来(上一块GeForce 680M 400个+ FPS)接受(虽然比我期望比较其他着色器我已经写了这么很远),但是当我看到空闲(所以循环一直到64),帧速率是可怕的(40fps)。当在一个网格上看得如此接近以至于每个像素都在相同的网格物体中结束时,我可以达到1200 fps。

尽管我明白为每个像素做这个循环都是一些工作,但它仍然是一些简单的基础数学,特别是现在我已经删除了纹理查找并仅使用了一个简单的测试,所以我不明白为什么这必须如此艰难地放慢一切。我的GPU有16个内核,运行在700 + Mhz。我在960x540,518400像素渲染。它应该能够处理比我想象的更多的东西。

如果我删除上面的抗锯齿部分(我将根据增量值测试一些额外的相邻点的代码部分),它会更好一些(100fps),但是通过这些计算,它应该没有太大的区别! 如果我分裂,使增速不使用,但下面的代码为每个不同的部分进行编码,帧率保持不变。 如果我改变一些整数浮动,没有什么变化。

我以前做过更密集和/或复杂的着色器,所以这是为什么一个如此可怕慢?任何人都可以告诉我做了什么计算让它变得如此缓慢?

我没有设置未使用或类似的东西,在C-代码也什么都不做的不仅仅是呈现更多的制服。这是我以前成功使用过的代码。

有人吗?

+1

条件语句是有过之而无不及垫子操作:他们拖延管道。 – Luca 2013-05-02 13:11:10

+0

我明白了。我会试着想一个更好的方法。但是,为什么呢?条件为什么拖延管道?我不明白为什么着色器之外的任何东西需要等待其执行? – scippie 2013-05-02 13:44:53

+0

采用了最短路径的其他着色器线程。 – Luca 2013-05-02 13:56:49

回答

9

简短的回答是:分支和循环着色是(可能是)邪恶。 但它远不止于此:阅读本主题以获取更多信息:Efficiency of branching in shaders

谈到这个:

图形适配器有一个或多个GPU的,和GPU有几个核心。每个核心都设计为运行多个线程,但这些线程只能运行完全相同的代码(具体取决于实现)。

因此,如果10个线程必须做不同的循环,只要最大的循环需要运行,这10个线程都必须运行(取决于实现,循环可能会持续超过必要或线程可能会失速)。

与分支中的相同:如果线程如果,有可能(取决于实施方式),其两个分支被执行,并用于它们中的一个的结果。

所以,总而言之,它可能如果你想计算一下,在某些条件去除,比写的条件本身和分支(且也许大部分是)好做更多的数学和使用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; 

但有时也可能不...服务表现测试是关键...