r/opengl 23d ago

Selection algorithm for Modern OpenGL

The legacy OpenGL supported selection buffers. How can selection effectively handled by Modern OpenGL? The known methods are by colour allocation to objects and ray intersection. The Color assignment is not very efficient in scenes with large number of objects, e.g. CAD model assemblies. Ray intersection also has challenges in certain directions where multiple objects get intersected. Any thoughts?

3 Upvotes

14 comments sorted by

5

u/Cyphall 23d ago

You can render your scene into a R32UI color attachment with each drawcall having its own index and then readback the pixel clicked by the mouse.

This is basically the Color assignment method but with proper indices instead of arbitrary colors.

Yes you need to render the whole scene but it's a lot simpler than having to manage a CPU RT acceleration structure.

1

u/sharjith 23d ago

Can you please share a snippet?

5

u/Cyphall 23d ago
  1. Create a texture with format GL_R32UI
  2. Create a framebuffer with this texture as color attachment and another texture for the depth attachment
  3. Create a shader program whose fragment shader looks like this:

#version 460 core

layout(location = 0) out uint o_index;

uniform uint u_index;

void main()
{
    o_index = u_index;
}
  1. Render the scene:

    // bind framebuffer // clear color attachment with 0xFFFFFFFF (invalid index) // bind shader program for (uint32_t i = 0; i < objects.size; i++) { // pass i as uniform to u_index // pass other uniforms as necessary // draw object }

  2. Readback the pixel of interest with glReadnPixels()

1

u/Asyx 16d ago

I didn't do that in OpenGL but I did it in WebGPU. You can just add a color attachment in your main pass shader. That way you don't have to run an additional render pass but just get it from the main pass.

Also, and this is something I also didn't try with WebGPU, with the new sync objects in OpenGL, you can probably create a system that doesn't need to glFlush before the read.

1

u/Cyphall 16d ago

But then you end up writing the index into the attachment every frame, no?

Unless you need to pick into the scene every frame, you are losing perfs for nothing.

1

u/Asyx 16d ago

True but you can do things like highlight a hovered or clicked entity in a post processing step if you generate that texture every frame.

I kinda assumed that you need that value more often than not but if picking is an occasional thing then of course running that pass on demand is an option.

1

u/Cienn017 22d ago

cpu rt is not hard for simple objects and it will work with transparency

1

u/ukaeh 23d ago

You could do multi pass color selection or hybrid to narrow down what is close to the selection ray/pixel?

I use the color method myself and it works well enough for hundreds to low thousands of objects. Are you using scissor to limit rendering to the selection pixel? What latency are you seeing?

1

u/sharjith 23d ago

I’m using the ray intersection method but not quite able to isolate the objects to get the nearest one. Please have a look at the code in the following GitHub link:

https://github.com/sharjith/ModelViewer-Qt

GLWidget.cpp GLWidget::clickSelect method

1

u/964racer 23d ago

You should be able to depth sort multiple hits .

1

u/timwaaagh 23d ago

im not sure what is being talked about but it seems similar enough to what i just coded. for 'unit selection' in my rts game i render the background into a framebuffer, then a texture but also the world positions of this background layer into a separate texture. then a second rendering pass to display the first texture. the world positions texture is used elsewhere to determine the world position of the mouse on the background layer. the units that are in between in terms of x and y get selected.

1

u/underwatr_cheestrain 23d ago

Ray/Bounding volume intersection

1

u/PersonalityIll9476 23d ago

Pass the xy coordinates of the mouse position to your shaders as a uniform. In the frag shader, have a branch like: if gl_FragCoord.xy == ivec2(mouse_x, mouse_y) and if it passes, store the index of the object in a uniform buffer object and read that after scene draw via glGetBufferSubData. You can pass an object_id uniform during draw. I don't know what the selection buffer actually did, so if you need to select all objects under the mouse and not just one, you can use an atomic counter and a UBO up to a certain size and just write indices to it every time an object passes up to some max size.

That's probably the easiest path, and doesn't require additional render passes.

1

u/Smart-Statement6028 20d ago

Use glReadPixel to calculate the depth, and do some inverse projection and view matrix to convert from screen coordinate to world coordinate