为啥我的 Ray March 片段着色器反射纹理查找会减慢我的帧速率?

Posted

技术标签:

【中文标题】为啥我的 Ray March 片段着色器反射纹理查找会减慢我的帧速率?【英文标题】:Why are my Ray march fragment shader refelction texture lookups slowing my frame rate?为什么我的 Ray March 片段着色器反射纹理查找会减慢我的帧速率? 【发布时间】:2020-03-09 10:55:50 【问题描述】:

我使用着色器玩具在 GLSL 中编写了片段着色器。 链接:https://www.shadertoy.com/view/wtGSzy

大部分都可以,但是当我在反射函数中启用纹理查找时,性能从 60FPS 下降到 5~FPS。

有问题的代码在第 173 - 176 行

if(SDFObjectToDraw.texChannelID == 0)
    col = texture(iChannel0, uv);
if(SDFObjectToDraw.texChannelID == 1)
           col = texture(iChannel1, uv);

在我的 rayMarch 函数(第 274-277 行)中可以看到相同的代码,并且可以很好地为我的对象着色。它只会导致反射函数出现问题。

我的问题是,为什么我的纹理查找在反射代码中会降低我的性能这么多,我可以做些什么来改进它?

/**
 * Return the normalized direction to march in from the eye point for a single pixel.
 * 
 * fieldOfView: vertical field of view in degrees
 * size: resolution of the output image
 * fragCoord: the x,y coordinate of the pixel in the output image
 */
vec3 rayDirection(float fieldOfView, vec2 size, vec2 fragCoord) 
    vec2 xy = fragCoord - size / 2.0;
    float z = size.y / tan(radians(fieldOfView) / 2.0);
    return normalize(vec3(xy, -z));



float start = 0.0;
vec3 eye = vec3(0,0,5);
int MAX_MARCHING_STEPS = 255;
float EPSILON = 0.00001;
float end = 10.0;


const uint Shpere = 1u;
const uint Box = 2u;
const uint Plane = 4u;



vec3 lightPos = vec3(-10,0,5);
#define M_PI 3.1415926535897932384626433832795
const int SDF_OBJECT_COUNT = 4;

struct SDFObject

    uint Shape;
    vec3 Position;
    float Radius;
    int  texChannelID;
    float Ambiant;
    float Spec;
    float Diff;
    vec3 BoxSize;
    bool isMirror; //quick hack to get refletions working

;
SDFObject SDFObjects[SDF_OBJECT_COUNT] = SDFObject[SDF_OBJECT_COUNT](    
        SDFObject(Shpere, vec3(2,0,-3),1.0,0,0.2,0.2,0.8, vec3(0,0,0),true)
        ,SDFObject(Shpere, vec3(-2,0,-3),1.0,0,0.1,1.0,1.0, vec3(0,0,0),false)
        ,SDFObject(Box, vec3(0,0,-6),0.2,1,0.2,0.2,0.8, vec3(1.0,0.5,0.5),false)
        ,SDFObject(Plane, vec3(0,0,0),1.0,1,0.2,0.2,0.8, vec3(0.0,1.0,0.0),false)
        );

float shereSDF(vec3 p, SDFObject o)

    return length(p-o.Position)-o.Radius;

float boxSDF(vec3 pointToTest, vec3 boxBoundery, float radius, vec3 boxPos)

    vec3 q = abs(pointToTest - boxPos) - boxBoundery;
    return length(max(q,0.0)) + min(max(q.x, max(q.y,q.z)) ,0.0) -radius;

float planeSDF(vec3 p, vec4 n, vec3 Pos)

    return dot(p-Pos, n.xyz) + n.w;






bool IsShadow(vec3 LightPos, vec3 HitPos)

    bool isShadow = false;


    vec3 viewRayDirection = normalize(lightPos- HitPos) ;       

    float depth = start;

    vec3 hitpoint;

    for(int i=0; i<MAX_MARCHING_STEPS; i++)
    
        hitpoint = (HitPos+  depth * viewRayDirection);

        float dist = end;

        for(int j =0; j<SDF_OBJECT_COUNT; j++)
        
            float distToObjectBeingConsidered;
             if(SDFObjects[j].Shape == Shpere)
                distToObjectBeingConsidered = shereSDF(hitpoint, SDFObjects[j]);
            if(SDFObjects[j].Shape == Box)
                distToObjectBeingConsidered = boxSDF(hitpoint, SDFObjects[j].BoxSize , SDFObjects[j].Radius, SDFObjects[j].Position);
            if(SDFObjects[j].Shape == Plane)
                distToObjectBeingConsidered= planeSDF(hitpoint, vec4(SDFObjects[j].BoxSize, SDFObjects[j].Radius), SDFObjects[j].Position);

            if( distToObjectBeingConsidered < dist)
            
               dist = distToObjectBeingConsidered;
            
        

        if(dist < EPSILON)
        
            isShadow = true;
        


        depth += dist;

        if(depth >= end)
        
           isShadow = false;
         

    

    return isShadow;



