PBR support in 1.11?

I remember that one of the focus of the 1.11 release was to add support for PBR, or at least provide the framework to support PBR pipelines, but is it still the case ?

So far I’m happily using panda3d-gltf to imports PBR based models and apart from a few bugs or limitations here and there it’s working fine. However there is one missing feature in Panda right now that is cumbersome and will grow more and more annoying in the future (at least for me :stuck_out_tongue: ), the texture stage has nothing to support PBR texture mode. There are mode for modular, normal map, specular, glow, gloss, … and their combination, but nothing for PBR related data like roughness, metalness, AO, …

I have my own shader generator and I’m using the texture stage mode to enable certain rendering feature or not. To support this with meshes loaded by panda3d-gltf I patched it to add texture stage mode, for example the metal/roughness map is set as a gloss map, but this is not a long term solution. Already I have trouble to detect if a AO map is present or not, and that hack can not work with more advanced extensions like clearcoat or sheen.

My question is, could there be support for more texture stage modes in 1.11, even if the default shader generator of Panda does not support them, a bit like the PBR parameters in Material that are not used natively ?

The very baseline of PBR support that I want to get in 1.11 is a more idiomatic and built-in way to specify “PBR” textures (roughness, metallic, etc.). This will be my next priority after I finish the new shader pipeline (which is making great progress).

I am not personally really happy with using texture stage modes, because you can have multiple stages with the same mode and it’s not quite clear how they should interact. I have been proposing to put the textures on Material instead. It feels like the textures belong with the other material parameters, though it is a bit of a shift away from the more close-to-the-metal philosophy behind TextureAttrib. The alternative is to change the texture stage system to a “texture slot” system.

You’d be very welcome to comment on the proposal. Other ideas are also welcome.

2 Likes

Thank you for the good news :slight_smile:

I also don’t like much the texture stages, so the move to “full” material seems a good idea and more natural, at least for a end user point of view! I have to read the proposal more carefully though.

My two questions/concerns now are :

  • how easy would it be to create or derive new materials ? For example if an app needs a glass material or extend an existing material to add a new concept (e.g. anisotropy). Would it be possible to derive a new material class in Python (with all the needed functions and hooks) to support the new attributes and textures ?

  • Could the material approach limits the flexibility of the workflow used ? For example if my model has a specular color map instead of a specular level map ? (Or that could be handled by another material type)

I think materials should be subclassable. I intend for the standard PBR material to be in a Material subclass called StandardMaterial. I haven’t yet given much thought to extending material models from Python. But that’d be an easy problem to solve at that point.

The idea of adopting the now industry-standard material model with standard slots is that it should become easier to exchange models, shaders and materials—most of the time, it should “just work” even when mix-and-matching materials and shaders. However, if you want to use your own material models, it should remain possible to subclass StandardMaterial and add additional slots, or subclass Material and redefine the slots entirely.

The flipside is that something that used to be relatively simple in the fixed-function pipeline, like texture blending, now becomes hard. After all, you’d need to create an extended material model that is able to take in a second texture and then you need to make a shader that is able to interpret this correctly.

This is why I’m still a little uncertain about the proposal. I think the best way to solve this is to have some kind of node-based material system like Blender has, but that’s certainly an unrealistic target for 1.11.

However, at least the StandardMaterial will give people a solution for PBR, without requiring our default shaders to implement tremendously complicated texture blending pipelines, and we can later build on this to create more flexible Material subclasses.

I used to stick to the idea of binding textures as part of materials. However, when I experimented with the low-level API, I found that RenderStage meets all the requirements that describe the asset. I fully configure the asset and save it as a bam file. Next, I just load the RenderStage and apply it to the geometry. The only problem is the lack of named input textures in the shader. For example, there is not enough input to transfer the opacity texture and so on.

Now I don’t see the need for materials that will reference textures. Using RenderStage allows you to fully customize the display and apply it immediately in one way, including shading. This slightly changes the sequence of actions for users, but it is very flexible and does not require changes to the panda API, accordingly there is no bias in any shading style, for example PBR.

1 Like

Do you mean TextureStage?

This is the route we are actually going in 1.11, in contrary to my previous post. We do have named texture inputs nowadays (named after the type of slot).

