为啥我的 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
中的循环中完成的,而MirrorReflection
在rayMarch
中的循环中被调用。在这种情况下,查找最多完成 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 片段着色器反射纹理查找会减慢我的帧速率?的主要内容,如果未能解决你的问题,请参考以下文章