Complexpbr -- A Module for PBR IBL, SSAO, SSR, AA, Vertex Displacement and More in Panda3D

I am very excited to introduce complexpbr – a module built to offer straightforward support for IBL (Image-based lighting) rendering in Panda3D. Here is a short list of supported features: PBR, IBL, SSAO, SSR, custom AA, vertex displacement mapping, HSV color correction, custom BRDF generation, dynamic (or optionally static) environment reflections, and custom environment map camera positioning.

My goal here is to provide an easy to use and easily maintainable interface for applying advanced rendering techniques to a Panda3D game or program. “RenderPipeline” is an impressive piece of software, but it is rather convoluted, difficult to maintain, and has fallen victim to quite a bit of technical debt. I intend complexpbr to be an alternative for people who just want to build a cool “AA” game today, without all the hassle, and using contemporary Blender BSDF models. As a bonus, it is quite fast, and gives you the option to disable screenspace postprocessing or even use CommonFilters if you’d like.

Full source here: GitHub - rayanalysis/panda3d-complexpbr: Functional node level shader application for Panda3D.

PyPI update!

You may now use pip to install complexpbr with the following command:

pip install panda3d-complexpbr

Here’s the new package page: panda3d-complexpbr · PyPI

If you like what you see here, please consider supporting me on itch: complexpbr Demo link here: complexpbr Demo by Simulan

This is a “buy me a coffee” kind of thing, as you can already access a fleshed-out demo via GitHub - rayanalysis/Panda3D-Arena-FPS-Sample-Program: This is a modern Panda3D sample program with an Arena first person shooter environment and many basic features. This program would be useful to experienced Python programmers jumping into Panda3D with the intention of building a first person shooter.

Demo Videos:

6/14/23 Version 0.5.2 Update:

  • Added TBN logic to the screenspace_init() normal procedures to theoretically improve the accuracy of the screenspace effects. The core functions affected are getViewPos(), getNormalMatrix(), transformNormalToViewSpace(), and getViewNormal() in min_f.frag .

  • Usability improvements including no longer having to copy the output_brdf_lut.png file. complexpbr will generate a dummy LUT for you, and also offers a new optional “lut_fill” input to complexpbr.apply_shader() This feature is automatic, so if you provide the output_brdf_lut.png file in your program directory like normal, it will default to that .png image ignoring the lut_fill input.

Comparison image between versions 0.5.1 (on the left) and 0.5.2 (on the right) default screenspace_init() settings:

And 0.5.2 with default settings after copying the provided output_brdf_lut.png (now optional).


