View-dependent Hatch Shading in Unreal Engine



Hatch shading is a term that describes creating a hash-like texture. There are several ways to go about this, however, this blog shows one of the simplest ways using a number of textures. I will also be showing only one variant of the shading, based on the angle between the camera direction and normal of the surface. There are several other ways, containing both model and post-process materials, that I'll be describing in further blogs. It's important to note, that this material is used on per-model basis.

Idea

The view-dependent hatch shading is inspired by Blinn-Phong shading model that is used to compute specular reflection (see Wiki). It computes the cosine of the angle (via dot product) between the camera view direction and the surface normal. Then, based on the value of the cosine, we decide which textures we will be using and interpolate between them to get smooth transition.

Code

The material itself requires a number of inputs. In this case, it's 4 textures for hatching, and 3 scalar values to determine where each of the hatching textures start and ends. The majority of the computation is done via Custom Node for which I create a separate function just for clarity. 

Let's start with all the parameters we will can later adjust in Material Instance.


We have Hatch0, Hatch1, Hatch2, and Hatch3 that represent texture parameters or textures that we will be interpolating between. Hatch0 End, Hatch1 End, and Hatch2 End are scalar parameters (aka numbers) that are used to tweak the look of the result. For example, Hatch0 End equal to 0.1 means, that only the parts to which camera is looking under small angle will use Hatch0 texture. Note that view angle will always be between 0.0 and 1.0, so it's best you keep your parameter in the same range.

Next off, we need to get the last two parameters we feed into the material function, normal direction in given vertex and current camera view direction. We can do this as follows:


Note that we get the normals from the normal texture (in this case, it's also a parameter so that we can use it for different models), as this is a common way to change normals in computer graphics. If you however don't have a normal texture, you can always use a blue texture (RGB values (0,0,1)), or directly plug vector (0,0,1) into the Normalize node.

Next, we create a material function (in my case I named it MF_Hatch). The function itself will look as follows


where Hatch is a custom expression node. Type this code into the code field within the custom expression

  float3 res;
  float t;
  float3 minHatch;
  float3 maxHatch;

  //COMPUTE VIEW ANGLE
  float3 normView = normalize(ViewDirection);
  float3 normNorm = normalize(Normal);
  float view = 1.0 - dot(normView, normNorm);
  view = clamp(0.0, 1.0, view);

  //DECIDE WHICH TEXTURE TO USE AND HOW TO INTERPOLATE IT
  if(view <= Hatch0Ends){

    t = 0;
    minHatch = Hatch0;
    maxHatch = Hatch0;
  }else if (view <= Hatch1Ends){

    t = (view - Hatch0Ends) / (Hatch1Ends - Hatch0Ends);
    minHatch = Hatch0;
    maxHatch = Hatch1;
  }else if(view <= Hatch2Ends){

    t = (view - Hatch1Ends) / (Hatch2Ends - Hatch1Ends);
    minHatch = Hatch1;
    maxHatch = Hatch2;
  }else{

     t = (view - Hatch2Ends) / (1.0 - Hatch2Ends);
    minHatch = Hatch2;
    maxHatch = Hatch3;
  }

  res = lerp(minHatch, maxHatch, t);

  return res;

It is possible to write this code without all if statements (for those who are not big fans of using them withing shaders), however, I opted for this version for better comprehension.

Finally, we plug all the parameters into the material function


Results

Following picture shows material used on default third person character.


And just for the completion, here's the effect in motion.


Notes

- This effect pairs well with the Sobel edge detector from previous blog

References

 Blinn-Phong model - information on Blinn-Phong model that is used as an inspiration for the computation of this effect.

Comments

Popular Posts