Anomaly when using ColorWriteAttrib

Playing with stencil buffer capabilities, doing experiments on subtracting objects from each other, I ran into this bug. The problem is in the line cube.node (). SetAttrib (ColorWriteAttrib.make (0)) It makes the second object also invisible.


from direct.showbase.ShowBase import ShowBase
from panda3d.core import DirectionalLight, AmbientLight, LVector4, ColorWriteAttrib

class StencilDemo(ShowBase):

    def __init__(self):
        ShowBase.__init__(self)
        base.cam.setPos(0, -10, 0)
        
        alight = AmbientLight('alight')
        alight.setColor(LVector4(0.5, 0.5, 0.5, 1))
        alnp = render.attachNewNode(alight)
        render.setLight(alnp)
        
        dlight = DirectionalLight('dlight')
        dlight.setColor(LVector4(1.0, 1.0, 1.0, 1))
        dlight.setColorTemperature(4000)
        dlnp = render.attachNewNode(dlight)
        dlnp.setHpr(0, -60, 0)
        render.setLight(dlnp)

        cube = loader.loadModel("cube")
        cube.node().setAttrib(ColorWriteAttrib.make(0))
        cube.setColor(LVector4(0, 1, 0, 1))
        cube.setPos(0, 0, 0)
        cube.reparentTo(render)

        cube1 = loader.loadModel("cube")
        cube1.setColor(LVector4(1, 0, 0, 1))
        cube1.setPos(0, 0, 0)
        cube1.reparentTo(render)

demo = StencilDemo()
demo.run()

But I need this line so that I can use the depth test.

I found out this problem arises because of the coincidence of the name of the vertex group. If you create a copy of the cube file and change the name of the vertex group, the problem will disappear. I wonder how can get around this? After all, loading the same object is not an option.

This seems to be a more global problem. Of course, perhaps the problem is that I read the old OpenGL tutorial. But setAttrib (ColorWriteAttrib.make (0)) creates a curvature of space like a black hole in the center of the galaxy. :grin::grin::grin: There was a workaround for solving the problem of using the “Never” value to fail a test, as suggested in the manual. However, the panda does not write a depth buffer in this case. Although OpenGL does it. :thinking:

I am confused about what you are asking. When I run your code, I see only a red cube, as expected.

Maybe you are expecting the order in which the cubes are rendered to be defined? Panda doesn’t guarantee a particular order, so one may be rendered after the other. You need to use setBin to control the objects to be ordered in a particular order if you want this.

Changing the vertex group may just happen to change some internal state mechanism such that Panda happens to sort them in a different order, but you should not rely on this.

Hmm, I don’t have an object, the effect of an invisible predator is created. This can be seen if the coordinates are shifted.

python(python.trace).zip (1.0 MB)

If I use setBin to define an ordering such that the “green” (invisible) cube is rendered first, I see nothing. That is to be expected, not? You are still writing it to the depth buffer, so the other object will not show up. This is how Panda is expected to behave.

Maybe you are trying to do something like this?

from direct.showbase.ShowBase import ShowBase
from panda3d.core import DirectionalLight, AmbientLight, LVector4, ColorWriteAttrib

class StencilDemo(ShowBase):

    def __init__(self):
        ShowBase.__init__(self)
        base.cam.setPos(0, -10, 0)
        
        alight = AmbientLight('alight')
        alight.setColor(LVector4(0.5, 0.5, 0.5, 1))
        alnp = render.attachNewNode(alight)
        render.setLight(alnp)
        
        dlight = DirectionalLight('dlight')
        dlight.setColor(LVector4(1.0, 1.0, 1.0, 1))
        dlight.setColorTemperature(4000)
        dlnp = render.attachNewNode(dlight)
        dlnp.setHpr(0, -60, 0)
        render.setLight(dlnp)

        sphere = loader.loadModel("sphere")
        sphere.node().setAttrib(ColorWriteAttrib.make(0))
        sphere.reparentTo(render)
        sphere.setBin('fixed', 1)

        cube1 = loader.loadModel("cube")
        cube1.reparentTo(render)
        cube1.setBin('fixed', 2)

