Skip to content

Commit

Permalink
pass until end of docs
Browse files Browse the repository at this point in the history
  • Loading branch information
wjakob committed Mar 3, 2020
1 parent f3cfafc commit 04f03a4
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 30 deletions.
71 changes: 43 additions & 28 deletions docs/src/advanced_topics/custom_plugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,39 @@
Custom plugins in Python
========================

In Mitsuba 2, the Python bindings provide a mechanism to implement custom plugins directly in Python.
This can be achieved by extending one of the plugin's base class (e.g. `BSDF`, `Emitter`) and
overwriting the class methods (e.g. ``sample()``, ``eval()``, ...). This mechanism leverages the
`trampoline feature <https://pybind11.readthedocs.io/en/stable/advanced/classes.html#overriding-virtual-functions-in-python>`_ of *pybind11*.
Mitsuba 2 provides a mechanism to implement custom plugins *directly in
Python*. To do so, simply extend a base class (e.g. `BSDF`, `Emitter`) and
override its class methods (e.g. ``sample()``, ``eval()``, ...). This leverages
the `trampoline feature
<https://pybind11.readthedocs.io/en/stable/advanced/classes.html#overriding-virtual-functions-in-python>`_
of *pybind11*.

The custom Python plugin can then be registered to the XML parser by calling one of the
``register_<type>(name, constructor)`` Python function as described later in the examples. By doing
so, it is possible to refer to this new plugin in the XML scene description file as with any other
C++ plugins.
The new plugin can then be registered with the XML parser by calling one of
several ``register_<type>(name, constructor)`` functions, making it accessible
in the XML scene language like any other C++ plugin.

.. note:: This mechanism currently only supports custom Python plugins for `BSDF`, `Emitter` and
`Integrator`.
.. warning::

The rest of this section discusses various examples using this mechanism:
Only ``gpu_*`` variants will produce reasonable performance
when using such Python implementations of core system components (since
they can be JIT-compiled along with other system components). We plan to
extend this feature to further variant types in the future.

Furthermore, only ``BSDF``, ``Emitter``, and ``Integrator`` plugins can
currently be implemented in Python.

The remainder of this section discusses several examples.


BSDF example
------------

In this example, we will implement a simple diffuse BSDF in Python by extending the :code:`BSDF`
base class. The code is very similar to the diffuse BSDF implemented in C++
(in :code:`src/bsdf/diffuse.cpp`).
In this example, we will implement a simple diffuse BSDF in Python by extending
the :code:`BSDF` base class. The code is very similar to the diffuse BSDF
implemented in C++ (in :code:`src/bsdf/diffuse.cpp`).

The BSDF class need to implement the following 3 methods: :code:`sample`, :code:`eval` and :code:`pdf`:
The BSDF class need to implement the following 3 methods: :code:`sample`,
:code:`eval` and :code:`pdf`:

.. literalinclude:: ../../examples/04_diffuse_bsdf/diffuse_bsdf.py
:language: python
Expand All @@ -44,7 +53,7 @@ instances. After that, we can use our new BSDF in a XML scene file by specifying

.. code-block:: xml
<BSDF type="mydiffusebsdf"/>
<bsdf type="mydiffusebsdf"/>
The scene can then rendered by calling the standard :code:`render` function:

Expand Down Expand Up @@ -72,25 +81,31 @@ The code is very similar to the direct illumination integrator implemented in C+
The function takes the current scene, sampler and array of rays as arguments.
The :code:`active` argument specifies which lanes are active.

Similar to before, we first intersect the rays with the scene.
We then first evaluate the radiance of directly visible emitters.
Then, we explicitly sample positions on emitters and evaluate their contributions.
Additionally, we sample a ray direction according to the BSDF and evaluate whether these rays also hit some emitters.
These different contributions are then combined using multiple importance sampling to reduce variance.
We first intersect the provided rays against the scene and evaluate the
radiance of directly visible emitters. Then, we explicitly sample positions on
emitters and evaluate their contributions. Additionally, we sample a ray
direction according to the BSDF and evaluate whether these rays also hit some
emitters. These different contributions are then combined using multiple
importance sampling to reduce variance.

