# Simple Rendering

The full source code for this part of the tutorial is available in the file `root/tutorials/s3d/simple.bs`. You can run it using `storm -f tutorials.s3d.simple`. The code itself is fairly well-documented, so it is possible to read it and understand what is happening. The rest of this page gives an overview of why all parts are needed, and the role they have in the complete rendering pipeline.

If you want to follow along, you can create a `.bs` file in the current working directory and run it on the command line (`storm <file>.bs`) as long as you use the name `main` for the function that is named `simple` below. You also need to add `use tutorials:s3d` to the use statements at the start of your file.

## Geometry

The goal of this example is to render a multi-colored cube on the screen. The first step in this process is to define a vertex type to store the cube itself. To do this, we define a new vertex type called `SimpleVertex`. For this example, we only need a position and a color for each vertex.

    use ui;
    use ui:s3d;
    use core:geometry;
    use graphics;
    // Needed if you create your own file, for SamplePainter3D later on.
    // use tutorials:s3d;

    vertex SimpleVertex {
        Vector pos;
        Color color;
    }

As can be seen above, the 3D library allows using normal types from the `core:geometry` and `graphics` libraries to do this. As we will se later, we can even use these types in shaders.

Once we have a vertex type, we can define the geometry and store it inside a `VertexBuffer` and an `IndexBuffer`. These will eventually end up in our `Painter3D` class, so we will create them there right away.

    class SimplePainter extends SimplePainter3D {
        // Vertex buffer, containing the vertices of the cube.
        VertexBuffer<SimpleVertex> vertices;

        // Index buffer. Lets us define faces without duplicating vertices.
        IndexBuffer indices;

        // The constructor.
        init() {
            init {}

            // Create a cube. First, create one vertex for each corner.
            vertices << SimpleVertex(Vector(-1, 1, 1), red);
            vertices << SimpleVertex(Vector(1, 1, 1), green);
            vertices << SimpleVertex(Vector(-1, -1, 1), blue);
            vertices << SimpleVertex(Vector(1, -1, 1), yellow);
            vertices << SimpleVertex(Vector(-1, 1, -1), red);
            vertices << SimpleVertex(Vector(1, 1, -1), green);
            vertices << SimpleVertex(Vector(-1, -1, -1), blue);
            vertices << SimpleVertex(Vector(1, -1, -1), yellow);

            // Then connect them using the index buffer.

            // Back face:
            indices << 0 << 2 << 1;
            indices << 1 << 2 << 3;

            // Top face:
            indices << 0 << 1 << 4;
            indices << 1 << 5 << 4;

            // Bottom face:
            indices << 2 << 6 << 3;
            indices << 3 << 6 << 7;

            // Left face:
            indices << 0 << 4 << 2;
            indices << 4 << 6 << 2;

            // Right face:
            indices << 1 << 3 << 5;
            indices << 5 << 3 << 7;

            // Front face:
            indices << 4 << 5 << 6;
            indices << 5 << 7 << 6;
        }
    }

Above, we first add one vertex for each corner of the cube (using the automatically generated constructor). We also give each corner an arbitrary color. After that we populate the index buffer with indices into the vertex buffer. This way we can use the same vertex as a part of more than one triangle, without duplicating the vertices.

## Basic Rendering

To render things, we can overload the `render` function in our painter. Much like rendering 2D graphics, the `render` function is called by the UI library to render new frames. The return value from the `render` function determines whether the UI library will render frames continuously (e.g. for an animation) or not. We can try this out by overriding the `render` function and create a window that uses our painter using the `runSample` function in the `common.bs` file.

    class SimplePainter extends SimplePainter3D {
        // ...

        // Render a frame.
        Bool render(Size size, Graphics3D g) : override {
            print("Render a frame!");
            false; // no need for more frames
        }
    }

    // Note: call this function 'main' if you are writing code in a separate file.
    void simple() {
        runSample("Simple 3D", SimplePainter());
    }

If we run the program at this point, we will se a blank window and a the text `Render a frame!` printed to standard output whenever the window needs to be repainted.