demo = StencilDemo()
demo.run()

I am not sure what exactly the effect is you are looking for. The tutorial you linked uses the stencil buffer, which offers more flexibility than you can get with just the depth buffer.

1

Panda example, is this the correct setAttrib behavior (ColorWriteAttrib.make (0))?

2
3

When you disable color write, depth write is still enabled. So it is still rendering the new depth values for the green Panda to the depth buffer.

So when the second object renders after the first one is already in the framebuffer, OpenGL will perform a depth test before writing the red panda fragments. This means it will check if there is already an object in the depth buffer in front of the current pixel, and if so, it will then avoid writing the red Panda fragment. After all, it thinks there is something in front of that pixel, so it won’t waste time computing the red Panda pixel color!

So this is exactly what you expect to see: where the green object has already been written to the depth buffer in front of the red panda, the red object won’t be shown; but all this will only be the case if the green panda is rendered first and the red panda second, which is not something Panda guarantees.

Maybe you can explain what you are trying to achieve and I could help you find the right combination of render parameters for that.

I’m not trying to achieve, I have a problem object with setAttrib (ColorWriteAttrib.make (0)) overlaps another object.

Source. If you see the same thing. The I understood everything is not a mistake.

from direct.showbase.ShowBase import ShowBase
from panda3d.core import DirectionalLight, AmbientLight, LVector4, ColorWriteAttrib

class StencilDemo(ShowBase):

    def __init__(self):
        ShowBase.__init__(self)
        base.cam.setPos(0, -10, 0)
        
        alight = AmbientLight('alight')
        alight.setColor(LVector4(0.5, 0.5, 0.5, 1))
        alnp = render.attachNewNode(alight)
        render.setLight(alnp)
        
        dlight = DirectionalLight('dlight')
        dlight.setColor(LVector4(1.0, 1.0, 1.0, 1))
        dlight.setColorTemperature(4000)
        dlnp = render.attachNewNode(dlight)
        dlnp.setHpr(0, -60, 0)
        render.setLight(dlnp)

        cube = loader.loadModel("panda")
        cube.node().setAttrib(ColorWriteAttrib.make(0))
        cube.setColor(LVector4(0, 1, 0, 1))
        cube.setPos(0, 0, 1)
        cube.reparentTo(render)

        cube1 = loader.loadModel("panda")
        cube1.setColor(LVector4(1, 0, 0, 1))
        cube1.setPos(0, 0, -1)
        cube1.reparentTo(render)

demo = StencilDemo()
demo.run()

Result: https://discourse.panda3d.org/uploads/default/original/2X/4/4cf82c1ea84c0084a1d5f80af4791e5c2f82485d.jpeg

Yes, I see the same thing as you do: a red Panda with bits missing. This is a correct result given your render parameters.

Actually, you should arbitrarily see either this or a fully red Panda. The order in which two objects are rendered is not defined by default in Panda. So random changes may make you see a fully red Panda instead. Use setBin to specify a particular order in which they should be rendered.

As far as I know, it will spoil the scene. In the end, it may happen that the panda will be on top of others.

As for writing to the depth buffer when the stencil test fails, I think I’ll check it on OpenGL.

Although no, I found out when nothing is displayed on the screen and, accordingly, nothing is added to the depth buffer.

The correct solution would be to use setBin ("unsorted", 0)
for all objects, this will keep order when added to the scene.

When a stencil test fails, the fragment will be discarded and not be written to the depth buffer. This is different behaviour from just having a ColorWriteAttrib, which has nothing to do with the stencil buffer (it is not a test, like depth test or stencil test, it only affects how the color is blended to the framebuffer)

