Simple Shadows
==============

The full source code for this part of the tutorial is available in the file
`root/tutorials/s3d/shadow.bs`. You can run it using `storm -f tutorials.s3d.shadow`. 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) (or the
[cel-shading](md:Cel-Shading)) part of the tutorial. As you will see, this part only modifies the
painter and adds another shader. To avoid name clashes, the painter is renamed from `LightPainter`
to `ShadowPainter`. Apart from that, this sample will reuse the vertex types and shaders from the
previous parts.


More Geometry
-------------

To make sure that we actually see the shadow, we start by adding a floor below the teapot and a
sphere that shows where the light source is located. We do this by adding a few models to the
painter (here called `ShadowPainter`):

```bsfragment:SFile:use=ui.s3d
class ShadowPainter extends SamplePainter3D {
    // Normal pipeline.
    LightPipeline pipeline;

    // Model to render (teapot).
    Model model;

    // Cube used as a floor.
    Model floor;

    // Sphere used to indicate the position of the light.
    Model cameraPos;

    // Create.
    init() {
        init {
            // Create the pipeline and attach shaders.
            pipeline = LightPipeline:create(lightVertexShader, lightPixelShader);

            // Create the default camera transform.
            cameraPos = rotateX(-35 deg) * translate(Vector(0, -3, 6));

            // Load a teapot to show.
            model = resUrl("teapot.obj").loadOBJ;
            // Create a cube and scale it as the floor. Also move it to
            // make the top edge slightly below 0.
            floor = Model:unitCube()
                .transform(scale(Vector(40, 0.1, 40)) * translate(Vector(0, -0.15, 0)));
            // Create a sphere that we place where the light is.
            light = Model:sphere(0.2, 20, 20);
        }

        // Set colors.
        model.color = red;
        floor.color = green;
        light.color = yellow;

        // Set ambient light strength.
        pipeline.ambientStrength = 0.1;

        // Set specular light properties.
        pipeline.specularStrength = 0.8;
        pipeline.specularExponent = 32.0;
        pipeline.specularColor = white;
    }
}
```

Note that we place the floor slightly below the coordinate y = 0. This is convenient, since it will
be natural to render the shadow at y = 0 later.

We also update the `render` function to actually draw the new geometry. To make the shadows a bit
more interesting, we also make the teapot move up and down a bit.

```bsfragment:SClassBody:use=ui.s3d
Bool render(Size size, Graphics3D g) : override {
    Float time = animationTime(20 s);

    // Clear the buffer.
    g.clearDepth();
    g.fill(black);

    // Set the camera matrix.
    pipeline.camera = cameraPos * perspectiveProject(100 deg, size.aspect, 0.1, 100);

    // Let the light rotate around the model in a big circle.
    Vector lightPos = Vector(0, 7, -7) * rotateY(360 deg * -time);
    pipeline.lightPos = lightPos;

    // Compute the camera position, for specular highlights.
    pipeline.cameraPos = Vector(0, 0, 0) * cameraPos.inverted;

    // Draw the light.
    // Note: We cheat a bit here. We use scale(-1) to flip the normals of the sphere
    // inwards. We do this since the light source is *inside* the sphere. As such,
    // this means that the light will be completely lit up. For this reason we bypass
    // the 'world' setter we created earlier.
    pipeline.worldPosition = translate(lightPos);
    pipeline.worldNormal = scale(-1);
    pipeline.render(g, light);

    // Draw the teapot. Make it move up and down slightly as well as rotate.
    Vector modelPos(0, 2 + sin(time * 3 * 360 deg), 0);
    pipeline.world = rotateY(360 deg * time * 4) * scale(0.5) * translate(modelPos);
    pipeline.render(g, model);

    // Draw the floor.
    pipeline.world = Transform();
    pipeline.render(g, floor);
}
```

This code also shows how we can render multiple sets of geometry (with different positions, for
example) using the same pipeline. At this point, we can run the program and we should see a rotating
teapot that moves up and down above a floor. From time to time the light source will also come into
view.

As a final step, let's render a shadow of a teapot. For this, we will simply project the geometry
onto the y=0 plane, rather than doing proper shadow mapping. As you will see, this gives a fairly
good-looking result, but the teapot will only cast a shadow onto the floor. For example, the
teapot's handle will not cast a shadow onto the teapot itself.

To render the shadow, we first need a different pixel shader that discards the color of the geometry
and replaces it with the color black:

```bsfragment:SFile:use=ui.s3d
pixel shader for LightPipeline: Output shadowPixelShader(LightIntermediate point) {
    Output(black);
}
```

Then, we need a new pipeline instance so that we can use the two sets of shaders for rendering. As
such, we add the following to the shader class:

```bs
class ShadowPainter extends SamplePainter3D {
    // Normal pipeline.
    LightPipeline pipeline;

    // Pipeline for rendering shadows.
    LightPipeline shadowPipeline;

    // ...

    // Create.
    init() {
        init {
            // Create the pipelines.
            pipeline = LightPipeline:create(lightVertexShader, lightPixelShader);
            shadowPipeline = LightPipeline:create(lightVertexShader, shadowPixelShader);

            // ...
        }

        // ...
    }

    // ...
}
```

Note that we don't have to set up all light parameters on `shadowPipeline`, since it will not do any
light computations - shadows are always rendered in black regardless.

Finally, we can use the shadow pipeline to render the teapot's shadow in the `render` function. It
is not too important exactly when we do this (unless we use transparency, which has other problems),
but it is convenient to do it right after rendering the teapot itself.

A key insight here is that we use the
[stormname:core.geometry.shadowProjectXZ(core.geometry.Vector)] function to create a matrix that
projects the entire teapot onto the XZ plane (i.e. where y = 0). This gives us a flat version of the
teapot that we render in black to simulate a shadow. This looks as follows:

```bsfragment:SClassBody:use=ui.s3d
Bool render(Size size, Graphics3D g) : override {
    // ...

    // Draw the teapot.
    // ...

    // Draw the shadow. Note that we need to bypass the 'world' setter here
    // since the projection matrix is not invertible. Calling 'world' would
    // trigger an assertion as 'forNormal' involves an inverse.
    shadowPipeline.world = pipeline.worldPosition * shadowProjectXZ(lightPos);
    shadowPipeline.render(g, model);

    // Draw the floor.
    // ...
}
```

Now, if we run the program, we will see that the teapot seems to cast a shadow onto the green floor.
