Archive for April, 2009

CMake module for PyGObject

Wednesday, April 29th, 2009

I’m working on writing an internal library at work for accessing one of the (slightly crazy) file formats we work with here. It is being ported over to being GObject/GType aware (away from vanilla C) in order to make writing bindings to higher-level languages a little less painful.

The build system is based on CMake but the PyGTK FAQ entry on extending PyGTK (apparently one of the few up to date sources of information on writing GObject bindings) only has eyes for automake.

I’ve created a simple CMake module[1] which wraps up some of the nastier magic involved in writing bindings. Here is an example of it in use for the hypothetical libfoo.

# Attempt to find pygobject
find_package(PyGObject)

# Use CMake magic to find GLIB_... and GOBJECT_...
# (I would suggest pkg-config)

if(NOT PYGOBJECT_FOUND)
    message(STATUS "PyGObject could not be found, Python bindings will not be built.")
else(NOT PYGOBJECT_FOUND)
    pygobject_target_add_bindings(FOOBINDINGS foo 
        MODULEPREFIX foo
        HEADERS path/to/libfoo/foo.h 
        OVERRIDE foo.override)
    include_directories(
        ${GLIB_INCLUDE_DIRS} ${GOBJECT_INCLUDE_DIRS}
        ${FOOBINDINGS_INCLUDE_DIRS} ${PYGOBJECT_PYGTK_INCLUDE_DIRS})
    add_library(foo MODULE foomodule.c ${FOOBINDINGS_SOURCES})
    link_directories(
        ${GLIB_LIBRARY_DIRS} ${GOBJECT_LIBRARY_DIRS}
        ${FOOBINDINGS_LINK_DIRS})
    target_link_libraries(foo libfoo ${SVFBINDINGS_LIBRARIES})
    set_target_properties(foo PROPERTIES PREFIX "")
endif(NOT PYGOBJECT_FOUND)

[1] To the extent available under any applicable copyright laws, I release this into the public domain.

Channel 4 On Demand

Monday, April 20th, 2009

This was unexpected. Channel 4 on demand appears to work under Linux (modulo Flash). Now, why do the adverts on Channel 4 only say ‘now supports Macs too’. Grr.

Twitter Weekly Updates for 2009-04-12

Sunday, April 12th, 2009
  • Behaving like a little girl by trying on clothes that haven’t fitted for years and giggling. #
  • @jennielees only two orders of magnitude behind you in followers then :). in reply to jennielees #
  • Writing GUIs in Matlab. An exercise in pain. #

Yet more denoising

Wednesday, April 8th, 2009

This may well be becoming boring to many. Sorry but then I do tend to obsess over new toys :). An interesting thing about my denoiser is that it is now finding (maths) bugs in my path tracer. I was thinking that the under-arch noise wasn’t being removed like it should’ve been. In fact, I was introducing bias into my bidirectional path-tracing (which will be another post). I’ve made another video of the denoiser.

The video is available in OGG (9.7MiB), Windows Media (10MiB) or a high-quality h.264/MPEG 4 (20MiB). What you are seeing: The (noisy) input is on the left, the denoiser output is on the right. The iteration count (~samples/pixel) is at the top-left. Things to look for: under the arches the denoiser now fills in a lot of detail quickly. Also, as the sample count gets to around 200, really fine details start appearing in the stone texture under the arches and around the pillars.

Denoising redux

Monday, April 6th, 2009

I’ve just finished doing a little more on the denoiser and have made some nice animations. The movie shows the denoiser’s performance over ~400 iterations of the path tracer. Before the movie though, a side-by-side comparison after 400 iterations:

A comparison of path tracer output and denoiser output.

Things to note when watching: The floor fills in relatively rapidly. You can see fine texture detail just appearing on the underside of the arches at the end of the video.

In the video, the middle picture is output, left is Willett denoising and right is my algorithm. The video is available in Ogg/Theora (3.8MiB), Windows Media (4.2MiB), Motion JPEG (9.7MiB) and High-quality MJPEG (22MiB). The high-quality version is recommended if your bandwidth is manly enough.

Twitter Weekly Updates for 2009-04-05

