## Diffuse reflection

I managed to include diffuse reflection in the fur plugin, and now the lighting model is similar to Phong shading and the specular highlights are done either using Kajiya and Kay or Marschner shading models.

Here is a video of the results:

## Marschner Shader Part III

This is the last part of the three post regarding the Marschner shader. I will explain how to efficiently make the shader for this model, how to add ambient and diffuse lighting and at the end of the post I will also give source code for generating Marschner lookup textures and a video showing the results I had in CS.

**Lookup Textures**

Because there are too many computations done in *M* and *N* functions to be put in the pixel shader, the best optimization is to use lookup textures, that need to be updated as rarely as possible.

We can easily observe that apart from the constants defined in Table 1 ( page 8 ) from Marschner’s paper, the *M* function only depends on *q* _{ i }and *q* _{r }, and *N *on* q* _{ d }and *f* _{ d}. Although this might seem a good optimization at first, taking into account that all these angles must be computed from inverse trigonometric functions, such as acos and asin, which aren’t fast at all, indexing the lookup textures directly by cos and sin sounds a better idea.

The way in which sinus and cosinus values can be computed for all these angles can be found in GPU Gems 2, Chapter 23:

- sin
*q*_{ i }= (*light*·*Tangent*), - sin
*q*_{ o }= (*eye*·*Tangent*). *lightPerp*=*light*– (*light*·*tangent*) x*tangent*,*eyePerp*=*eye*– (*eye*·*tangent*) x*tangent*.- cos
*f*_{ d }= (*eyePerp*·*lightPerp*) x ((*eyePerp*·*eyePerp*) x (*lightPerp*·*lightPerp*))^{-0.5}

As for the cos *q* _{d} if we observe that *q* _{ d} depends on *q* _{ i } and *q* _{r }then we figure out that we can use a channel from the lookup texture indexed by the sins of these two angles.

The easiest way to build these two textures is to make a lookup texture for *M*, having *M _{R, }*

*M*

_{TT, }*M*and cos

_{TRT}*q*

_{d, }and a lookup texture for

*N.*However, in the original paper

*N*and

_{TT }*N*each have three channels, but they can be reduced to only one channel if we consider the absorption to have one channel as well.

_{TRT }These are the lookup textures obtained with my first implementation of the Marschner project:

**Ambient and diffuse lighting**

The Marschner model only specifies the specular component for lighting, so in order to obtain nice visual effects, both ambient and diffuse lighting were added to this model.

I used the lighting from the Nalu Demo, presented in detail in one of my previous posts:

/* Compute diffuse lighting with phi-dependent component */ float diffuse = sqrt(max(0.0001, 1 - uv1.x * uv1.x)); /* Pass colors */ float4 diffuseColor; diffuseColor.rgb = diffuse * objColor.rgb * DiffuseCol; diffuseColor.a = objColor.a; float3 ambientColor; ambientColor = objColor.rgb * AmbientCol; float3 lighting = (( M.r * N.r + M.g * N.g + M.b * N.b ) / (cos_qd * cos_qd)); lighting += diffuseColor.rgb; OUT.xyz = lighting + diffuseColor.rgb * 0.2 + IN.AmbientColor;

**Source code**

Here you can find the first version of my Marschner C# Project, which generates the lookup textures needed for a shader similar to the one presented in the Nalu Demo post.

There are still some things that can be improved, but I plan to release another version for that, as soon as I get a chance. Until then feel free to improve the project yourself.

These are the two adjustments done to the original model, as described in Marschner:

- The absorption is specify by only one channel.
- Instead of the standard
*N*component, the simplify version was used._{TRT}

You can find more information in the README, INSTALL and LICENSE files from the archive.

**Demo**

Next you can see the effects this shader has on Krystal’s hair. If you want to play with the application yourself checkout the hair branch from CS main repository.

## Marschner Shader Part II

In my last post I mentioned two functions that are needed to represent the hair model as depicted in Marschner’s paper.

*S* = *S _{R} * +

*S*+

_{TT}*S*

_{TRT,}*S _{p} * =

*M*(

_{p}*q*

_{ i },

*q*

_{r }) x

*N*(

_{p}*q*

_{ d },

*f*

_{ d }) for

*P*=

*R*,

*TT*,

*TRT*.

**M component**

This is actually just a probability density function and the best choice here is to use a Gaussian distribution (or normal distribution).

And the M components are as follows:

