Introduction to Shaders
by Alexey Tukalo
Shaders are an essential part of our workflow in Arction. They are used to achieve outstanding performance through delegation of the calculation process to Graphic Card. They also provide us with an ability to implement custom visualization algorithms in a very close to the hardware level.
GPU is an example of an extremely parallel hardware. Efficient utilization of its power requires parallel data computation. Computation Pipeline is a very good approach for this scenario. It can be used in a situation when parallel computation is applied to some specific problem. Then the case can be generalized and broken to steps with certain sets of valid input and output parameters. In such pipelines data travels from stage to stage sequentially. Rigid data flow allows performing automatic parallelization of the computation process efficiently.
There are programmable and unprogrammable parts of computation pipeline. Programmable steps are represented by some kind of pure functions which have access to the isolated part of the entire dataset provided as an input. In addition, they can access a set of the immutable objects which is supplied at an initialization step. Unprogrammable stages implement a part of logic which is generic for the pipeline. For example, they manage data distribution across computational units.
The pipeline approach is very common nowadays; for example, Apache Hadoop (MapReduce) uses it to perform Big Data analysis on computer clusters. They break computation into three steps: Map (sorting, filtering), Shuffle (automatic redistribution of a dataset according to Map results) and Reduce (summary operation). A combination of chained higher-order functions allows Apache Spark to build highly scalable, fault tolerant custom pipelines for real-time data processing. Graphic Pipeline represents an application of the concept to GPU-accelerated computer graphics.
Every Graphic Library has its own implementation of Graphic Pipeline. Their common feature is the fixed order of computational stages. The unprogrammable part of the pipeline is called fixed functions. Programmable parts are called Shaders. In Arction, we are using DirectX 9 and 11, so some of the terminology used further in the article can be specific for their implementation of Graphic Pipeline.
The computational pipeline of modern Graphic Library contains four types of Shaders: Vertex, Tessellation, Geometry and Pixel (Fragment). Shaders are usually implemented in specially designed C-like languages with a very simple syntax. The main aim of the languages is clear and efficient declaration of computations which are needed for realistic 3D space visualization.
Graphics Pipeline receives a collection of vertices as an input. For a successful invocation, it also needs to set options for fixed functions, code of correspondent shaders and constant values for them. An output of the pipeline processing is displayed on the screen or returned as a texture.
Implementation of Graphic Pipeline
Types of Shaders
At this stage, vertices are processed one by one. The process includes operations which have to be performed at every vertex separately. Typically, these are just intermediate values which are needed in future stages. Rotation, scaling and positioning of the vertex at the scene are a good example of such operations. Usually, it is done by multiplication of vertex’ world coordinates by three matrixes which hold the information. Vertex Shaders are executed rarer than other types of shaders, so it is faster to perform there as much work as possible. Thereby, sometimes it also can be used for more sophisticated tasks like lightning calculation. This kind of decisions has a positive impact on performance but can cause unpleasant artefacts on a final picture, that is why this approach is not a good idea for a major part of 3D applications.
It is a very new feature of Graphics Pipeline, which is used for smoothing the geometrical shapes by dividing the primitives into smaller parts. Tessellation stage contains two shaders and one fixed function. The sub-stages are represented by Hull Shader, Tessellator and Domain Shader. To achieve desirable results, all three of them should be utilised together. Tessellation is a very advanced topic which requires deeper discussion, so it is not fully covered in the article.
It is also quite a new stage of the rendering pipeline, which is not required for simple 3D applications, but it opens unique possibilities for advanced cases. In contrast to Vertex Shader, it allows performing operations over complete geometry primitives represented by several vertices. In other words, it can modify, add and remove vertices from the pipeline. The abilities allow it to convert input geometry to output one. As a result, a single vertex can be replaced by a line, triangle or even a complex shape constructed from multiple polygons. Great possibilities for drawing things like grass, leaves, rain, sprays and other particle-based effects are opened by the feature.
Pixel or Fragment shader is the last programmable part of the rendering pipeline. It is executed after rasterization of geometry, and receives specific information for each pixel of each polygon. The values are calculated by interpolation of data from correspondent vertices of the triangle which covers the area. It is the best place for accurate calculation of a pixel’s colour. Usually, it is used for precise lighting calculation, but sometimes it is also utilized for image processing. Due to a large number of pixels in modern screens, a part of computation specific for every pixel tends to be the most expensive one. In addition to color, an output of the stage can also contain the depth of the corresponding point of the triangle. The depth value is needed for accurate compilation of the final image at Output Merge.
In this short article, we discussed the concepts of rendering pipeline and shaders. We also covered the advantage of GPU-based acceleration for computer graphics rendering. Additionally, utilisation of computational pipelines for parallelization of computational process was discussed. I hope that knowledge and experience which we shared here will help you in your projects.