This function will be invoked for an array of different rays, hence each ray can then potentially hit a surface with a different BSDF.
Therefore, :code:`bsdf = si.bsdf(rays)` will be an array of different BSDF pointers.
To then call member functions of these different BSDFs, we have to invoke the according vectorized functions, e.g.
This function will be invoked for an array of different rays, hence each ray
can then potentially hit a surface with a different BSDF. Therefore,
:code:`bsdf = si.bsdf(rays)` will be an array of different BSDF pointers. To
then call member functions of these different BSDFs, we invoke special dispatch
functions for vectorized method calls:

.. literalinclude:: ../../examples/03_direct_integrator/direct_integrator.py
:language: python
:lines: 38-39

This ensures that implementation provided by the different BSDF models will be called.
Other than that, the code and used interfaces are nearly identical to the C++ version.
Please refer to the documentation of the C++ types for details on the different functions and objects.
This will ensure that the C++ or Python implementation of the right BSDF model
is invoked for each variant. Other than that, the code and used interfaces are
nearly identical to the C++ version. Please refer to the documentation of the
C++ types for details on the different functions and objects.

When implementing the depth integrator, we did a lot of manual work to correctly sample rays from the camera and splatting samples into the film.
When implementing the depth integrator in the section on
:ref:`custom rendering pipelines <sec-rendering-scene-custom>`, considerable work was
necessary to correctly sample rays from the camera and splat samples into the film.
While this can be very useful for certain applications, it is also a bit tedious.
In many cases, we simply want to implement a custom integrator and not bother with how camera rays are exactly generated.
In this example, we will therefore use a more elegant mechanism, which allows to simply extend the :code:`SamplingIntegrator` base class.
Expand Down
4 changes: 2 additions & 2 deletions docs/src/developer_guide/polarization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ The horizontally polarized light (with :math:`\mathbf{s} = [1, 1, 0, 0]`) shown
:align: center


During rendering, light will of course also interact with matter which will generally affect its polarization state. This change is desribed with a *Mueller matrix* :math:`\mathbf{M} \in \mathbb{R}^{4\times4}`. After a reflection (or transmission), the incident (:math:`\mathbf{s}_i`) and outgoing (:math:`\mathbf{s}_o`) Stokes vectors are related with :math:`\mathbf{s}_o = \mathbf{M}\mathbf{s}_i`. This raises the same questions about reference frames as in the Stokes vector case, only that now *two* frames need to be defined for both incident and outgoing direction of the interaction:
During rendering, light will of course also interact with matter which will generally affect its polarization state. This change is described by a *Mueller matrix* :math:`\mathbf{M} \in \mathbb{R}^{4\times4}`. After a reflection (or transmission), the incident (:math:`\mathbf{s}_i`) and outgoing (:math:`\mathbf{s}_o`) Stokes vectors are related by :math:`\mathbf{s}_o = \mathbf{M}\mathbf{s}_i`. This raises the same questions about reference frames as in the Stokes vector case, only that now *two* frames need to be defined for both incident and outgoing direction of the interaction:

.. image:: ../../images/polarization_mueller_matrix.svg
:width: 100%
Expand All @@ -53,4 +53,4 @@ Luckily, such a frame rotation can itself be expressed with a simple Mueller mat
A last important detail in polarized rendering is the question of reciprocity. Unfortunately, pBSDFs generally do not follow *Helmholtz reciprocity* like standard BSDFs do, and are only defined along the direction of light. This complicates the implementation of some (e.g. bidirectional) rendering techniques slightly :cite:`Jarabo2018BidirectionalPol` :cite:`Mojzik2016BidirectionalPol`:

- When tracing radiance emitted from the light sources (e.g. in a light tracer), everything follows the standard flow of light. Here, the simulation can simply track Stokes vectors instead of radiance and perform the necessary Mueller matrix multiplications.
- When tracing importance "emitted" from sensors (e.g. in a path tracer), things are reversed and we need to track the (Mueller matrix valued) throughput from the sensor side. Additional care is needed in order to apply the right order of operations when multiplying Mueller matrices at the encountered surfaces here.
- When tracing importance "emitted" from sensors (e.g. in a path tracer), things are reversed and we need to track the (Mueller matrix valued) throughput from the sensor side. Additional care is needed in order to apply the right order of operations when multiplying Mueller matrices at the encountered surfaces here.
2 changes: 2 additions & 0 deletions docs/src/python_interface/rendering_scene.rst
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ in Python:
:file:`docs/examples/01_render_scene/render_scene.py`


.. _sec-rendering-scene-custom:

Custom rendering pipeline in Python
------------------------------------

Expand Down

0 comments on commit 04f03a4

Please sign in to comment.