As with 2D rendering, we need to use the `ui.Graphics3D` instance to actually render things. The `Graphics3D` class has a few member functions that are useful to call at the start of rendering, such as `clearDepth` to clear the depth buffer, and `fill` to fill the entire screen with a background color. If we call them in our `render` function we can see control the background color of the window.

    Bool render(Size size, Graphics3D g) : override {
        // Clear the depth buffer.
        g.clearDepth();

        // Set the background color to something interesting!
        g.fill(green);

        false; // No need for animations.
    }

However, if you inspect the members of the `ui.Graphics3D` object (e.g. here), you will see that it does not provide any way to draw vertices. To achieve that, we need to define a pipeline, a few additional vertex types, and two shaders.

## Pipelines and Intermediate Vertex Types

We start with the pipeline. A pipeline describes how data flows through the rendering pipeline: what data is unique for each vertex, what data is common between all vertices, how should this data be interpolated across the rendered triangles, and so on.

In this case, we want to render a set of `SimpleVertex` vertices in a vertex buffer (along with an index buffer, but that is not too important at this time). However, we also need to transform them from model space into screen space. In essence, we want to apply a transform to them. As such, we can define our pipeline like below:

    pipeline SimplePipeline {
        // The flow of data through the pipeline:
        // (input parameters) -> vertex shader output -> pixel shader output
        data (SimpleVertex point) -> SimpleIntermediate -> SimpleOutput;

        // Additional members become uniforms automatically, but are set globally rather than passed to
        // the function.
        Transform camera;
    }

The pipeline we defined is similar in structure to a class. Any variables (e.g. `camera`) will become members of the class. What is special for a pipeline is that these will also become available to shaders in the pipeline. They are often called uniforms since they have the same value for all vertices.

Another difference is that a pipeline needs a `data` statement. This specifies how the pipeline operates. One can think of a pipeline as a function that transforms data on the GPU. What makes it a bit special is that it parallelizes its work on the GPU by operating on an array of its inputs in parallel (this is one part of why GPUs are fast - the programming model makes it almost trivial to parallellize the execution!). In this case, the pipeline accepts an array of `SimpleVertex` as an input. The first stage is the vertex shader, and in this case the vertex shader will transform each `SimpleVertex` into a `SimpleIntermediate` vertex. After that, the rendering hardware will interpolate the `SimpleIntermediate` vertex type across all triangles, and execute the pixel shader for each pixel. The pixel shader therefore accepts `SimpleIntermediate` as an input and outputs a `SimpleOutput`, which the rendering hardware then uses to determine the final color of that pixel on the screen.

The next step is therefore to define `SimpleIntermediate` and `SimpleOutput`. Since the rendering hardware will use the information in `SimpleIntermediate` to construct and render a triangle on the screen, it needs to contain at least a screen-space coordinate. Apart from that, we want it to contain the color of the rendered triangle. As such, we can define it as follows:

    vertex SimpleIntermediate {
        // Screen position.
        Vector4 pos : position, perspective;

        // Vertex color.
        Color color : perspective;
    }

Note that we need to annotate at least some of the members of `SimpleIntermediate`. To let the rendering hardware know which element is the screen-space position, we need to mark one member as `position`. We may also specify how each vertex compontent should be interpolated across the 3D geometry. By default, perspective correct interpolation is used (i.e. the `perspective` keyword). As such, we don't need to add `perspective` on the members. We chose to do it for clarity here. Other interpolation options are `linear` for a linear interpolation, and `none` for no interpolation (it simply uses the value from the first vertex).

It is worth noting that the position needs to be of the type `Vector4`, since the fourth component (`w`) is used to perform perspective correct interpolation, for example.

The situation is similar for the `SimpleOutput` vertex type. It needs to contain at least one element marked `color` or `output` (they are synonyms) to let the rendering hardware know which member should be used for the screen color. As such, we can define it as follows:

    vertex SimpleOutput {
        // Output color.
        Color color : color;
    }

## Shaders

Now that we have defined a pipeline, we need to describe how to transform data between the different vertex types we just defined. We do this using shaders, which are essentially small programs that execute (often in parallel) on the GPU.

We need two shaders: a vertex shader and a pixel shader. The vertex shader is executed once for each vertex in the vertex buffer. As we have defined our pipeline, its job is to convert a `SimpleVertex` into a `SimpleIntermediate` vertex. As a part of this transform, it should transform the position of the vertex from model space to world space. The pixel shader then receives a `SimpleIntermediate` and determines what color the corresponding pixel should have.

