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

@Simulan That’s interesting, thank you!

I’ve been playing around with it and it is looking very nice, but having a bit of an issue getting the reflections working?

I downloaded a sample you did but the reflections seem to be not referencing the correct positions. You mentioned this earlier in the thread:

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:

How do I do that?

I haven’t been able to find a reference for all the text variable inputs to the “set_shader_input()” function in the complexpbr library so I think I’m missing something big here. Sorry if this is just me missing some basic stuff.

EDIT:
I’m using your Arena-FPS sample program. When I set all the textures to mirror in blender the reflections are not alligned.

I made some progress by setting

complexpbr_z_tracking=True

Which helps fixed the reflections on the floor of the Arena, but the reflections on the walls and the spheres appear to have the wrong Z position as they appear to be originating under the floor. Also the balls are not really reflecting off each other or the wall. That’s where I’m at now :upside_down_face:

Here’s a picture

Very good questions, and yes, getting reflections looking right especially from multiple differently-shaped models can be challenging. We can usually get most of the way there for the “vertical reflections” by simply using:

        complexpbr.apply_shader(self.render)
        base.complexpbr_map_z = 0.0  # can be modified afterward
        base.complexpbr_z_tracking = True

In some cases though, the relative cubemap camera may require node-specific positioning to get the reflections correct. Here we get into more advanced usage, namely setting up a new “cube buffer” and giving it as the (preexisting complexpbr) node-level “cubemaptex” shader input like this:

box_model.set_shader_input("cubemaptex", base.cube_buffer_2.get_texture())

Where the new cube buffer is constructed something like the following:

        base.complexpbr_map_2 = NodePath('cuberig_2')
        base.cube_buffer_2 = base.win.make_cube_map('cubemap_2', 256, base.complexpbr_map_2)
        base.complexpbr_map_2.reparent_to(base.render)
        base.complexpbr_map_z_2 = 0.0

Notice the “map_2” to differentiate it from the existing and already-created base.complexpbr_map buffer. And yes, one can then move the newly created secondary cubemap camera independently of the first one with set_p(), set_h(), set_pos(), and so forth live in a task IE:

            base.complexpbr_map_2.set_h(base.render,base.cam.get_h(base.render))
            base.complexpbr_map_2.set_p(base.render,base.cam.get_p(base.render) + 90)
            base.complexpbr_map_2.set_pos(base.env_cam_pos[0],base.env_cam_pos[1],base.env_cam_pos[2]+base.complexpbr_map_z_2)

In the following screenshot I show this in practice. The sphere models here are reflecting a differently positioned cubemap from the same applied shader by taking input from a different cubemap, again using the node-level “cubemaptex” shader input which can be set arbitrarily on each model/node.

In summary, as much as I’d like the default reflection settings to be fully general, in practice these are rendering tricks attempting to approximate reality. For fully mirrored walls, I might separate the floor model from the wall model, or even apply the aforementioned custom cubemap to the wall geometry separately from the one reflecting off the floor. These are also performance and artistic choices which relate to the kind of game you want to make.

@Simulan thanks for the help, I really appreciate it! I was able to get a second cube buffer setup and apply it to the spheres, which really helped me understand what’s going on.

So essentially: to get more true-to-life reflection behavior where objects are reflecting each other I would likely need a cube map for each object?

Using your sample code I was able to get the spheres behaving well but the floor is still presenting some challenges. First off I’m getting some pretty substantial lag because the cube buffer seems to be using the previous frame as reference instead of the current frame, leading to the reflection lagging behind the view. Is there a way to fix this. I do have a pretty low frame-rate, but is there a way to render the cube maps for the current frame? Or is this a limitation of the way that these kinds of reflections are generated?

Here is picture, in this image I am moving my camera quickly to the right and you can see how the reflection shows up where the spheres previously were instead of their current position.

Second question: I was able to get control of the z adjustment for the ground reflection but for some reason it seems that it looks different depending on which direction I am looking from. Here is what I mean:

These two images are taken from opposite sides of the same sphere, and you can see that on one the z appears too low and in the other it is close but maybe a tad high. Why would the view direction affect this?


Finally I’m noticing that for the mirrored sphere, the reflection of the sphere’s reflection in the ground plane is inverted, with the reflected sky pointing up instead of down as it would be in reality. The reflection of the highlights, though, is oriented correctly.
Show reversed reflection

Again, thank you for your help. I think I’m starting to get it… gradually. :smiley:

We watched your Arcological Design video, that’s pretty cool stuff! I noticed you described a need for some lightweight representational game assets to serve as the building blocks for your simulation, I would be happy to help you make some of those (for free). Here’s an example of something I did awhile ago:

1 Like

It’s a limitation of how the reflections are generated. I’ve experimented a little with reducing the input lag but generally speaking it might not be possible. It helps to have a fast graphics card that being said.

With regard to the other reflection issues, it’s a lot to go into with regard to setting up cube buffers correctly. It’d probably be easier for me to upload an example version of the FPS program showing how mirrors and that kind of thing could potentially work. I don’t think I’ve seen the upside-down reflection skybox issue before, as the helmet scene on a mirrored bridge looks more or less correct.

Thanks for saying so!

That’s quite cool, and I appreciate the offer to help make some 3D models. A couple years back myself and some members of the Panda3D community worked on an international “space tech demo” which got pretty involved. We worked mostly over the personal message system here.

