Dynamic Cubemaps
While watching John Beech work in MM Dreams, I noticed the background and had to try and replicate it. This effort resulted in a easy method of rendering painterly / cloudy looking backgrounds, with the added benefit of being very flexible while remaining cheap.
Inspiration
It occurred to me that they are just accumulating splats on the background, so I hopped into Krita and spammed to circles to see how it looked - I really liked the results and how simple it was to make this effect.
Approach
The main idea here is that you need to render to 6 different textures, one for each face of a cube. By leaving the previous cubemap's contents, but turning on alpha blending you get this nice incremental effect that looks like rain or brush strokes. Anything can be rendered to these textures, including fullscreen shader passes (e.g., shadertoy shaders). Care must be taken to make sure the corners/edges match or it will look pretty terrible.
Since my goal is to match the effect seen in Dreams, adding a few (10-100) randomly positioned splats per frame seemed like a good place to start. I tried GL_POINTS
first, but couldn't get consistent UVs across cube seams.
This could probably be rectified with billboarded quads, but I had a wild idea about putting some volumetric data in each splat. In other words, I needed to be rendering boxes.
If you look really close you can still see some seams, but it turns out those artifacts were created while rendering the cubemap to the screen and fixed by computing a more accurate direction vector.
Implementation
- setup
- create cubemap
- pre-compute VP matrices
- frame
- render N boxes to each face of the cubemap
- generate random box positions in vertex shader
- render procedural / volumetric data in fragment shader
- render the cubemap
- render N boxes to each face of the cubemap
source (webgl2): dynamic-cubemaps.js
Conclusion
So there you have it, a simple way to render painterly backgrounds using ~100 cubes rendered 6 times per frame. The hardest part about this whole thing was setting up the framebuffers.