Today I reworked a large part of RF’s rendering pipeline. I finally added proper sRGB support throughout the pipeline, and the fake bloom just looks so much better with it. We’ll need to re-tweak a lot of colors as all non-texture colors (e.g. hardcoded float color values for as vertex colors or uniform color values) will look significantly brighter now, but it’ll just be a tiny amount of work to fix.
A very common problem I’ve encountered is essentially wanting to scale something nicely without aliasing, but still retaining the pixely look of GL_NEAREST filtering. Essentially, I want antialiased edges between the different texels, but the actual interior of each texel to be just a single color from the texture. I found the solution to this: I enable linear filtering and then use a shader to “sharpen” the texture coordinates before sampling. This lead to very good results.
What I actually wanted to use this for was to upscale the entire rendered scene in Robot Farm to screen resolution. Our tiles are 16x16 pixels big, which meant that we needed to render those tiles (and everything else) at a scale that was a multiple of 16. We then realized that having a higher screen resolution or simply lowering the tile scale meant that the player would be able to see much further than intended, which gave the player a big advantage when exploring. To keep the view distance fixed, we needed to support arbitrary scaling with of tiles without them looking like absolute crap. For example, here are our 16x16 tiles drawn as 17x17 quads.
The issue here is that with nearest neighbor filtering, the pixels from the original tiles end up covering either 1 or 2 pixels, which causes heavy distortion of objects depending on where they are on the screen. This is especially visible during motion, but the anti-aliased text drawn at tile resolution shows the issue very well in a still image. The numbers look choppy and have weird thickness here and there. When using the new shader to upscale, here’s what I get:
In this case, the new shader essentially works as bilinear filtering of the screen as the upscaled resolution is so similar to the original resolution, but it is a tiny bit sharper than bilinear filtering at least. Not too impressive actually…
Looking at a more zoomed-in result where we upscale from 16x16 to 34x34, we get the following result with nearest neighbor filtering:
Ugly again, the fact that each pixel is upscaled to either 2 or 3 pixels causes visible differences and artifacts in the text again. Let’s try bilinear filtering:
Well, it’s soft and all, but it’s also very blurry. How about the new shader?
Perfect! The image is as sharp as it can be without introducing aliasing, but still filtered to the point where the pixels in the text look perfectly even. Try switching between this and the unfiltered image and see the massive difference without a significant loss of sharpness.
Essentially, what the new shader does is give you the output of extremely super-sampled nearest neihgbor filtering, without requiring rendering at a higher resolution. It looks great and costs pretty much nothing! =D