7/6/23 Lumberyard Bistro (

6/1/23 Sponza:

(initial images with more “GI”)

5/12/23 Short reflections:

5/11/23 Long reflections demo:

5/6/23 Bloom implementation:

Thanks for reading, and hope you enjoy using complexpbr.


That looks really, really impressive–and sounds like it could be an incredible boon to Panda devs!

Indeed, that is quite a feature-list that you have there, and the demo-video really looks the part! :slight_smile:

Thank you very much for making and releasing this, Simulan! :slight_smile:

(One thing that I will note, if I may, is that the reflections in the floor appear to have a bit of a scaling factor in effect. This can perhaps be seen at 1:06, where on the right a large star and two small ones close together visually approach the floor–but their reflections sit further to the right.)

1 Like

My pleasure, and thank you for the accolades!

The skybox being used for that reflection cubemap is indeed separate from the observable one above the player, though both skyboxes are at the same scale factor, I believe. I’m using the inverse projection matrix in order to apply the reflection, and the relative position of the player’s view cam is not taken into account, only the relative heading. All that said, I’d venture to say that you might be seeing the repeated stars in the texture itself, which are not perfectly randomly distributed. Finally, I’m not sure! There are a number of things one might fiddle with to customize such an apparent scaling factor, including perhaps adjusting the relative position of the skybox cuberig to match the player’s position.

1 Like

Ah, perhaps–indeed, looking closely at the left-hand side at about the same time, the stars in the reflection do look different to the ones in the sky above them.

Hi @Simulan,

Thanks for sharing! But my window shows nothing after initializing the module. The only error I received is :display(error): Shader input displacement_map is not present. Do you have any ideas about this? Thanks!

Hi, thanks for the error report. I just checked in a change which should provide a dummy texture so that it doesn’t complain about that.

The module will not show anything in the window until you actually add geometry to your scene, of course.

Adding some daytime screenshots to the original post. Somewhat of a work in progress with brighter scenes.


Those images do look really impressive. :slight_smile:

1 Like

Long Reflections Update:

I’ve recorded a quick demo video of an outdoor scene with some metallic columns to show off complexpbr’s capability to render long reflections of objects in a scene in realtime.

Additionally, I have pushed some usability improvements, including an “env_res” input to complexpbr.apply_shader() to customize your cube map resolution.

1 Like

Very cool, and rather impressive! :slight_smile:

I take it that it’s a screen-space reflection, and thus won’t include elements occluded from the viewer’s perspective?

Either way, it looks really good! :slight_smile:

Plus, of course, additional tuning parameters are appreciated. :slight_smile:

Actually, the column reflections are not screen-space reflections, they are proper cube map reflections. So, the renderer is capable of reflecting things IE behind the first person perspective frustum. I’ve witnessed myself enemy fighters exploding behind a spacecraft as reflections in the stern.

Ooh, that is cool! Nicely done, then! :slight_smile:

1 Like

Especially if you can manage to set your live env_cam_pos Z component properly the first time, unlike the designer of the module!

With the proper Z position, you can achieve short reflections as well:

This also tends to make it such that the column reflections begin at the true bottom of the column, showing the visible reflected texture instead of somewhere halfway up the column.

1 Like

This looks really cool (and I like that you’re focusing on making it simple to use - setting up shades can be such a pain). Maybe someone has a cool model or scene available that we could try this on? I feel like these simple abstract scenes never really do the rendering justice.

There’s the (CC non-commercial) damaged battle helmet, for example: glTF-Sample-Models/2.0/DamagedHelmet at master · KhronosGroup/glTF-Sample-Models · GitHub
Or a knight? I think this is also free:

1 Like

Ask and you shall receive. Here are two angles of the DamagedHelmet. I did convert the metal-roughness texture that comes with the model to grayscale, I changed the Material to BSDF Principled, and I added a scaling factor of 3 so that it appears larger.

Edit: Whoops, forgot the Emission texture the first time around.

(^ bright flashlight on facing the helmet)

There are a bunch of dials to adjust here, reflection intensity and so on.

If anybody else has a model to provide and would like to see it rendered let me know.


I know that the model is rather large but have you tried Sponza? Graphics Research Samples. Intel also has some other cool scenes for testing rendering tech on that page.


Thanks for the suggestion, that does seem like a logical example to use.

I loaded the Sponza scene with a couple different example models, and it does indeed work. I’ll have to set aside some time to get the lighting environment up to spec for demonstration screenshots. It may be a while as I’m going to be busy this month, but we’ll see.

1 Like

Here are my initial renders of the “NewSponza” scene using complexpbr. I included the Ivy, Curtains, and one shot with the Cypress as well. I did reduce the 4k texture maps to 2k for performance. The lighting demonstrated here is really straightforward:

  • AmbientLight
  • PointLight
  • Spotlight 1 (high intensity w/ 8k shadow caster)
  • Spotlight 2 (no shadow caster, opposite fill light)
  • Ambient contribution (variable AO)
  • Screenspace effects

One could take it further than this of course, given that I only spent a few hours on it. Suggestions regarding this Sponza scene are welcome.


The Sponza scene looks really good thus far, I do think! :slight_smile:

That said, I do keep feeling like something is missing, and I think that it’s a degree of specularity: I’m not seeing significant highlights anywhere. The result is a scene that feels a bit overly “flat”.

Although looking at other images online, perhaps there’s also an element of there being too much ambient light: there’s not enough contrast between light and shadow, and between well-lit areas and dimly-lit ones.

1 Like

With regard to the specular intensity, that could well be; in the getIBL() function, the specular component is multiplied by the global ‘ao’ value, which in this scene I set quite low across the board. I could probably do something like split the specular component off into a separate multiple, I’ll have to see what that looks like. Part of the trick here is knowing how the artists are setting their Roughness values, and what they intend by those values, I think.

Contrast is something I’d like to improve as well. I think it could stand to look more "HDR"ish.