vec3 MirrorReflection(vec3 inComingRay, vec3 surfNormal, vec3 HitPos, int objectIndexToIgnore)

    vec3 returnCol;

    vec3 reflectedRay = reflect(inComingRay, surfNormal);



    vec3 RayDirection = normalize(reflectedRay) ;       

    float depth = start;


    vec3 hitpoint;
    int i;
    for(i=0; i<MAX_MARCHING_STEPS; i++)
    
        hitpoint = (HitPos+  depth * RayDirection);

        SDFObject SDFObjectToDraw;
        float dist = end;

        for(int j =0; j<SDF_OBJECT_COUNT; j++)
        
            float distToObjectBeingConsidered;
             if(SDFObjects[j].Shape == Shpere)
                distToObjectBeingConsidered = shereSDF(hitpoint, SDFObjects[j]);
            if(SDFObjects[j].Shape == Box)
                distToObjectBeingConsidered = boxSDF(hitpoint, SDFObjects[j].BoxSize , SDFObjects[j].Radius, SDFObjects[j].Position);
            if(SDFObjects[j].Shape == Plane)
                distToObjectBeingConsidered= planeSDF(hitpoint, vec4(SDFObjects[j].BoxSize, SDFObjects[j].Radius), SDFObjects[j].Position);


           if( distToObjectBeingConsidered < dist && j!= objectIndexToIgnore )// D > 0.0)
            
               dist = distToObjectBeingConsidered;
               SDFObjectToDraw = SDFObjects[j];
            
        

        if(dist < EPSILON)
        
            vec3 normal =normalize(hitpoint-SDFObjectToDraw.Position);
            float u = 0.5+ (atan(normal.z, normal.x)/(2.0*M_PI));
            float v = 0.5+ (asin(normal.y)/(M_PI));

            vec2 uv =vec2(u,v);            
            vec4 col = vec4(0,0.5,0.5,0);


///>>>>>>>>>>>> THESE LINES ARE broken, WHY?        
            //if(SDFObjectToDraw.texChannelID == 0)
                //col = texture(iChannel0, uv);
            //if(SDFObjectToDraw.texChannelID == 1)
                //col = texture(iChannel1, uv);


            vec3 NormalizedDirToLight = normalize(lightPos-SDFObjectToDraw.Position);
            float theta = dot(normal,NormalizedDirToLight);


            vec3 reflectionOfLight = reflect(NormalizedDirToLight, normal);
            vec3 viewDir = normalize(SDFObjectToDraw.Position);
            float Spec = dot(reflectionOfLight,  viewDir);


            if(IsShadow(lightPos, hitpoint))
            
                returnCol= (col.xyz*SDFObjectToDraw.Ambiant);
            
            else
            
                returnCol= (col.xyz*SDFObjectToDraw.Ambiant) 
                +(col.xyz * max(theta *SDFObjectToDraw.Diff, SDFObjectToDraw.Ambiant));
            
            break;
        


        depth += dist;

        if(depth >= end)
        
            //should look up bg texture here but cant be assed right now
           returnCol = vec3(1.0,0.0,0.0);
           break;
         

    



    return returnCol;//*= (vec3(i+1)/vec3(MAX_MARCHING_STEPS));



