Volumetric Rendering: Surface Shading

in shader, tutorial, Unity3D

Volumetric Rendering: Surface Shading

Share Button

This third instalment on Volumetric Rendering will explain how to shade volumes in a realistic fashion. This essential step is what gives threedimensionality to the flat, unlit shapes that have been generated so far with raymarching.

geom1

You can find here all the other posts in this series:

Introduction

The previous part of this tutorial on volumetric rendering, Volumetric Rendering, uses the raymarching technique to draw a spheres within a cube:

rotate

This solution is only able to tell if the rays projected from the camera within the volume are hitting the virtual sphere. We have no information about its position or orientation. Consequently, we can only provide an outline. The result is an unlit, flat sphere which hardly looks any different from a circle.

Lambertian Reflectance

If we want to bring depth to volumetric rendering, we need a way to shade arbitrary geometries. In a previous tutorial, Physically Based Rendering and Lighting Models, we have seen how the shading for 3D object is calculated in Unity 4. The technique relies on the Lambertian reflectance, which provides a simple – yet effective – model to simulate how light behaves on 3D surfaces. The amount of light reflected by a Lambertian surface depends on the surface orientation (its normal direction) and on the light direction.

Image_4850_03_03

We have previously seen this in a function called LightingSimpleLambert; for the purpose of this tutorial, we can rewrite it like this:

The function takes the surface normal as an input; all other parameters are retrieved via the built-in variables that Unity provides to the shader (you can find the full list here). The one line that actually computes the Lambertian reflectance is highlighted.

Normal Estimation

The main idea behind this tutorial is to adopt the Lambertian reflectance to the virtual geometries that are drawn inside the cube. The lighting model chosen does not depend on the distance from the lighting source, but requires the normal direction of the surface point we are rendering.

This is not a trivial task, since the distance function used for the sphere encodes no such information. In his comprehensive guide to volume rendering (here), code artist Íñigo Quílez, suggests a technique to estimate the normal direction. His approach is to sample the distance field at nearby points, to get an estimation of the local surface curvature. If you are familiar with gradient descent, this is the gradient estimation step:

The difference on the X axis is calculate by evaluating the distance field on the left and on the right of the point. We can replicate this for all the Y and Z axes, and normalise it into a unit vector:

This normal estimation introduces a new parameter, eps, which represents the distance used to calculate the surface gradient. The assumption of this technique is that the surface we are shading is relatively smooth. The gradient of discontinuous surfaces won’t correctly approximate the normal direction of the point to shade.

The Shading

The raymarching code we have so far only account for a hit or a miss. We now want to return the actual colour of hit point on the volumetric surface:

The function to render the surface will calculate the normal and feed it into a Lambertian lighting model:

Those simple modifications are already enough to create very realistic effects:

shade1

The advantage of this shading is that it reacts to the lighting in your scene. The model provided is very simple, but you can add more details by using a more sophisticated lighting technique.

Specular Reflections

If we want to go the extra mile, we can also implement specular reflections on the surfaces. Once again, we can refer to the Blinn-Phong lighting model from Physically Based Rendering and Lighting Models, and change  simpleLambert accordingly:

BlinnPhong

The variable _SpeculerPower controls the size or spread of the specular reflections, while _Gloss indicates how strong they are. To better appreciate the result, we need use a more interesting piece of geometry. To highlight the difference, only the right half uses specular reflection:

v10

Conclusion

This post has shown how to simulate realistic lighting on the volumetric shape created with a distance-aided raymarching shader. Both the Lambertian reflectance and the Blinn-Phong lighting model have been used to shade objects realistically. Both these techniques shipped as the state-of-the-art real time lighting models in Unity 4. Nothing prevents you from exploring this concept further, by implementing your own model.

The next instalment in this series will teach you how to create and combine geometrical primitives to create whichever shape you want.

Other Resources

📧 Stay updated

A new tutorial is released every week.

💖 Support this blog

This websites exists thanks to the contribution of patrons on Patreon. If you think these posts have either helped or inspired you, please consider supporting this blog.

Paypal
Twitter_logo

Write a Comment

Comment

18 Comments

  1. Maybe I’m just being a noob, but Unity is saying that map(float3) is not a function and I can’t find anything on it online. What am I missing? Thanks! I love the tutorials!

    • Hey! That function is the one that defines the distance from the surface of the sphere!
      You can use the one defined in the first tutorial:

      float map (float3 p)
      {
      return distance(p,_Centre) – _Radius;
      }

      • I don’t think you ever defined it in any of the tutorials here, so maybe you should add it? It was definitely confusing me while reading. And maybe name it something more like “DistanceToSphere” or whatever.

        Either way, I’m enjoying the tutorials!

      • I confirm why Brian said, “map()” is actually “SphereDistance()” that is presented in part 2. You should rename “map” therefore, because it’s very confusing. I spent a few minutes looking for this function in the Unity doc.

        Thanks for the tutorial. 🙂

  2. I get a lighting glitch that I can’t figure out. Moving the camera seems to make the lighting randomly flip, coming from opposite direction. Its not affected by FOV, or the eps variable or any step variables :O

  3. Having trouble getting the specular lighting to work given the shader file reads from bottom to top. Since viewDirection is declared in the fragment function (at the end of the shader) I can’t get it any earlier in the script. Is there a right way to write this that I’m missing?

  4. How do i render the simpleLambert its not showing up its just white? What do i have to do to make it work i tried using uniform (…) ; for _LightColor0 and _WorldSpaceLightPos0 but it does not seem to work, do i fix this?

    • Hey! It is very hard to know what’s wrong without seeing your setup!
      The pieces of code in this tutorial alone won’t compile. You’ll need to add these new snippets of code to the shader from Part 1 and 2 of this tutorial!

  5. It might be better to integrate the volumetric rendering into a surface shader, because it only needs to output albedo and normal, and Unity’s built-in lighting system does the rest (I haven’t worked in Unity for a while so I might be wrong).

  6. To get proper light direction depending on the type of light in Unity (Directional, Point, Spot) I am putting this line into the lambert shading function:

    lightDir *= (_WorldSpaceLightPos0.w – 0.5) * 2.0;

Webmentions

  • Volumetric Rendering: Signed Distance Functions - Alan Zucconi August 2, 2017

    Hey!
    I have not tried it, might have a look inside that.

  • Issue z – Unity Dev Weekly August 2, 2017

    Hey!
    I have not tried it, might have a look inside that.

  • Issue 3 – Unity Dev Weekly August 2, 2017

    Hey!
    I have not tried it, might have a look inside that.

  • Volumetric Rendering: Raymarching - Alan Zucconi August 2, 2017

    Hey!
    I have not tried it, might have a look inside that.

  • Volumetric Rendering - Alan Zucconi August 2, 2017

    Hey!
    I have not tried it, might have a look inside that.