A simple, lightweight PBR render pipeline for Panda3D

panda3d, gamedev, pbr, pbr-shading, rendering
pip install panda3d-simplepbr==0.10


Build Status Panda3D Versions


This is a simple, basic, lightweight, no-frills PBR render pipeline for Panda3D. It is currently intended to be used with panda3d-gltf, which will output textures in the right order. The PBR shader is heavily inspired by the Khronos glTF Sample Viewer. Note: this project does not make an attempt to match a reference renderer.


  • Supports running on a wide range of hardware with an easy OpenGL 2.1+ requirement
  • Forward rendered metal-rough PBR
  • All Panda3D light types (point, directional, spot, and ambient)
  • Filmic tonemapping
  • Normal maps
  • Emission maps
  • Occlusion maps
  • Basic shadow mapping for DirectionalLight and Spotlight

Notable Todos

There are a few big things still missing and are planned to be implemented:

  • Shadow mapping for PointLight
  • IBL Diffuse
  • IBL Specular

Other missing features

The goal is to keep this simple and lightweight. As such, the following missing features are not currently on the roadmap:

  • Something to deal with many lights (e.g., deferred, forward+, tiling, clustering, etc.)
  • Fancy post-process effects (temporal anti-aliasing, ambient occlusion, screen-space reflections)
  • Environment probes


Use pip to install the panda3d-simplepbr package:

pip install panda3d-simplepbr

To grab the latest development build, use:

pip install git+


Just add simplepbr.init() to your ShowBase instance:

from direct.showbase.ShowBase import ShowBase

import simplepbr

class App(ShowBase):
    def __init__(self):


The init() function will choose typical defaults, but the following can be modified via keyword arguments:

render_node : The node to attach the shader too, defaults to base.render if None

window : The window to attach the framebuffer too, defaults to if None

camera_node : The NodePath of the camera to use when rendering the scene, defaults to if None

msaa_samples : The number of samples to use for multisample anti-aliasing, defaults to 4

max_lights : The maximum number of lights to render, defaults to 8

use_normal_maps : Use normal maps to modify fragment normals, defaults to False (NOTE: Requires models with appropriate tangents defined)

use_emission_maps : Use emission maps, defaults to True

use_occlusion_maps : Use occlusion maps, defaults to False (NOTE: Requires occlusion channel in metal-roughness map)

enable_shadows : Enable shadow map support (breaks with point lights), defaults to False

enable_fog : Enable exponential fog, defaults to False

exposure : a value used to multiply the screen-space color value prior to tonemapping, defaults to 1.0

use_330 : Force shaders to use GLSL version 330 (if True) or 120 (if False) or auto-detect if None, defaults to None

use_hardware_skinning : Force usage of hardware skinning for skeleton animations or auto-detect if None, defaults to None

Those parameters can also be modified later on by setting the related attribute of the simplepbr pipeline returned by the init() function:

        pipeline = simplepbr.init()
        pipeline.use_normals_map = True


simplepbr expects the following textures are assigned to the following texture stages:

  • BaseColor - Modulate
  • MetalRoughness - Selector
  • Normals - Normal
  • Emission - Emission


For an example application using panda3d-simplepbr check out the viewer in the panda3d-gltf repo.

Running Tests

First install panda3d-simplepbr in editable mode along with test extras:

pip install -e .[test]

Then run the test suite with pytest:


Building Wheels

Install build:

pip install --upgrade build

and run:

python -m build


B3D 3-Clause