For metallic-roughness maps, the convention is to use M_selector, which can be accessed with p3d_TextureSelector in the shader, but this will be renamed in 1.11.

1 Like

No, I was talking specifically about RenderStage

sampler_state = SamplerState()
sampler_state.set_magfilter(SamplerState.FT_linear_mipmap_linear)
sampler_state.set_minfilter(SamplerState.FT_linear_mipmap_linear)

albedo_texture = TexturePool.load_texture("pbr_maps/albedoMap.png")
albedo_texture.set_magfilter(SamplerState.FT_linear_mipmap_linear)
albedo_texture.set_minfilter(SamplerState.FT_linear_mipmap_linear)

albedo_stage = TextureStage('albedo_stage')
albedo_stage.set_mode(TextureStage.M_modulate)

normal_texture = TexturePool.load_texture("pbr_maps/normalMap.png")
normal_texture.set_magfilter(SamplerState.FT_linear_mipmap_linear)
normal_texture.set_minfilter(SamplerState.FT_linear_mipmap_linear)

normal_stage = TextureStage('normal_stage')
normal_stage.set_mode(TextureStage.M_normal)

selector_texture = TexturePool.load_texture("pbr_maps/selectorMap.png")
selector_texture.set_magfilter(SamplerState.FT_linear_mipmap_linear)
selector_texture.set_minfilter(SamplerState.FT_linear_mipmap_linear)

selector_stage = TextureStage('selector_stage')
selector_stage.set_mode(TextureStage.M_selector)

texture_attrib = TextureAttrib.make_default()

texture_slot_1 = texture_attrib.add_on_stage(albedo_stage, albedo_texture, sampler_state, 0)
texture_slot_2 = texture_slot_1.add_on_stage(normal_stage, normal_texture, sampler_state, 0)
texture_slot_3 = texture_slot_2.add_on_stage(selector_stage, selector_texture, sampler_state, 0)

material = Material("Test")
material.set_base_color(Vec4(1, 1, 1, 1))
material.set_emission(Vec4(0, 0, 0, 1))
material.set_roughness(1.0)
material.set_metallic(1.0)

render_state = RenderState.make_empty()

slot_1 = render_state.add_attrib(texture_slot_3, 1)
slot_2 = slot_1.add_attrib(MaterialAttrib.make(material), 1)
slot_3 = slot_2.add_attrib(TransparencyAttrib.make(TransparencyAttrib.M_alpha), 1)

file = BamFile()
file.open_write('plane.rstage')
writer = file.get_writer()
writer.write_object(slot_3)
writer.flush()
file.close()

Next, I just do this in the code and get ready assets.

file = BamFile()
file.open_read('plane.rstage')
file.get_reader()
render_state = file.read_object()
file.resolve()

plane = loader.load_model('plane.egg')
plane.set_state(render_state)
plane.reparent_to(render)

Everything works fine, the material settings, texture and transparency attribute are preserved, in this example I did not use a shader. I think the shader will work too.

But the problem with texture slots is present when using names, for example, there is nowhere to keep transparency. This needs to be done in a separate texture, since filtering is disabled individually, so that it does not act on translucency.

ADD:

I should clarify that I was talking about the exchange of assets between users. As for the input data for the shader, I think the indexed way remains relevant. We just need a blender plugin that will allow us to assign any number of stages to create textures of any type.

There are no problems with Egg in this regard. As for glTF 2.0, it will only allow exporting a simple pbr.

In fact, this is the problem that the glTF 2.0 format specification does not allow storing other stages of textures. Expanding the specification does not solve the problems, so it is unclear why then abandon the native format .bam or .egg

ADD:
Hmm, research has shown that when exporting to glTF 2.0, roughness and metallicity maps are always exported to png, even if they were originally .dds. In addition, the size changes to the largest according to the textures.

Which creates additional problems, png needs to be converted back to the desired format. Monitor the size of all roughness and metallicity textures so that it is the same. It looks like the Khronos specification only adds problems, there is no clarity why it is necessary to combine the textures of metalicity and roughness into one.

However, I have no doubt that this can only be fixed by our own exporters for the blender, using texture indexes is the most reliable way. However , for this you need to develop your own exporter for support .bam or .egg.

Alas, there is no other way out.