It seems that setBin (“unsorted”, 0) is clearly not appropriate, so this means that it should be added first, and the stencil buffer is not full at this stage.

I already think that this OpenGL example cannot be solved on panda3d.
In this case, the order of working with the stencil buffer is important, the panda does not want to visualize the object, therefore there is an entry for the other object in the depth buffer. :hushed:

My code:

from panda3d.core import loadPrcFileData
loadPrcFileData("", "framebuffer-stencil #t")

from direct.showbase.ShowBase import ShowBase
from panda3d.core import DirectionalLight, AmbientLight, LVector4, StencilAttrib, ColorWriteAttrib

class StencilDemo(ShowBase):

    def __init__(self):
        ShowBase.__init__(self)
        base.cam.setPos(0, -10, 0)
        
        alight = AmbientLight('alight')
        alight.setColor(LVector4(0.5, 0.5, 0.5, 1))
        alnp = render.attachNewNode(alight)
        render.setLight(alnp)
        
        dlight = DirectionalLight('dlight')
        dlight.setColor(LVector4(1.0, 1.0, 1.0, 1))
        dlight.setColorTemperature(4000)
        dlnp = render.attachNewNode(dlight)
        dlnp.setHpr(0, -60, 0)
        render.setLight(dlnp)

        # ------------------------

        cube = loader.loadModel("cube")
        cube.node().setAttrib(ColorWriteAttrib.make(0))
        cube.setColor(LVector4(0, 0, 1, 1))
        cube.setBin('fixed', 1)
        cube.reparentTo(render)
        # Запись трафарета.
        cube.node().setAttrib(StencilAttrib.make(1, StencilAttrib.SCFAlways, StencilAttrib.SOReplace, StencilAttrib.SOReplace, StencilAttrib.SOReplace, 10, 0, 10))

        sphere = loader.loadModel("sphere")
        sphere.node().setAttrib(ColorWriteAttrib.make(0))
        sphere.setColor(LVector4(0, 1, 0, 1))
        sphere.setBin('fixed', 2)
        sphere.reparentTo(render)
        # Запись трафарета.
        sphere.node().setAttrib(StencilAttrib.make(1, StencilAttrib.SCFAlways, StencilAttrib.SOKeep, StencilAttrib.SOKeep, StencilAttrib.SOReplace, 20, 0, 20))
        
        cube1 = loader.loadModel("cube")
        cube1.setColor(LVector4(1, 0, 0, 1))
        cube1.setBin('fixed', 3)
        cube1.reparentTo(render)

        # Чтение трафарета.
        cube1.node().setAttrib(StencilAttrib.make(1, StencilAttrib.SCFEqual, StencilAttrib.SOKeep, StencilAttrib.SOKeep, StencilAttrib.SOKeep, 10, 10, 0))



demo = StencilDemo()
demo.run()

cube.egg.pz (374 Bytes)

sphere.egg.pz (106.2 KB)

If change the sorting order in the opposite direction, violate the stencil buffer.

I partially solved the problem using StencilAttrib.SCFNever for a cube stencil test. This allowed to get rid of setAttrib (ColorWriteAttrib.make (0)) There is a problem with the sphere.

Code:

from panda3d.core import loadPrcFileData
loadPrcFileData("", "framebuffer-stencil #t")

from direct.showbase.ShowBase import ShowBase
from panda3d.core import DirectionalLight, AmbientLight, LVector4, StencilAttrib, ColorWriteAttrib

