Shadows in Showbase AND offscreen buffer

Hi,

I am trying to render a scene not only in the ShowBase window but also into an off-screen buffer. This worked quite well until now, but now I am trying to introduce shadows from a directional light and I am observing strange artifacts. Here is a minimal code sample illustrating the problem:

import cv2
import numpy as np

from direct.showbase.ShowBase import ShowBase
from panda3d.core import (Camera, DirectionalLight, Filename,
	FrameBufferProperties, GraphicsEngine, GraphicsOutput, GraphicsPipe,
	GraphicsPipeSelection, Loader, NodePath, Texture, WindowProperties)

base = ShowBase()
base.cam.set_pos(-3, 3, 3)
base.cam.look_at(0, 0, 0)

root = NodePath("root")
root.set_shader_auto()

model_path = Filename(__file__).get_dirname() + '/models/box.egg'

root.attach_new_node(Loader().load_sync(model_path)).set_scale((5, 5, 0.1))
root.attach_new_node(Loader().load_sync(model_path)).set_pos((0, 0, 1))

dlight = DirectionalLight("dlight")
dlight.get_lens().set_film_size(5, 5)
dlight.get_lens().set_near_far(1, 8)
dlight.set_shadow_caster(True, 1024, 1024)
dlight.show_frustum()
dlnp = root.attach_new_node(dlight)
dlnp.set_pos(2, 2, 4)
dlnp.look_at(0, 0, 0)
root.set_light(dlnp)

cam = Camera("my camera")
cam_np = root.attach_new_node(cam)
cam_np.set_pos(-3, 3, 3)
cam_np.look_at(0, 0, 0)

graphics_engine = GraphicsEngine().get_global_ptr()

my_buffer = graphics_engine.makeOutput(
	pipe=GraphicsPipeSelection.get_global_ptr().make_default_pipe(),
	name='my buffer',
	sort=1,
	fb_prop=FrameBufferProperties.get_default(),
	win_prop=WindowProperties.size(400, 400),
	flags=GraphicsPipe.BFRefuseWindow)

display_region = my_buffer.make_display_region()
display_region.set_camera(cam_np)

texture = Texture()

my_buffer.add_render_texture(
	texture, GraphicsOutput.RTM_copy_ram, GraphicsOutput.RTP_color)

# variant 1:
root.reparent_to(base.render)

graphics_engine.render_frame()
graphics_engine.render_frame()

# variant 2:
#root.reparent_to(base.render)

ram = texture.get_ram_image()
rgb_im = np.frombuffer(ram.get_data(), dtype=np.uint8)
rgb_im = rgb_im.reshape(400, 400, 4)
rgb_im = cv2.flip(rgb_im, 0)
cv2.imshow('my image', rgb_im)
cv2.waitKey(100)

base.run()

Depending on whether I first reparent my scene to base.render (variant 1) or whether I first call render_frame (variant 2) the artifacts occur either in the ShowBase window or in the read out offscreen buffer:

Variant 1:

Variant 2:

Obviously, I’d like to have both images look nice :slight_smile:

Any help would be really appreciated!
Thanks!
Best
Timo

Not sure, but try to play with “prefer-parasite-buffer” option

Thanks for your answer! Unfortunately

prefer-parasite-buffer 1

and

prefer-parasite-buffer 0

did not help. I also tried to vary prefer-single-buffer and prefer-texture-buffer, but again without any success.

Actually, I have the feeling that the artifacts in the shadow map kind of reflect my screen content. Sometimes I am able to recognize parts of the eclipse ui… Might it be that I somehow have to properly clear the shadow map buffer or something like this?

Hmm. On Win7 this code

my_buffer = graphics_engine.makeOutput(
   pipe=GraphicsPipeSelection.get_global_ptr().make_default_pipe(),
   name='my buffer',
   sort=1,
   fb_prop=FrameBufferProperties.get_default(),
   win_prop=WindowProperties.size(400, 400),
   flags=GraphicsPipe.BFRefuseWindow)

destroys Panda’s app

As to the shadow problem, I think it’s a problem with not enough precision in the depth buffer for the offscreen buffer. I believe the dev version of Panda should have this already fixed; have you tried out the latest buildbot version? But ensuring you have a parasite buffer should also solve the problem, but “prefer-parasite-buffer” won’t create one if you’re asking for a larger buffer than your main window. You can try asking for a smaller buffer, or starting with a larger window; or you can set “force-parasite-buffer 1” to make it automatically create the largest parasite buffer that will fit within your window size.

As to the crash here:

my_buffer = graphics_engine.makeOutput(
   pipe=GraphicsPipeSelection.get_global_ptr().make_default_pipe(),
   name='my buffer',
   sort=1,
   fb_prop=FrameBufferProperties.get_default(),
   win_prop=WindowProperties.size(400, 400),
   flags=GraphicsPipe.BFRefuseWindow)

this is because the GraphicsPipe you are creating is getting immediately destroyed before the makeOutput() function can receive it. You shouldn’t pass it in-line like this, instead save it in a Python object to hold onto the reference count at least long enough to pass it into the function:

pipe = GraphicsPipeSelection.get_global_ptr().make_default_pipe()
my_buffer = graphics_engine.makeOutput(
   pipe=pipe,
   name='my buffer',
   sort=1,
   fb_prop=FrameBufferProperties.get_default(),
   win_prop=WindowProperties.size(400, 400),
   flags=GraphicsPipe.BFRefuseWindow)

But it would be even better to use the same pipe object that was already created for you:

my_buffer = graphics_engine.makeOutput(
   pipe=base.pipe,
   name='my buffer',
   sort=1,
   fb_prop=FrameBufferProperties.get_default(),
   win_prop=WindowProperties.size(400, 400),
   flags=GraphicsPipe.BFRefuseWindow)

David

David,

thank you for your reply!
I am ashamed of having to admit that again, as you suggested, updating to the most recent code version resolved my problem :slight_smile:

Best
Timo