vec3 rayMarch(vec2 fragCoord)

    vec3 viewRayDirection = rayDirection(45.0, iResolution.xy, fragCoord);

    float depth = start;

    vec3 hitpoint;

    vec3 ReturnColour = vec3(0,0,0);

    for(int i=0; i<MAX_MARCHING_STEPS; i++)
    
        hitpoint = (eye+  depth * viewRayDirection);

        float dist = end;
        SDFObject SDFObjectToDraw;
        int objectInDexToIgnore=-1;

        //find closest objecct to current point
        for(int j =0; j<SDF_OBJECT_COUNT; j++)
        
            float distToObjectBeingConsidered;

            if(SDFObjects[j].Shape == Shpere)
                distToObjectBeingConsidered = shereSDF(hitpoint, SDFObjects[j]);
            if(SDFObjects[j].Shape == Box)
                distToObjectBeingConsidered = boxSDF(hitpoint, SDFObjects[j].BoxSize , SDFObjects[j].Radius, SDFObjects[j].Position);
            if(SDFObjects[j].Shape == Plane)
                distToObjectBeingConsidered= planeSDF(hitpoint, vec4(SDFObjects[j].BoxSize, SDFObjects[j].Radius), SDFObjects[j].Position);

            if( distToObjectBeingConsidered < dist)
            
                dist = distToObjectBeingConsidered;
                SDFObjectToDraw = SDFObjects[j];
                objectInDexToIgnore = j;
            
        


        //if we are close enough to an objectoto hit it.
        if(dist < EPSILON)
        
            vec3 normal =normalize(hitpoint-SDFObjectToDraw.Position);
            if(SDFObjectToDraw.isMirror)
            
                ReturnColour = MirrorReflection( viewRayDirection, normal, hitpoint, objectInDexToIgnore);
            
            else
            

                float u = 0.5+ (atan(normal.z, normal.x)/(2.0*M_PI));
                float v = 0.5+ (asin(normal.y)/(M_PI));

                vec2 uv =vec2(u,v);            
                vec4 col;

                if(SDFObjectToDraw.texChannelID == 0)
                    col = texture(iChannel0, uv);
                if(SDFObjectToDraw.texChannelID == 1)
                    col = texture(iChannel1, uv);


                vec3 NormalizedDirToLight = normalize(lightPos-SDFObjectToDraw.Position);
                float theta = dot(normal,NormalizedDirToLight);


                vec3 reflectionOfLight = reflect(NormalizedDirToLight, normal);
                vec3 viewDir = normalize(SDFObjectToDraw.Position);
                float Spec = dot(reflectionOfLight,  viewDir);


                if(IsShadow(lightPos, hitpoint))
                
                    ReturnColour= (col.xyz*SDFObjectToDraw.Ambiant);
                
                else
                
                    ReturnColour= (col.xyz*SDFObjectToDraw.Ambiant) 
                    +(col.xyz * max(theta *SDFObjectToDraw.Diff, SDFObjectToDraw.Ambiant));
                    //+(col.xyz* Spec * SDFObjectToDraw.Spec);
                
            

             return ReturnColour;
        


        depth += dist;

        if(depth >= end)
        
            float u = fragCoord.x/ iResolution.x;
            float v =  fragCoord.y/ iResolution.y;
            vec4 col = texture(iChannel2, vec2(u,v));
            ReturnColour =col.xyz;
         

    
    return ReturnColour;


void mainImage( out vec4 fragColor, in vec2 fragCoord )

    // Normalized pixel coordinates (from 0 to 1)
    //vec2 uv = fragCoord/iResolution.xy;

    // Time varying pixel color
    //vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));

    // Output to screen

    lightPos *= cos(iTime+vec3(1.5,2,2));

    //lightPos= vec3(cos(iTime)*2.0,0,0);

    vec3 SDFCol= rayMarch(fragCoord);
    vec3 col = vec3(0);

    //if(SDFVal <=1.0)
      //  col = vec3(1,0,0);

    //col = vec3(SDFVal,0,0);

    col = vec3(0.5,0,0);
    col =  SDFCol;

    fragColor = vec4(col,1.0);

【问题讨论】:

【参考方案1】:

[...] 在我的 rayMarch 函数(第 274-277 行)中可以看到相同的代码,并且可以很好地为我的对象着色。 [...]

“工作”纹理查找在rayMarch 中循环执行。 MAX_MARCHING_STEPS 是 255,所以最多查找 255 次。

vec3 rayMarch(vec2 fragCoord)

   // [...]

   for(int i=0; i<MAX_MARCHING_STEPS; i++)
   
       // [...]

       if(SDFObjectToDraw.texChannelID == 0)
           col = texture(iChannel0, uv);
       if(SDFObjectToDraw.texChannelID == 1)
           col = texture(iChannel1, uv);

       // [...]
   

   // [...]

当您在MirrorReflection 中进行查找时,性能会下降,因为它是在MirrorReflection 中的循环中完成的,而MirrorReflectionrayMarch 中的循环中被调用。在这种情况下,查找最多完成 255*255 = 65025 次。

~65000 个片段的纹理查找次数太多了,会导致性能下降。

vec3 MirrorReflection(vec3 inComingRay, vec3 surfNormal, vec3 HitPos, int objectIndexToIgnore)

   // [...]

   for(i=0; i<MAX_MARCHING_STEPS; i++)
   
       // [...]

       if(SDFObjectToDraw.texChannelID == 0)
            col = texture(iChannel0, uv);
       if(SDFObjectToDraw.texChannelID == 1)
            col = texture(iChannel1, uv);

       // [...]
   

   // [...]


vec3 rayMarch(vec2 fragCoord)

   // [...]

   for(int i=0; i<MAX_MARCHING_STEPS; i++)
   
       // [...]

       ReturnColour = MirrorReflection(viewRayDirection, normal, hitpoint, objectInDexToIgnore);

       // [...]
   

   // [...]

【讨论】:

谢谢老兄!我将纹理查找移到循环之外,性能得到了改善。但我还是一头雾水。纹理查找在 if(dist

以上是关于为啥我的 Ray March 片段着色器反射纹理查找会减慢我的帧速率?的主要内容,如果未能解决你的问题,请参考以下文章

为啥片段着色器比渲染纹理更快?

为啥我的着色器中的 GLSL 纹理坐标不是线性的?

片段着色器究竟如何用于纹理?

纹理中的 OpenGL 片段着色器

在片段着色器中,为啥我不能使用平面输入整数来索引 sampler2D 的统一数组?

OpenGL、GLSL 片段着色器无法读取 Sampler2D 纹理