GradeSchool
GradeSchool

Reputation: 23

glsl pixel shader- distance to closest target pixel

i have a 2k x 1k image with randomly placed "target" pixels. these pixels are pure red.

in a frag/pixel shader, for each pixel that is not red (target color), i need to find the distance to the closest red pixel. i'll use this distance value to create a gradient.

i found this answer, which seems the closest to my problem --- Finding closest non-black pixel in an image fast --- but it's not glsl specific.

i have the option to send my red target pixels into the frag shader as a texture buffer array. but i think it would be cleaner if i didn't need to.

Upvotes: 2

Views: 2640

Answers (2)

Ryan
Ryan

Reputation: 521

Why don't you gaussian blur the whole image with a large radius, but at each iteration keep adding the red pixels back into the equation at full intensity so they bleed out. The red channel of the final blur would be your distance values - the higher values are closer to the red pixels. It's an approximation, but then you can make use of heavily optimised blur shaders.

Upvotes: 0

Tommy
Tommy

Reputation: 100632

A shader cannot read and write to the same texture because that would introduce too many constraints and complexities about the sequence of execution and would make caching much more difficult. So you're talking about sending some data about the red pixels in and getting the distance information out.

Fragment shaders run in parallel and it's much more expensive to perform random-access texture reads than to read from a location that is known outside of the shader, primarily due to pipelining considerations. The pre-programmable situation where sampling coordinates are known at vertices and then interpolated across the face of the geometry is still the most optimal way to access a texture.

So, writing a shader that, for each pixel, did a search outwards for a red pixel would be extremely inefficient. It's definitely possible, doing much the algorithm you link to, but probably not the smartest way around.

Ideally you'd phrase things the other way around and use some sort of accumulation. So:

  1. clear your output buffer to its maximal values;
  2. for each red location:
    1. for every output fragment, work out the distance from the location;
    2. check what distance is already stored for that fragment;
    3. if the new distance is less than that stored, replace the stored version.

The easiest way to do that in OpenGL is likely going to be to use a depth buffer, because that has the per-fragment steps (2) and (3) implemented directly in hardware.

So for each each fragment you're going to calculate the distance from the current red fragment. You're going to output that as depth. When you're finished with all red dots you can use the depth buffer as input to a shader that outputs appropriate colours.

To avoid 2000 red spots turning into a 2000-pass drawing algorithm which would quickly run up against memory bandwidth, you'll probably want to write a single shader that does a large number of red dots at once and outputs a single depth value.

You should check GL_MAX_UNIFORM_LOCATIONS to find out how many uniforms you can push at once. It's guaranteed to be at least 1024 on recent versions of desktop OpenGL. You'll probably want to generate your shader dynamically.

Upvotes: 3

Related Questions