Sunday, April 5th, 2009
  • Down one size in trousers. Win. #
  • Drinking coffee (typical Tweet, eh?). Oh and my leg hurts. #
  • http://filecoreinuse.livejournal.com/191617.html <- Some thoughts on sampling BRDFs. #
  • Teenage Mutant Ninja Turtles! #
  • Installed Jaunty on my Macbook. Sound now works! #
  • Some damn fool left all the Allo Allo in the house on DVD. Yay. #

Vapourware 2.0

Saturday, April 4th, 2009

This is a bit of a pre-anouncement. As you no doubt have noticed, I’ve been thinking about BRDF sampling and, as will be outlined in a future post, how to effectively do bidirectional path tracing in an unbiased manner. Why?, you may ask. I am interested in whether an understanding of the statistics of stochastic rendering can help with removing the characteristic ’sampling noise’ present in rendered images. As an example, look at the following figure. Those reading this on Facebook will probably need to follow the link to the full size image.

Theirs, original, ours

a) Willett’s TI tree-pruning approach, b) the original input image and c) my approach.

Above is the output from my unbiased path tracer and on the left is the output from a state of the art canned denoising algorithm from Willett. Much of the literature on denoising rendered images relies on bilateral filtering as an image based approach. The advantage of Willett’s approach is that it doesn’t require a ’smoothing’ parameter to be inferred and works instead directly on the per-pixel sample count.

The disadvantage is, however, obvious: fine texture detail is lost or blurred severely. This is not the fault of Willett’s algorithm: it requires that the input images have certain statistics which are not exhibited by the images my renderer produces. I have tried to ‘help’ the Willett algorithm along by rendering a high sample count image so the sampling noise is relatively low.

The blurring you see, especially on the floor, is due to the ‘unbiased’ renderer not being truly ‘unbiased’ in that although light transport is modelled correctly, a) surface colour is modelled by multiplying colours along the light path and b) in a bidirectional renderer one ‘merges’ light and eye paths not stochastically but by directly connecting nodes on the path and calculating an explicit BRDF to ‘join’ the lights to the eye.

My approach is on the right. As you can see, the floor detail, etc is preserved. The algorithm is in essence the same as Willett’s except that I explicitly acknowledge the multiplicative bias in my renderer and, by careful consideration of the sample statistics, I estimate it and remove it before the denoising algorithm is run.

In summary, this post makes the (somewhat obvious) statement that in a path tracing renderer one must be extremely careful to track sources of bias. Once you accept that the renderer is only quasi-unbiased, you can move on and ‘deal’ as our North American cousins would say.

Some notes on sampling BRDFs

Friday, April 3rd, 2009

These notes are the product of some fiddling earlier today on how to use BRDFs to do stochastic ray-tracing. I’m putting them up here as a Google target because I couldn’t find any similar treatments on the web. I hope they’re useful to someone!

The BRDF is the ratio of reflected radiance to incident irradiance at a particular wavelength. Radiance is a measure of energy (proportional to photon count for a particular wavelength) and irradiance is radiance scaled by projective area.

Letting $L_{i,r}(\theta_{i,r}, \phi_{i,r})$ represent the incident and reflected radiance, then the BRDF is given by:

\begin{displaymath} f(\theta_i, \phi_i, \theta_r, \phi_r) = L_r(\theta_r, \phi_r) / ( L_i(\theta_i, \phi_i) \cos \theta_i ), \end{displaymath}

where the incident irradiance is

\begin{displaymath} E_i(\theta_i, \phi_i) = L_i(\theta_i, \phi_i) \cos \theta_i. \end{displaymath}

From this we can find an estimate for the incident and reflected radiance contribution from a single direction:

\begin{displaymath} L_r(\theta_r, \phi_r; \theta_i, \phi_i) = E_i(\theta_i, \phi_i) f(\theta_i, \phi_i, \theta_r, \phi_r), \end{displaymath}
\begin{displaymath} E_i(\theta_i, \phi_i; \theta_r, \phi_r) = L_r(\theta_r, \phi_r) / f(\theta_i, \phi_i, \theta_r, \phi_r). \end{displaymath}

For the moment, consider two PDFs over the incident and reflected directions:

\begin{displaymath} P_r(\theta_r, \phi_r; \theta_i, \phi_i) = (1/N_r(\theta_i, \phi_i)) f(\theta_i, \phi_i, \theta_r, \phi_r), \end{displaymath}
\begin{displaymath} P_i(\theta_i, \phi_i; \theta_r, \phi_r) = (1/N_i(\theta_r, \phi_r)) f(\theta_i, \phi_i, \theta_r, \phi_r) \cos \theta_i. \end{displaymath}