class StencilDemo(ShowBase):

    def __init__(self):
        ShowBase.__init__(self)
        base.cam.setPos(0, -10, 0)
        
        alight = AmbientLight('alight')
        alight.setColor(LVector4(0.5, 0.5, 0.5, 1))
        alnp = render.attachNewNode(alight)
        render.setLight(alnp)
        
        dlight = DirectionalLight('dlight')
        dlight.setColor(LVector4(1.0, 1.0, 1.0, 1))
        dlight.setColorTemperature(4000)
        dlnp = render.attachNewNode(dlight)
        dlnp.setHpr(0, -60, 0)
        render.setLight(dlnp)

        # ------------------------

        cube = loader.loadModel("cube")
        cube.setColor(LVector4(0, 0, 1, 1))
        cube.setBin('fixed', 1)
        cube.reparentTo(render)
        # Запись трафарета.
        cube.node().setAttrib(StencilAttrib.make(1, StencilAttrib.SCFNever, StencilAttrib.SOReplace, StencilAttrib.SOReplace, StencilAttrib.SOReplace, 10, 0, 10))

        sphere = loader.loadModel("sphere")
        sphere.node().setAttrib(ColorWriteAttrib.make(0))
        sphere.setColor(LVector4(0, 1, 0, 1))
        sphere.setBin('fixed', 2)
        sphere.reparentTo(render)
        # Запись трафарета.
        sphere.node().setAttrib(StencilAttrib.make(1, StencilAttrib.SCFAlways, StencilAttrib.SOKeep, StencilAttrib.SOKeep, StencilAttrib.SOReplace, 20, 0, 20))
        
        cube1 = loader.loadModel("cube")
        cube1.setColor(LVector4(1, 0, 0, 1))
        cube1.setBin('fixed', 3)
        cube1.reparentTo(render)

        # Чтение трафарета.
        cube1.node().setAttrib(StencilAttrib.make(1, StencilAttrib.SCFEqual, StencilAttrib.SOKeep, StencilAttrib.SOKeep, StencilAttrib.SOKeep, 10, 10, 0))



demo = StencilDemo()
demo.run()

I think it is possible to achieve what the tutorial does in Panda, but note this line:

glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

It clears the color and depth buffer before rendering the last cube. To achieve this in Panda exactly you need to put the last cube in a new DisplayRegion, with clear settings enabled. You can use a new camera for the second display region and use draw masks (setCameraMask, in combination with hide(mask) and show(mask)) to selectively show/hide objects in different regions, or use a separate scene graph.

For the record, the correct stencil settings to match the OpenGL code exactly are this:

        cube = loader.loadModel("cube")
        cube.setColor(LVector4(0, 0, 1, 1))
        cube.setBin('fixed', 1)
        cube.reparentTo(render)
        cube.setScale(2.5)
        # Запись трафарета.
        cube.node().setAttrib(StencilAttrib.make(1, StencilAttrib.SCFAlways, StencilAttrib.SOKeep, StencilAttrib.SOKeep, StencilAttrib.SOReplace, 1, 0, 0xff))

        sphere = loader.loadModel("sphere")
        sphere.setColor(LVector4(0, 1, 0, 1))
        sphere.setBin('fixed', 2)
        sphere.reparentTo(render)
        sphere.setScale(1.5)
        # Запись трафарета.
        sphere.node().setAttrib(StencilAttrib.make(1, StencilAttrib.SCFAlways, StencilAttrib.SOKeep, StencilAttrib.SOKeep, StencilAttrib.SOReplace, 2, 0, 0xff))
        
        cube1 = loader.loadModel("cube")
        cube1.setColor(LVector4(1, 0, 0, 1))
        cube1.setBin('fixed', 3)
        cube1.reparentTo(render)
        cube1.setScale(2.5)

        # Чтение трафарета.
        cube1.node().setAttrib(StencilAttrib.make(1, StencilAttrib.SCFEqual, StencilAttrib.SOKeep, StencilAttrib.SOKeep, StencilAttrib.SOReplace, 1, 0xff, 0xff))
1 Like

I tried your recommendation, but the depth buffers are not cleared.

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 DirectionalLight, AmbientLight, LVector4, StencilAttrib, ColorWriteAttrib, NodePath, Camera
from direct.task import Task

