The basic problem you are facing is having to multiply iterate and the cost of doing so each frame. (Apologies if my DirectX lingo confuses)
You feel that the only option available to you is to:
Iterate through and evaluate every sphere against the point, every frame.
But, what you'd really prefer to do is:
Iterate through and evaluate every sphere against the point, every frame, simultaneously.
Good news! You can easily accomplish that by moving your current algorithm to a
ComputeShader. Good news; doing so is rediculously easy!
Right now, you index the "current sphere" by a
for(i=0; ...). With a
ComputeShader, we can
Dispatch a fixed number of threads or thread groups based on how many spheres there are, and index the sphere list using
SV_ThreadID. Each thread, simultaneously evaluates your falloff formula against the point. If we start off with an empty
AppendBuffer, and only
Append where the falloff is non-zero, we end up with a list of only the spheres that influence the point, as well as the calculated influence for each.
At this point, you can retrieve the "instantly" filtered list and iterate it on the CPU. However, since it was so easy to implement the first, why not just create a second one:
This one uses the previously created
AppendBuffer as a
ConsumeBuffer. This will only contain a couple of variables like
BestInfluenceSphereIndex, initialized to their most-negative values. We'll use a single thread and a plain, old,
for loop that will
Consume() each influencing sphere from the buffer, and compare it to
BestInfluence is lower, replace both with the current sphere.
At the end, you are left with the "best nearest sphere from a point in 3D space", which you can then read back on the CPU. This method has approximately "fixed" performance. The first
ComputeShader will take about the same time to finish, regardless of sphere-count (thread-count). You say you won't be calculating this every frame, but what you mean to say is that it's impossible to do so every frame and still maintain a good framerate. Well, now you can do both!
The most important factor here, is that the CPU only has to tell the GPU "go". The CPU can continue on, updating your camera, objects, etc. while the GPU crunches the spherical falloff. You're doing more than one thing at a time and one of those things is operating on all of its' inputs at the same time.
The new shaders can feel like scary, intimidating beasts that should be avoided at all costs. Spend a good week learning each of them now, so that your future designs can include their use. You'll find that each one, approached individually, is actually not that hard to understand. If you are successful implementing the
ComputeShaders described, you'll have expanded your limits exponentially. When you are able to fully utilize every shader stage, you can, then, combine them and discard any previously perceived limitations.