The normalisation functions, $N_{i,r}(.)$ are:

\begin{displaymath} N_r(\theta_i, \phi_i) = \int_{-\pi}^{+\pi} \int_0^{\pi/2} ... ...phi_i, \theta_r, \phi_r) \sin\theta_r d\theta_r d\phi_r, \end{displaymath}
\begin{displaymath} N_i(\theta_r, \phi_r) = \int_{-\pi}^{+\pi} \int_0^{\pi/2} ... ...r, \phi_r) \cos \theta_i \sin\theta_i d\theta_i d\phi_i. \end{displaymath}

Suppose we wish to model light transport by sampling from $P_i(.)$ or $P_r(.)$. For an eye ray, we with to estimate the total reflected radiance at a point:

\begin{displaymath} R(\theta_r, \phi_r) = \int_{-\pi}^{+\pi} \int_0^{\pi/2} L_... ...phi_r; \theta_i, \phi_i) \sin\theta_i d\theta_i d\phi_i. \end{displaymath}

We can re-express $R(.)$ via $L_i(.)$ and $P_i(.)$:

\begin{displaymath} R(\theta_r, \phi_r) = \int_{-\pi}^{+\pi} \int_0^{\pi/2} L_... ...phi_i; \theta_r, \phi_r) \sin\theta_i d\theta_i d\phi_i. \end{displaymath}

We can estimate this integral via importance sampling. That is to say if we draw $(\theta_i, \phi_i)$ from $P_i(.)$,

\begin{displaymath} R(\theta_r, \phi_r) \approx \left< L_i(\theta_r, \phi_r; \theta_i, \phi_i) N_i(\theta_r, \phi_r) \right> \end{displaymath}

where $\left< . \right>$ denotes the sample mean.

Now consider tracing a light ray. We interpret the ratio

\begin{displaymath} L_r(.) / L_i(.) = \cos \theta_i f(\theta_i, \phi_i, \theta_r, \phi_r) \end{displaymath}

as a relative likelihood of a ray incident at $(\theta_i, \phi_i)$ being reflected at $(\theta_r, \phi_r)$. For a given incidence angle, $\cos \theta_i$ is constant and we can therefore interpret $P_r(.)$ as a PDF governing the ray transport.

Given an incident ray we sample an outgoing direction from $P_r(.)$ and propagate the ray along it. We must however normalise the reflected radiance by the probability of having sampled that reflection direction. Specifically we multiply the incident luminance by the sample weight $A(.) = L_r(.) / (L_i(.) P_r(.))$ or

\begin{displaymath} A(\theta_i, \phi_i, \theta_r, \phi_r) = \cos \theta_i f(\the... ...i, \theta_r, \phi_r) / P_r(\theta_r, \phi_r; \theta_i, \phi_i) \end{displaymath}

which, by definition, can be simplified to

\begin{displaymath} A(\theta_i, \phi_i) = N_r(\theta_i, \phi_i) \cos \theta_i. \end{displaymath}

We call this multiplicative factor the attenuation.

For a Lambertian diffuse BRDF, $f(.) = k$ is constant and so $N_i(.) = \pi k$. Thus, if we draw $(\theta_i, \phi_i)$ from $P_i(.) \propto \cos \theta_i$, we have

\begin{displaymath} R(\theta_r, \phi_r) \approx \pi k \left< L_i(\theta_r, \phi_r; \theta_i, \phi_i) \right>. \end{displaymath}

Similarly, $N_r(.) = 2\pi k$, therefore $A(.) = 2 \pi k \cos \theta_i$ with uniform $P_r(.) = 1/(2\pi)$.

For a physically based BRDF, it must satisfy two conditions. Firstly it mus satisfy Helmholtz reciprocity:

\begin{displaymath} f(\theta_i, \phi_i, \theta_r, \phi_r) = f(\theta_r, \phi_r, \theta_i, \phi_i). \end{displaymath}

Secondly, it must conserve energy:

\begin{displaymath} \forall\, (\theta_i, \phi_i), \quad \int_{-\pi}^{+\pi} \int_... ...i_r) \cos \theta_r \, \sin\theta_r \, d\theta_r d\phi_r \le 1. \end{displaymath}

For a Lambertian BRDF, this implies that $k \le 1/\pi$.