Frame buffer, use stencil and depth

Hello, that’s me again. I decided to use a frame buffer to prepare the objects for shaders. However, as it turned out, the frame buffer does not use a stencil buffer and a depth buffer. I decided to use duplicate objects for rendering stencils and a depth buffer, but duplicating objects is somehow not rational for memory. The question is, can I use the same geometry for stencil writing and reading. Or adjust the frame buffer to use depth and stencil tests.

Imitation of the desired. Here, two geometries create the output I need in the frame buffer, I also do the same with the stencil buffer. I would like to use one object. The question is how?

from direct.showbase.ShowBase import ShowBase
from panda3d.core import NodePath, BitMask32, LVector4, CardMaker, ColorWriteAttrib

import sys

class GlowDemo(ShowBase):

    def __init__(self):
        ShowBase.__init__(self)

        camera.setPos(0, -50, 0)
        
        cm = CardMaker("create_mesh")
        cm.setFrame(-1, 1, -1, 1)

        ground = render.attachNewNode(cm.generate())
        ground.setBin('fixed', 1)
        ground.hide(BitMask32.bit(0))
        ground.setColor(LVector4(0, 0, 1, 1))
        ground.setScale(10)
        ground.setP(-90)
        
        ground1 = render.attachNewNode(cm.generate())
        ground1.setBin('fixed', 2)
        ground1.setColor(LVector4(1, 0, 0, 1))
        ground1.setScale(10)
        ground1.setP(-90)
        ground1.setAttrib(ColorWriteAttrib.make(0))

        test_plane = render.attachNewNode(cm.generate())
        test_plane.setBin('fixed', 3)
        test_plane.setColor(LVector4(0, 1, 0, 1))
        test_plane.setScale(2)

        buffer = base.win.makeTextureBuffer("buffer", 512, 512)
        buffer.setClearColor(LVector4(0, 0, 0, 1))
        
        buffer_camera = base.makeCamera(buffer, lens=base.cam.node().getLens())
        buffer_camera.node().setCameraMask(BitMask32.bit(0))

        self.accept("v", base.bufferViewer.toggleEnable)
        
        del cm

demo = GlowDemo()
demo.run()

However, the use of one object at different stages is possible, just need to attach a callback function to the display region. And between frames to change the attributes of the object and so on. But the question arises how zakkono, say in one frame to use hide () and in the other show (). This cannot be avoided with a multi-pass render, or I reinvented the wheel???

Sorry, but I’m having a lot of trouble understanding your question.

In case it helps, it’s perfectly possible to render the same object into the depth buffer and stencil buffer (and color buffer if you wish) in one go. By default, Panda will write to the color and depth buffers, but all these things can be switched, as you have already found out.

Note that you need framebuffer-stencil true in Config.prc to enable the stencil buffer.

The essence of the question is how one and the same object to write a mask stencil and read. If I understand correctly, use the display area and callback to change the attributes of the render. This is the second question, which is essentially stupid, is this the right decision with multi-pass visualization? or it can be done differently in panda3d.

I’m just worried about performance, since changing attributes for objects between frames can have a slowdown.

A short example of what I want. However, the stencil buffer is empty at the time of the 2 stage rendera.

# -*- coding: utf_8 -*-
from panda3d.core import loadPrcFileData
loadPrcFileData("", "framebuffer-stencil #t")

from math import pi, sin, cos

from direct.showbase.ShowBase import ShowBase
from panda3d.core import LVector4, StencilAttrib, NodePath, Camera
from direct.task import Task

class StencilDemo(ShowBase):

    def __init__(self):
        ShowBase.__init__(self)
        
        self.camera.setPos(0, -10.0, 0)
        self.disableMouse()
        
        # --- Stage 1 we write the silhouettes of the figures in the stencil buffer.
        base.cam.node().getDisplayRegion(0).setDrawCallback(self.render_stage_1)
        base.win.setSort(1)

        # Load cube.
        self.cube = loader.loadModel("box")

        # --- In step 2, we render the cube using the stencil mask.
        # Create a new scene node.
        self.render2 = NodePath('render2')
        # Create a camera and configure the camera.
        self.camera2 = self.render2.attachNewNode(Camera('cam2'))
        self.camera2.node().setLens(base.cam.node().getLens())
        self.camera2.setPos(0, -10.0, 0)
        # Create a display region.
        self.stage_2 = base.win.makeDisplayRegion()
        self.stage_2.setSort(2)
        self.stage_2.setClearColorActive(True)
        self.stage_2.setClearColor(base.getBackgroundColor())
        self.stage_2.setClearDepthActive(True)
        self.stage_2.setClearDepth(1.0)
        self.stage_2.setClearStencilActive(False)

        # Set the camera for display.
        self.stage_2.setCamera(self.camera2)
        self.camera2.node().getDisplayRegion(0).setDrawCallback(self.render_stage_2)

        # Press "a" to disable the 2 stage of the render.
        self.accept("a", self.display_my_active)

        self.status = True
        
    def render_stage_1(self, cbdata):
        # Cube stencil recording.
        self.cube.setColor(LVector4(0, 1, 0, 1))
        self.cube.node().setAttrib(StencilAttrib.make(1, StencilAttrib.SCFAlways, StencilAttrib.SOKeep, StencilAttrib.SOKeep, StencilAttrib.SOReplace, 10, 0, 0xff))
        self.cube.reparentTo(render)
        cbdata.upcall()

    def render_stage_2(self, cbdata):
        # Cube stencil read.
        self.cube.setColor(LVector4(1, 0, 0, 1))
        self.cube.node().setAttrib(StencilAttrib.make(1, StencilAttrib.SCFEqual, StencilAttrib.SOKeep, StencilAttrib.SOKeep, StencilAttrib.SOReplace, 10, 0xff, 0xff))
        self.cube.reparentTo(self.render2)
        cbdata.upcall()

    def display_my_active(self):
        self.status = not self.status
        self.stage_2.setActive(self.status)

demo = StencilDemo()
demo.run()

Hurray found the reason, it turned out the panda clears the buffer: colors, depths and stencils on the completion of the frame.

call glClear
mask GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT

And not at the beginning as I thought.

It should clear it at the beginning of the window render and of each display region. Perhaps you’re observing the fact that additional display regions (such as render2d) that render afterwards also clear the buffers?

To make this code workable I added one line:
base.win.setClearStencilActive (False)
In this code, because of a logical error, need to press “a” to make it work. Maybe I need to rewrite ShowBase. To know what is happening and where.

I saw this in RenderDoc.