In terms of my arcology simulation talk, I was actually expressing a preference for geometric primitives in a literal sense, cubic volumes. The research right now is focused on volumetric layouts, mass and transport studies, with the built environment visualization being a separate thing. Eventually I will be working on that again (like I did for the MCS), and I can let you know when the time comes if you’re still interested.

No problem, and today I uploaded some updates and a separate mirror_demo.py program to the Arena FPS repo. As a bonus I also updated the position of the “sun” Spotlight so that it’s more in line with the skybox sun position from a shadowing perspective in the main example program and the new mirror demo. It’s pretty WIP, so if you think of any improvements feel free to submit a PR. Panda3D-Arena-FPS-Sample-Program/mirror_demo.py at 7a731e3e75997ef23ee614f4d1e58e9f42ad80c0 · rayanalysis/Panda3D-Arena-FPS-Sample-Program · GitHub

Here one can see the far wall reflecting onto its adjacent wall on the right-side in the distance:

In terms of the cube buffers used, the floor plane is using one and the spheres + walls are using another (two total) at different heights to get a consistent-looking image.

1 Like

Yeah let me know!

Thanks for taking the time to do this! I need to go through it and figure it out. I did notice that the reflections on the walls are not lined up. Here’s an example:
wall wall reflection missalignment

The idea is to get to something like this:

So possibly this comes down to the difference between cubemap reflections vs screen-space reflections? Does complexpbr support a screenspace reflection mode as well as a cubemap reflection?

I like the cubemap for the ability to see things in a 360 view, such as for the spheres, but for the walls/floor effect I’m guessing we would need to create a separate cubemap for each of the 4 walls as well as for the floor and the spheres in order to get the reflection of the spheres on the wall to behave the way it does reflecting off the floor?

Technically yes it does support a screenspace reflection mode, which is accessed like so:

    complexpbr.screenspace_init()   <--- call this first to start the SS effects
    # example of how to customize SSR
    ssr_intensity = 0.5  
    ssr_step = 4.0
    ssr_fresnel_pow = 3.0
    ssr_samples = 128  # ssr_samples defaults to 0 / off
    
    screen_quad.set_shader_input("ssr_intensity", ssr_intensity)
    screen_quad.set_shader_input("ssr_step", ssr_step)
    screen_quad.set_shader_input("ssr_fresnel_pow", ssr_fresnel_pow)
    screen_quad.set_shader_input("ssr_samples", ssr_samples)

I haven’t stress tested this implementation, so let me know how it works.

You can also adjust the cubemap rendering height when doing cubemap reflections. Notes on this below:

   # adjustment factors for the cubemap rendering height (as of version 0.5.5)
    base.complexpbr_map_z = 2.1  # manual additive/subtractive factor on the rendering height
    # automatically adjust the environment reflections such that they
    # update relative to the base.cam position during movement
    base.complexpbr_z_tracking = True  # defaults to False

That’s awesome but I couldn’t seem to get it to work. I tried activating it and disabling the cubemap with complexpbr.set_cubebuff_inactive() but nothing happened. How do I activate it?

I’ve also had some major difficulty exporting textures from blender. The textures from your samples work fine after loading them into blender and exporting them, but when I create a new texture which appears to have all the same blender settings (principaled bsdf etc) and load it into panda 3D I get these weird artifacts:

Do you know what could be causing this? I feel like I must have missed a setting somewhere or something?

With reflections things get crazy:

Thanks for bringing this issue to my attention. I generally rely on cubemap reflections for my own usage of complexpbr, but that’s no reason to have SSR in disrepair.

I can confirm that there is something wonky going on with the either the SSR settings or my implementation of SSR when using the current-gen panda3d package. I’ll have to spend some time investigating what is going on there.

Awesome! I really appreciate it!

Well, after some investigating of my SSR settings and SSR implementation in complexpbr, I have decided to upload an “ssr_demo.py” to my existing Arena FPS Sample Program. This joins the separate “main.py” main demo program as well as the “mirror_demo.py”, making a total of three sample programs in the Arena FPS Sample Program repo showcasing various reflection effects. The mirror_demo.py program shows how to achieve multiple reflection cubemap rendering heights, setting a specific secondary dynamic cubemap on particular “Plane” geometry discovered with a special Panda3D “find” command, and using complexpbr.

The new ssr_demo.py holds the screenspace_init() shader input values which I generally use when I’m looking for an SSR effect in my own programs. I have set the standard complexpbr IBL cubemap to a low intensity of “0.1” to show off the SSR reflections of the wood spheres. This demo program and these particular settings should work with the current pip install version of complexpbr, which is version 0.5.9 as of now. Using SSR like this does allow a certain improved frame-lag performance of the sphere reflections over the conventional cubemap approach. The reflections should generally be visible on various surfaces from objects in screenspace.

All that said, SSR is a finicky effect and I probably won’t spend a lot more time on this. The main thing to play around with for one’s own particular program would be the shader inputs found in the quality_mode() function near the top of ssr_demo.py such as base.screen_quad.set_shader_input('ssr_intensity', 2.0)

ssr_demo.py default SSR settings example screenshot: https://raw.githubusercontent.com/rayanalysis/Panda3D-Arena-FPS-Sample-Program/refs/heads/main/screencaps/arena_screen-Tue-Dec-03-02-52-20-2024-1143.jpg and the demo repo: 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.

Thanks simulan, I’ll check it out. I appreciate the time you put into this.

Hopefully when our game is further along I’ll have something with the artistic edge to do justice to complexpbr and we can boot those render pipeline images off the website. :stuck_out_tongue:

1 Like