In the 3D library, we can implement the shaders in a stripped-down version of Basic Storm. That way Storm is able to type-check the shader before it executes. It also cross-compiles the shader into a suitable shader language based on your current rendering backend (e.g. GLSL or HLSL).

We start by implementing the vertex shader as follows:

    vertex shader for SimplePipeline: SimpleIntermediate simpleVertexShader(SimpleVertex point) {
        // Note: we can use the 'camera' uniform here.
        Vector4 pos = Vector4(point.pos) * camera;

        // Note: 'return' is optional, just as in Basic Storm.
        return SimpleIntermediate(pos, point.color);
    }

As can be seen above, pixel and vertex shaders are tied to a specific pipeline. This makes it possible to use uniforms defined in the pipeline. In this case, the `camera` transform. The vertex shader is quite simple in this case. The first line inside the shader casts the `core.geometry.Vector` into a `core.geometry.Vector4` and applies the `camera` transformation to it. The intent is that we set up the camera rotation to transform model space into camera space (i.e. screen coordinates). The second line of code then uses the variable `pos` to create a `SimpleIntermediate` instance with the position and the original vertex color. As in Basic Storm, if `return` is omitted, the value of the last statement is returned automatically.

Since we do not do any lightning, the pixel shader can simply use the `color` value of the `SimpleIntermediate` vertex it receives (remember: the rendering hardware interpolates it for us). As such, we can implement the pixel shader as follows:

    pixel shader for SimplePipeline: SimpleOutput simplePixelShader(SimpleIntermediate point) {
        SimpleOutput(point.color);
    }

Note that we utilize the fact that `return` is optional in the implementation above.

## Rendering Geometry

Now we have everything needed to render our cube, we just need to connect the pieces together.

The first thing we need to do is to create an instance of the pipeline. We add it as a member variable to the `SimplePainter` class we created earlier. We also need to initialize it in the `init` block of the class.

    class SimplePainter extends SamplePainter3D {
        // The pipeline.
        SimplePipeline pipeline;

        // ...

        init() {
            init {
                pipeline = SimplePipeline:create(simpleVertexShader, simplePixelShader);
            }

            // ...
        }
    }

As can be seen above, creating a pipeline requires us to provide a vertex shader and a pixel shader. The type system will check that the shaders are actually associated with the pipeline we try to create.

Now we can use the pipeline in the `render` function to render geometry to the screen. The `pipeline` has a `render` function that accepts the inputs we specified previously. In this case, we use the overload that accepts a `Graphics3D`, a `IndexBuffer`, and a `VertexBuffer<SimpleVertex>`. Note that the last parameter corresponds to what we specified after `data` in the pipeline! As such, we can modify the `render` function in our class as follows:

    Bool render(Size size, Graphics3D g) : override {
        // Clear the depth buffer and the background color.
        g.clearDepth();
        g.fill(black);

        // Render using the pipeline.
        pipeline.render(g, indices, vertices);

        false; // No need for animations.
    }

If we run the program at this point, we will still not see anything useful. The reason for this is that we have not yet set `camera` to a useful transform. Since the uniforms are initialized to 0 by default, all vertices will simply be transformed to the coordinate (0, 0, 0), which is not very interesting to look at.

Luckily, the fix is fairly easy. We just need to create a suitable transform and assign it to `camera`. In this case, we can do the following:

    Bool render(Size size, Graphics3D g) : override {
        // Clear the depth buffer and the background color.
        g.clearDepth();
        g.fill(black);

        // Update the transform.
        Transform cameraPos = rotateX(-30 deg) * translate(Vector(0, 0, 3));
        pipeline.camera = cameraPos * perspectiveProject(100 deg, size.aspect, 0.1, 100);

        // Render using the pipeline.
        pipeline.render(g, indices, vertices);

        false; // No need for animations.
    }

We first set `cameraPos` to a transform that rotates everything -30 degrees around the X axis, and then translates everything 3 units away in the Z direction. This corresponds to a camera that looks down on our cube (due to the rotation), and sits 3 units away in the Z direction. In the final code, this part of the transform is stores as a member of the class so that we don't have to re-compute it every frame.

The second line appends a perspective projection to everything. This is what causes the final result to appear to be 3D. The parameters to `perspectiveProject` are: (1) the vertical FOV, (2) the aspect ratio of the screen, (3) the near plane's Z coordinate, and (4) the far plane's Z coordinate.

This concatenated transform is then assigned to `camera` in the pipeline. This makes it so that the data is uploaded to the GPU as appropriate. It is worth noting that it is not necessary to re-set the transform every time `render` is called. Rather, the uniform retains its value across multiple calls to `render`.

At this point, we can run the program and now we will see a stationary cube on the screen!

## Simple Animations

To make the end result a bit more interesting (and to make it easier to see the back of the cube), we want to make the cube rotate. To do this, we use the `animationTime` in the `SamplePainter3D` class. This function simply returns a `Float` value that goes from 0 to 1 in the specified interval. We can observe its behavior by updating our `render` function as follows:

    Bool render(Size size, Graphics3D g) : override {
        // Get the animation time.
        Float time = animationTime(5 s);
        print("Time: ${time}");

        // ...

        true; // We wish to do animations now!
    }

If you run the program now, you will see the output `Time: ...` and that the value increases from 0 to 1 every 5 seconds. Let's use this to make the cube move!

To make the cube rotate, we can simply add another transform before we apply the camera position and the perspective projection like below:

    Bool render(Size size, Graphics3D g) : override {
        // Get the animation time.
        Float time = animationTime(5 s);
        print("Time: ${time}");

        // Clear the depth buffer and the background color.
        g.clearDepth();
        g.fill(black);

        // Update the transform.
        Transform cameraPos = rotateX(-30 deg) * translate(Vector(0, 0, 3));
        pipeline.camera = rotateY(360 deg * time) * cameraPos * perspectiveProject(100 deg, size.aspect, 0.1, 100);

        // Render using the pipeline.
        pipeline.render(g, indices, vertices);

        true; // We wish to do animations now!
    }

Note that we use the expression `360 deg * time` to "convert" `time` from a number in the range 0 to 1 into an angle expressed in degrees. This is a useful way to convert between units, without needing to use `pi`.

If we run the program now, we see a cube that makes one full rotation every 5 seconds.

We can also use the `time` variable in shaders. Before we do, we first need to pass it to the shader. Of course, we could add it as a member of the pipeline, just as we did with the `camera` transform. However, we will illustrate a different way to pass uniforms to shaders. This way is useful for values that will vary from one call to `render` to another (`time` is perhaps not the best use-case for this, as it is the same for every frame).

First, we add another parameter after `data` in the pipeline. Note that we mark it as `uniform` since we want the shader to use the same value for all vertices.

    pipeline SimplePipeline {
        // The flow of data through the pipeline:
        // (input parameters) -> vertex shader output -> pixel shader output
        data (SimpleVertex point, uniform Float time) -> SimpleIntermediate -> SimpleOutput;

        // Additional members become uniforms automatically, but are set globally rather than passed to
        // the function.
        Transform camera;
    }

Much in the same way that `camera` is now available in our shaders, `time` is also available to our shaders without any other modifications. For example, we can move the cube around in the vertex shader, after applying the camera transform. We can do this by modifying the vertex shader as follows:

    vertex shader for SimplePipeline: SimpleIntermediate simpleVertexShader(SimpleVertex point) {
        // Note: we can use the 'camera' uniform here.
        Vector4 pos = Vector4(point.pos) * camera;

        // Modify the x coordinate a bit, for a wavy effect:
        pos.x += sin(360.0 deg * time);

        // Note: 'return' is optional, just as in Basic Storm.
        return SimpleIntermediate(pos, point.color);
    }

Finally, we need to pass the time to the rendering pipeline. We do this by updating the call to `pipeline.render` as follows:

    // Render using the pipeline.
    pipeline.render(g, indices, vertices, time);

Note how the list of types after `data` in the pipeline correspond to the parameters passed to `pipeline.render`. However, also note that uniform parameters do not appear as a formal parameter to the vertex (or pixel) shader. This is to illustrate the fact that only the formal parameters of the vertex and pixel shaders are unique for this invocation. Remaining parameters are constant.