Retrieving alpha from framebuffer

To detect objects in my project, I create a “picking camera” that renders the vertex colors of those objects to a 1x1 framebuffer. Since there exist several different object types, I’ve associated each type with a different alpha value. I could have chosen a different color channel for this purpose and ignored the alpha component, but that would limit the number of available IDs for the objects themselves.
Hence the need to be able to retrieve the correct alpha value of the rendered vertex color.

There are two ways I’ve tried to get the rendered color; one involves a TexturePeeker while the other uses a PNMImage. The former no longer works for me since Panda 1.9.0 and later, so I replaced it with the latter, but that one seems to require an inefficient framebuffer setup (at least on my laptop).

(The following quotes are from this post.)

Even adding that made no difference for the TexturePeeker method.

That may be obvious to you, but I have no intimate knowledge of framebuffers so I’ll just take your word for it. Then again, the buffer I use is just a single pixel, so how much of an impact can it have? I wanted to use setFloatColor(True) to have more object IDs available, and on my laptop I need this call for the PNMImage method to work.

Tried it, didn’t help when using either a TexturePeeker or a PNMImage.
Maybe I’m doing other things wrong, so I will give you a code sample for both approaches that I have tried.

Let’s start with the working method, which makes use of a PNMImage and the unorthodox framebuffer setup:

from panda3d.core import *
from direct.showbase.ShowBase import ShowBase


class MyApp(ShowBase):

    def __init__(self):

        ShowBase.__init__(self)

        self._pixel_color = VBase4()
        self._tex = Texture("picking_texture")
        self._img = PNMImage(1, 1)
        props = FrameBufferProperties()
        
        if False:
            props.set_rgba_bits(8, 8, 8, 8)
        else:
            props.set_float_color(True) # only needed when run on laptop
            props.set_alpha_bits(32)

#        props.set_depth_bits(32)
        self._buffer = bfr = self.win.make_texture_buffer("picking_buffer",
                                                          1, 1,
                                                          self._tex,
                                                          to_ram=True,
                                                          fbp=props)

        bfr.set_clear_color(VBase4(1., 128./255., 200./255, 100./255))
        bfr.set_clear_color_active(True)
        self._picking_cam = self.make_camera(bfr)

        self.task_mgr.add(self.__get_pixel_under_mouse, "get_pixel_under_mouse", sort=0)

    def __get_pixel_under_mouse(self, task):

        self._tex.store(self._img)
        self._pixel_color = self._img.get_xel_a(0, 0)
        print "pixel color:", self._pixel_color

        return task.cont


app = MyApp()
app.run()

The value stored in self._pixel_color should be the background color (1., 128./255., 200./255, 100./255). With the above code, that is what I’m getting, so that’s what I’m currently using.
Strangely, on my desktop PC I don’t need the set_float_color(True) call, but I do on my laptop, or the alpha value is zero.

With props.set_rgba_bits(8, 8, 8, 8 ), however, the alpha value is always zero.

The second code sample makes use of a TexturePeeker, which I cannot get to work no matter what I try:

from panda3d.core import *
from direct.showbase.ShowBase import ShowBase


class MyApp(ShowBase):

    def __init__(self):

        ShowBase.__init__(self)

        self._pixel_color = VBase4()
        self._tex = Texture("picking_texture")
        self._tex_peeker = None
        props = FrameBufferProperties()
        props.set_rgba_bits(8, 8, 8, 8)
        self._buffer = bfr = self.win.make_texture_buffer("picking_buffer",
                                                          1, 1,
                                                          self._tex,
                                                          to_ram=True,
                                                          fbp=props)

        bfr.set_clear_color(VBase4(1., 128./255., 200./255, 100./255))
        bfr.set_clear_color_active(True)
        self._picking_cam = self.make_camera(bfr)

        self.task_mgr.add(self.__get_pixel_under_mouse, "get_pixel_under_mouse", sort=0)

    def __get_pixel_under_mouse(self, task):

        if not self._tex_peeker:
            self._tex_peeker = self._tex.peek()
            return task.cont

        self._tex_peeker.lookup(self._pixel_color, .5, .5)
        print "pixel color:", self._pixel_color # alpha is always 1.

        return task.cont


app = MyApp()
app.run()

When I run the above code (either on desktop or laptop), the alpha component is always one.

This turned out to be a bug, to which I just checked in a fix. However, it had nothing to do with the use of PNMImage or TexturePeeker. What was different in your code snippets is that in the PNMImage case you called set_float_color(True), which triggered different logic that didn’t suffer from the same bug.

As for r32g32b32a32 buffers being more expensive than r8g8b8a8 buffers: it’s just about texture bandwidth, but I suppose that doesn’t really matter if you’ve only got one pixel. :slight_smile:

Novice programmer here, hoping to learn. Can you explain what you mean by trying to detect objects and a picking camera?

Ah, great, thank you!

Thank you for the explanations :slight_smile: .

In my project, Panda3D Studio (shameless plug :stuck_out_tongue: ), you can manipulate model objects (and their subobjects), but to do so, you need to select them first. Instead of having to choose an object’s name from a list, it’s obviously more intuitive and user-friendly to be able to just click on the object. Collision detection could be used for this, but is far too slow when many objects need to be checked. Instead, I prefer a technique called “picking”; basically, each object is rendered a second time, with a unique color. This color can then be retrieved from the framebuffer to identify the object the mouse is over. Normally, this could also be slow, depending on the window size, but I use a trick: I create a special “picking camera” to render only the exact pixel that the mouse is currently over, so it’s quite fast.