为什么我的 Ray march 片段着色器反射纹理查找会降低我的帧速率?

Why are my Ray march fragment shader refelction texture lookups slowing my frame rate?

我使用 shader toy 在 GLSL 中编写了一个片段着色器。 Link : 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);
}

[...] This same code can bee seen in my rayMarch function (lines 274-277) and works fine for colouring my objects. [...]

"working" 纹理查找在 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 中的循环中完成的,而 MirrorReflection 在 [=12] 中的循环中调用=].在这种情况下,查找最多进行 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);

       // [...]
   }

   // [...]
}