I need to write a fragment shader which would somehow be aware of N points that are in the scene and darken pixels based on their world coord distance to the points. I'm looking for a method that would be still suitable for mobile devices. I image I could pass the points as a texture but i'm stuck in figuring out how to effectively evaluate if a pixel is close enough to any of the N points (I know how to do this for 1 point, but for N without a loop?). Any ideas if this is doable in a performance efficient manner? All ideas are welcome. Thanks!
-
\$\begingroup\$ This sounds like it might be an XY problem. Can you describe more about the effect you want to achieve? There may be a way to accomplish it without iterating over a list of points in the fragment shader. \$\endgroup\$– DMGregory ♦Commented Jul 3, 2015 at 20:11
-
\$\begingroup\$ Given N points in a scene I need a plane to draw blurred circles onto itself if a point is at a certain distance from a plane (if it's too far away nothing is drawn and if a point gets close enough the circle becomes more and more visible). You can imagine something like as if the points were very hot and if they get close to the surface, the surface heats up as well. I don't know what else to add to make it more clear. It could be easily achieved with projection textures, but I need it done in a shader and without loops. \$\endgroup\$– mt_Commented Jul 5, 2015 at 11:39
-
\$\begingroup\$ Rather than have every fragment check its distance to every point, instead you could render a quad for each point, representing the camera-facing edge of its effect bounds. Use the depth buffer to compute the distance between the already-drawn scene geometry under each fragment to this quad's point, then shade accordingly. No loops in the fragment shader, each quad has knowledge only of itself, so you can scale up to very large N. If that would be suitable I can write up the details in an answer. \$\endgroup\$– DMGregory ♦Commented Jul 5, 2015 at 12:32
-
\$\begingroup\$ @DMGregory this sounds interesting and might actually be the solution I'm looking work. Please provide more details as an answer. Thanks! \$\endgroup\$– mt_Commented Jul 5, 2015 at 13:11
1 Answer
As discussed in the comments above, it may be easier to get this effect (and scale better to large values of N) by flipping the request around.
Rather than the shader for the scene geometry checking - in every rendered fragment - its distance to each of the N points, we can instead draw the scene geometry normally, then "stamp" the effects onto it by rendering some triangles for each of the N points.
This is effectively the same flip in thinking we do when moving from forward lighting to deferred. If each light/point's effect area is small (ie. less than the full screen) and there are many of them, we can reduce redundant work this way.
So, that leaves a few choices for how to render your points' effects:
1. Shadow quads
The images you've shown look a lot like blob shadows in older 3D games. If this is representative, that the blobs will always fall on some plane in a defined direction ("down"), we can do this very cheaply with no depth tricks:
In a CPU-side (C#/UnityScript) script, raycast down from each of your N points to find the nearest surface in that direction. Then position a quad at that position, scaled appropriately for how wide your effect is meant to be at that distance. Use a shader on the quad that will decal your desired effect onto the surface.
2. Deferred decals/ screen-space decals
This is the way to go if you want either an omni-directional effect, or one that shines "though" the first surface it encounters so it can affect multiple at once, or if the surfaces it's affecting are bumpy & irregular, hard to cover with a single quad. But it's much more complicated, so if you're not in one of these situations, you're probably better off using a quad.
The idea is to position a mesh representing the bounding volume of the point's effect. When your fragment shader goes to render the surface of the volume, it looks up into the depth buffer for the scene already rendered, to figure out what point in the scene has already been rendered to that pixel of the render target. It then shades the fragment as though it was located at that position. In your case, that means calculating the distance from this projected scene point to the source point for the volume.
The particulars will depend on the details of your use case (are your bounds cubes, spheres, cylinders? Can the camera get inside them? What type of shading effect needs to be applied within the bounds? Etc), but this type of technique is now pretty standard, so you should be able to find lots of references to get you started. Here's a sampling: