Cel-Shading
===========

The full source code for this part of the tutorial is available in the file
`root/tutorials/s3d/celshading.bs`. You can run it using `storm -f tutorials.s3d.celshading`. As in
the previous parts, the code is fairly well-documented, and it is likely possible to read it on its
own.

This tutorial builds upon the code from the [lighting part](md:Lighting) of the tutorial. As you
will see, this part of the tutorial only makes fairly small modifications to the code in the
previous step. To avoid name clashes, we will however rename the painter class and the pixel
shader. The rest of the tutorial reuses the code from the light tutorial directly.


Updates to the Painter
----------------------

As mentioned above, this part of the tutorial reuses a large part of the code from the previous step
(the [lighting tutorial](md:Lighting)). The provided file (`root/tutorials/s3d/celshading.bs`) does,
however, contain a copy of the `LightPainter` and the `lightPixelShader` (named `CelPainter` and
`celPixelShader` respectively). The `CelPainter` is the same, except that the pipeline is
initialized with `celPixelShader` instead of `lightPixelShader`. The interesting differences are in
the pixel shader.


Updating the Pixel Shader
-------------------------

This version of cel-shading (there are most likely other ones) simply splits the continuous output
of the light calculation into a few discrete steps. Since this is an operation we need to do a
couple of times, we create a shader function that does the discretization for us:

```bsfragment:SFile:use=ui.s3d
shader Float celShade(Float strength) {
    Float steps = 5.0;
    (strength * steps).floor / steps;
}
```

The function above assumes that the input value is in the range 0 to 1. To discretizise the steps,
the function then multiplies the input with the number of desired steps and then removes the decimal
part using the `floor` function. After that, it divides the resulting value with the number of steps
again to bring the value back into the range 0 to 1.

As in shader functions and Basic Storm, the value of the last statement in the function is returned
by default. You can of course use `return` as well if you want.


We can then use the `celShade` function to discertizise the scalars from the light computations as follows:

```bsfragment:SFile:use=ui.s3d
pixel shader for LightPipeline: Output celPixelShader(LightIntermediate point) {
	// Compute the direction vector to the light source and the normal. Note that we need to
	// normalize the normal at this point.
	Vector cameraDir = (cameraPos - point.world).normalized;
	Vector lightDir = (lightPos - point.world).normalized;
	Vector normal = point.normal.normalized;

	// Ambient light:
	Color light = point.color * ambientStrength;

	// Diffuse light, updated:
	light += point.color * celShade(max(normal * lightDir, 0.0));

	// Specular light:
	Vector reflectDir = lightDir.reflect(normal);
	Float specular = pow(max(cameraDir * reflectDir, 0.0), specularExponent);
    // Updated:
	light += specularColor * celShade(specularStrength * specular);

	Output(light);
}
```

And with these changes, we can run the program. At this time we will see a cel-shaded rotating teapot.
