Generative Art / Shader Gallery
Mateus Reis, Rando Tõnso
Fragment shaders alone or in combination with other techniques can be used to create interesting visual effects. We created a gallery of different shaders and effects applied to a selection of surfaces and objects. No game engines were used.
In a fragment shader, each pixel runs one program, with no sharing of information between pixels - this makes them interesting for visual effects, because the performance is naturally high, and yet certain limits are inposed - drawing a circle, for example, is not as straightforward as telling the computer to draw a circle. You must find where the pixel is, then conditionally 'activate' it, if it happens to fall on the circle. Obviously there are work-arounds and more technology you can attach to make them more and more powerful, but some people have embraced these limitations and chosen to work within them to create art with shaders, somewhat resembling the tradition of the old demoscene.
In this project, I went as far as I could in learning the techniques used to make these types of pieces of art, which can go surprisingly deep and intricate: I have barely managed to scratch the surface in this project. Here I will show some examples and attempt to explain them.
Note the ambient glow that comes to the forefront with each color. This is achieved by assigning values to each pixel that are proportional to the distance to the points we want to make bright, then dividing a very small amount by that value. That way, only the pixels that are extremely close to the desired point become bright, and pixels farther away get a small amount of "glowiness" scaling with distance.
Since shapes cannot be directly drawn with a single command, to draw shapes, we use distance fields: we calculate distance from some point, and choose our fragment output based on that distance. In this gif, I made concentric circles by taking only the fractional part of the distance, which naturally will vary between 0 and almost 1.
One of the advantages of fragment shaders on the GPU when it comes to creating such effects is that, for example, tiling even a hundred times doesn't result in a massive performance hit, while if you had to draw the same effect a hundred times in a more standard CPU-powered drawing library, the performance costs would probably increase linearly. Usually we localize the pixels using UV coordinates: each pixel is localized with a float between 0 and 1 for horizontal and vertical location in its canvas. By simply multiplying these numbers by some integer and taking the fractional value, we easily and quickly tile whatever effect we want, and it's similarly simple to vary the parameters.
Computerized randomness is a vast and deep subject, to the point that coming up with a decent-looking smooth pseudo-randomness algorithm can net you name recognition for life. Thus, a really deep exploration of noise techniques was unfortunately not feasible. Instead, an attempt was made to get something nice out of basic pseudo-random procedures.
For example, the simplest of them is to take the fractional part of the sine of your random seed multiplied by some arbitrarily high number. This nets you a random number between 0 and 1, but there are various caveats and strange side-effects that pop up: it cannot simply be used as a blanket random algorithm. This particular gif (above) doesn't showcase that, but such oddities (for instance, the fact that if you 'zoom in' far enough, the random number is actually mostly continuous) can also be leveraged.
We went quite bare-bones when it came to technologies used, with C++, opengl, glfw and glm only. Code::blocks was used for windows compilation, while the development itself used only the more traditional make.
Challenges & conclusion
The initial intention was to have 3d 'sculptures' with shaders applied to them, and as such there was a need for nothing more complex than an environment and three-dimensional movement. With a lack of a game engine or an integrated map-making tool, however, creating a 3d environment requires manual trial-and-error placement. In the end, we ended up running out of man-hours, and the 3d environment part of the project was scrapped, resulting in eleven pieces of 2d fragment shader art.
That said, they were quite enjoyable to create, and I feel like my knowledge of opengl, shaders, general program structure and even c++ build systems have deepened considerably as a result of this project.
Mateus Surrage Reis
Responsible for program structure, mouse aiming, camera movement, windows build, the final report page and demo video, as well as 10 out of the 11 final shader effects.
(Video quality unfortunately mangled by youtube...)