class StencilDemo(ShowBase):

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

        alight = AmbientLight('alight')
        alight.setColor(LVector4(0.5, 0.5, 0.5, 1))
        alnp = render.attachNewNode(alight)
        render.setLight(alnp)
        
        dlight = DirectionalLight('dlight')
        dlight.setColor(LVector4(1.0, 1.0, 1.0, 1))
        dlight.setColorTemperature(4000)
        dlnp = render.attachNewNode(dlight)
        dlnp.setHpr(0, -60, 0)
        render.setLight(dlnp)

        # --- 1 stage render.

        base.win.setSort(1)
        
        cube = loader.loadModel("cube")
        cube.setColor(LVector4(0, 0, 1, 1))
        cube.reparentTo(render)
        # Запись трафарета.
        cube.node().setAttrib(StencilAttrib.make(1, StencilAttrib.SCFAlways, StencilAttrib.SOKeep, StencilAttrib.SOKeep, StencilAttrib.SOReplace, 10, 0, 0xff))

        sphere = loader.loadModel("sphere")
        sphere.setColor(LVector4(0, 1, 0, 1))
        sphere.reparentTo(render)
        # Запись трафарета.
        sphere.node().setAttrib(StencilAttrib.make(1, StencilAttrib.SCFAlways, StencilAttrib.SOKeep, StencilAttrib.SOKeep, StencilAttrib.SOReplace, 20, 0, 0xff))

        # --- 2 stage render.
        
        # Создаем новый узел сцены.
        render2 = NodePath('render2')

        # Создаем камеру.
        self.camera2 = render2.attachNewNode(Camera('cam2'))
        self.camera2.node().setLens(base.cam.node().getLens())

        # Создаем дисплей.
        #display_my = base.win.makeDisplayRegion(0.5, 1, 0.5, 1)
        display_my = base.win.makeDisplayRegion()
        display_my.setSort(2)
        
        display_my.setClearColorActive(True)
        display_my.setClearColor(base.getBackgroundColor())

        display_my.setClearDepthActive(True)
        display_my.setClearDepth(1.0)
        
        # Устанавливаем камеру для дисплея.
        display_my.setCamera(self.camera2)
        
        cube1 = loader.loadModel("cube")
        cube1.setColor(LVector4(1, 0, 0, 1))
        cube1.reparentTo(render2)
        # Чтение трафарета.
        cube1.node().setAttrib(StencilAttrib.make(1, StencilAttrib.SCFEqual, StencilAttrib.SOKeep, StencilAttrib.SOKeep, StencilAttrib.SOReplace, 10, 0xff, 0xff))

        # Запускаем вращение камеры вокруг куба.
        self.taskMgr.add(self.spinCameraTask, "SpinCameraTask")
        
    # Функция вращения камеры.
    def spinCameraTask(self, task):
        angleDegrees = task.time * 80.0
        angleRadians = angleDegrees * (pi / 180.0)
        self.camera.setPos(10 * sin(angleRadians), -10.0 * cos(angleRadians), 0)
        self.camera.setHpr(angleDegrees, 0, angleDegrees)
        
        self.camera2.setPos(10 * sin(angleRadians), -10.0 * cos(angleRadians), 0)
        self.camera2.setHpr(angleDegrees, 0, angleDegrees)
        return Task.cont

demo = StencilDemo()
demo.run()

I would expect that you would need to make sure that the second camera renders the second cube, and that the first camera renders the first. And to disable the stencil clear.

        # Hide cube and sphere from second camera
        self.camera2.node().setCameraMask(0b010)
        cube.hide(0b010)
        sphere.hide(0b010)

        # Hide cube1 from normal camera
        self.camNode.setCameraMask(0b001)
        cube1.hide(0b001)
        cube1.show(0b010)

        display_my.setClearStencil(False)

s it not enough different nodes for the scene? I think that can achieve a result using the callback function, but for this need the function of direct buffer cleaning.