*M*(_{R}*q*) =_{ h }*g*(*Beta*,_{ R }*q*–_{ h }*Alpha*)._{ R}*M*(_{TT}*q*) =_{ h }*g*(*Beta*,_{ TT }*q*–_{ h }*Alpha*)._{ TT}*M*(_{TRT}*q*) =_{ h }*g*(*Beta*,_{ TRT }*q*–_{ h }*Alpha*)._{ TRT}

**N component**

The N component is actually a bit tricky to compute. Here are all the main steps:

- Convert to Miller-Bravais index.

This is done in order to change the index of refraction to 2D physics, so that the optics of a 3D cylindrical fiber may be reduced to the 2D analysis of the optics of its cross-section.

After looking into Snell’s Law we define the indexes of refraction as:

- Solve a cubic equation

Remember this picture:

We need to find out who the incident angles are, and we can approximate the solution for this equation as:

- Solve Fresnel equation

Fresnel equation is used in order to simulate the reflection model from within the attenuation

- Find out the absorption factor

This is actually quite straightforward, just:

- The attenuation factor

This is obtain combining both the reflection and the absorption factor, hence the “Attenuation by absorption and reflection” model from Marschner’s paper.

where the first derivative is

- The N component (finally)

and the N are

*N*(_{R}*q*_{ d },*f*_{ d }) =_{ }*N*(0,_{P}*q*_{ d },*f*_{ d })._{ }*N*(_{TT}*q*_{ d },*f*_{ d }) =_{ }*N*(1,_{P}*q*_{ d },*f*_{ d })._{ }*N*(_{TRT}*q*_{ d },*f*_{ d )}=*N*(2,_{P}*q*_{ d },*f*_{ d })._{ }

For the last component Marschner proposes a more complex model in order to avoid singularities, but for my implementation I couldn’t tell any improvement so I stuck with the simpler version of *N _{TRT.
}*

**The whole model**

As a sum up this is the whole Marschner hair model in just an equation:

Hope I managed to keep everything simple and explicit alike.

## Marschner Shader Part I

I decided to write a trilogy (3 posts) explaining, as best as I can, what is discussed in Marschner’s paper “Light Scattering from Human Hair Fibers“.

First of all, I have to warn you that in order to understand this paper you must have some physics and math background, rather than knowing a lot about shaders, things such as Snell’s law or probability density functions being mentioned quite often.

The main advantage of the model proposed by Marschner is that it is based on the actual physical phenomenon that occurs when light passes through hair fibers. So by studying electron micrograph of hair fibers such as this one:

a model has been proposed, where each individual hair fiber is treated as a translucent cylinder, having the following components:

and the components that contribute to a distinct and visually significant aspect of hair reflectance are *R*, *TT *and *TRT*.

*R*– light that bounces off of the surface of the hair fiber toward the viewer.*TT*– light that refracts into the hair and refracts out again toward the viewer.*TRT*– light that refracts into the hair fiber, reflects off of the inside surface, and refracts out again toward the viewer.

The notation used throughout this paper is in tangent space, for the light and viewer position, reported to the current hair fiber.

These are all the variable inputs that are needed for Marschner hair shading model:

*u*– tangent to the hair, pointing in the direction from the root toward the tip.*w*– normal to the hair, pointing toward the viewer (the geometry faces the camera).*v*– binormal to the hair, pointing such that*v*and*w*complete a right-handed orthonormal basis, and are the*v*–*w*is the normal plane.*w*_{i }*w*– direction of camera (viewer)._{ r}*q*– inclinations with respect to the normal plane (measured so that 0 is perpendicular to the hair,_{i ,r }*PI*is*u*, and –*PI*is –*u*).*f*– azimuths around the hair (measured so that_{i ,r }*v*is 0 and*w*is +*PI*).

Several derived angles are used, as well:

*q*=_{d }*q*–_{r }*q*)/2_{i }*;*– the difference angle.*f*=*f*–_{r }*f*); – the relative azimuth_{i }*q*=_{ h }*q*+_{i }*q*)/2; – half angle_{r }*f*= (_{ h }*f*+_{i }*f*)/2; – half angle_{r }

Also, there are some constants parameters for hair fibers, surface and glints that you can find in Table 1 ( page 8 ) from Marschner’s paper.

Having all of this in mind we can approximate the hair model as:

*S* = *S _{R} * +

*S*+

_{TT}*S*

_{TRT,}*S _{p} * =

*M*(

_{p}*q*

_{ i },

*q*

_{r }) x

*N*(

_{p}*q*

_{ d },

*f*

_{ d }) for

*P*=

*R*,

*TT*,

*TRT*.

So it turns out the only thing we need is to find out who M and N are. My next post will do just that.