Question

I have 2d texture S and want to return 3d texture H, such that pixel H[r,g,b] is equal to number of pixels of color rgb in texture S. Basically histogram of colors in texture S.

I know about occlusion queries, but it's only available in webgl2, and IIUC even there only with boolean results, and besides I would need to do separate query for each color. Ideally I'd like to do this in one fragment shader pass. Is there any way to do reduce (fold) operations in fragment shaders?

Why I need this (if you're interested):

I'm trying to do pixel-perfect 2d collision detection between objects and static terrain in webgl fragment shaders.

The plan is to use trick from old days - drawing a sprite 4 times - moved by (0,0), (0, dy), (dx, 0), and (dx, dy) pixels - and count the pixels of the sprite colliding with terrain each time - from that I can calculate vector (nx, ny) that represents the direction that the object should move to bounce of the terrain as quickly as possible. Simply adding that vector to the velocity of the object makes the object slide nicely along the terrain (down the hills, jumping on the bumps, etc). I've used that in old C++ game and that works great, but it's too slow in js.

I want to draw the whole vertex buffer of all sprites in game (each in different color to be able to recognize which objects collide) 4 times, each time drawing the black terrain over them, and then count the pixels of each color in the resulting 4 framebuffers.

The problem is - how to count the pixels in fragment shader?

Was it helpful?

Solution

how to count the pixels in fragment shader?

In one pass, possibly - you risk hitting either a GPU timeout exception or a too many commands exception. I recently ran some profiles to see the limit in WebGL: Can one fragment access all texture pixel values in WebGL GLSL? (Not just it's own TexCoord)

Basically you want to use a for loop (or two for loops for x/y) to iterate through each pixel on the texture, this example adds all the color value into one pixel:

void main() {
    vec4 color_register = 0.0;
    for (float x = 0.0; x < PIXELS_WIDE; x++) 
        for (float y = 0.0; y < PIXELS_TALL; y++) 
            color_register += texture2D(tex0, vec2(x / PIXELS_WIDE, y / PIXELS_TALL));
    gl_FragColor = color_register;
}

Then if you wanted each pixel to represent the total occurrences of a different color, you would need to render to a surface with C pixels, where C is the number of unique colors you want a histogram of. This requires each pixel to be aware of it's color value, which is most simply done by having a texture containing all colors once, sampling from that with the UV (texture coordinate), then iterating all pixels in the actual scene to be histogramed and tallying that color:

void main() {
    vec4 this_pixels_unique_color = texture2D(tex_unique_colors, tex_coord);
    int  occurrences_of_this_unique_color = 0;
    for (float x = 0.0; x < SCENE_PIXELS_WIDE; x++) 
        for (float y = 0.0; y < SCENE_PIXELS_TALL; y++) 
            color_register += texture2D(tex_scene_to_histogram, vec2(x / SCENE_PIXELS_WIDE, y / SCENE_PIXELS_TALL));
    gl_FragColor = vec4(occurrences_of_this_unique_color, occurrences_of_this_unique_color, occurrences_of_this_unique_color, 1);
}

Leaving you with a rendered surface with one pixel for each unique color, where the darkness of each pixel represents the amount of that color in the scene histogramed.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top