Can somebody explain how to implement volume rendering and scattering? I've been told that this is the easiest and most effective method to render things like smoke, milk and fog.

Scratchapixel has a great article about it here but the lesson on implementation is unavailable.

I understand what Ray Marching is because of this answer from a while back but similiar to my previous problem I have no idea how to implement it.

To make it clear and narrow this question down so it is easier to answer here are my main problems:

  1. Can someone provide some pseudo code or code of a basic Volume Rendering and Ray Marching loop with Absorption & Scattering coefficients, Phase Function and a Density function?
  2. How is Ray Marching used alongside Path Tracing?
  3. Is this the best way to render sub surface scattering?
  • $\begingroup$ Subsurface scattering won't necessarily require ray marching if it is in a homogeneous medium. Are you specifically looking to model inhomogeneous media (fire and smoke) rather than homogeneous media (milk or glass)? $\endgroup$ Commented Jun 11, 2017 at 0:25
  • $\begingroup$ Are you trying to do real time or offline rendering? SSS is a surface based algorithm that uses what's called a BSSRDF. Usually the implementation only accepts the data of a few layers past the surface. Usually just a few extra texture maps. Volume rendering can do the job of SSS but it models the entire inside of a volume. Often it does this with a 3d texture (uniform grid). But sometimes it's done with other datasets like tetrahedral grids. TLDR: Volume rendering isn't used for SSS although the concepts of emission, absorption, in/out scattering are the same. $\endgroup$ Commented Jun 16, 2017 at 23:47

2 Answers 2


For your first question, the basic process should be (simplified version):

// Step1: Ray intersection: calculate the maximum extent for the ray before hitting the surface
bool hit = ray_intersection(scene, ray);
if (!hit) {
    // check if the world itself contains scatter medium, if not then we return
/** Step2: distance sampling (free-path sampling).
 * For homogeneous scattering medium, it's easy to do with transmittance sampling
 * We sample a distance traveled before an scattering event
 * and evaluate transmittance
MediumInteraction mi;
Spectrum tr;
bool medium_event = ray.medium.distance_sample(scene, mi, tr);
if (medium_event) {
    // we are here, if the sampled distance is less than the distance to the closest surface
    // Step 3: phase function sampling to upate the ray direction,
    // since distance sampling already updates the origin of the ray
    ray = ray.medium.phase_sampling(mi);
    path_throughput *= tr;
} else {
    // Nothing to be said here, it's just basic surface rendering process
    // One thing to note is that, transmittance value should be evaluated
    // via ray marching (or some other algorithms)
// Okay, then we update our ray for the next intersection and scattering event. Loop continues.

There are some details hidden in this pseudo code:

  • How do we sample the free path in step 2? In Dartmouth distance-sampling, page 7, the sampling process is well explained: first find the bin we are in (since we are using fixed steps, it lefts us several bins in the space), and find the exact position in that bin (so, from discrete bins to continuous distance).
  • How to evaluate the transmittance? For homogeneous medium, it's simple and can be done analytically (if, the medium is exponential). For heterogeneous medium, when ray marching is used, we just subdivide the space and evaluate each part then multiply them together.
  • Absorption & scattering coefficient You will find them in most renders as sigma_a and sigma_s, they compose a extinction coefficient sigma_t. The transmittance of a certain distance is usually: $$ \exp(-\sigma_t d), \text{ where } d \text{ is the distance} $$

For the second question, I believe it has been answered: we sample the distance sample (distance traveled before a scattering event) according to the aggregated local transmittance, and we should be able to evaluate the transmittance (approximately) for any given path. So for non-uniform transmittance we can use ray marching here.

For the third question: No. Actually, for heterogeneous medium (not yet for SSS), some better ways are developed already: delta-tracking (woodcock tracking), residual tracking, etc. They are unbiased and efficient in multiple ways. For SSS, path tracing can have extremely poor converge in highly scattering medium (high sigma_s, low sigma_a) since the energy of the ray won't change much even we have hundreds of scattering events. This is decided by the sampling process, let's take a look at a homogeneous example:

$$ \text{estimator} = \sigma_s\frac{f(x)}{p(x)} = \sigma_s\frac{\exp(-\sigma_t d)}{\sigma_t\exp(-\sigma_t d)} = \frac{\sigma_s}{\sigma_s + \sigma_a} \approx 1 $$

So, a huge amount of scattering events need to be sampled (hence great max_depth) and the variance is pretty high. There are some interesting works on accelerating Monte Carlo SSS rendering, if you are interested:

  • Meng, Johannes, Johannes Hanika, and Carsten Dachsbacher. "Improving the Dwivedi sampling scheme." Computer Graphics Forum. Vol. 35. No. 4. 2016.
  • Křivánek, Jaroslav, and Eugene d'Eon. "A zero-variance-based sampling scheme for Monte Carlo subsurface scattering." ACM SIGGRAPH 2014 Talks. 2014. 1-1.

The ideas behind these works are basically: the ray will get lost in the highly scattering medium, so we should guide the ray out or to the emitter as quickly as possible.

I think for SSS, BSSRDF can already be very efficient.


If this is still of interest to some readers in the future), I would recommend that you look at the lesson on ray-marching / volume rendering for developers on scratchapixel.


It explains how ray-marching works and also gives info on how to render volumetric objects with varying volume density (including voxel grids). Hope it helps.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.