Hello Unity community! I am writing a 3D, first person game that makes use of low res, low color, unfiltered textures and I looking for a way to light the game that doesn’t spoil the charming retro look of the art.
My plan was to light the scene conventionally, using unity lights and the unity standard shader, then create a posterization post-process effect. That was relatively easy. I created a function to map any RGB value to its nearest perceptual match in the game’s palette and the results were just what I’d hoped.
As long as you don’t look too close anyway! Look at these ugly artifacts.
The lighting calculation is occurring per fragment, so when I go to posterize the result, I end up with these jagged looking patches that break up the pixel grid. Rendering low res works, of course, but it introduces all kinds of other artifacts as things scale. I really want to keep my high res render. What I need to do is calculate the lighting not per-vertex or per-pixel, but per-texel, to preserve the pixel grid in the sprites.
One technique would be to edit the standard shader’s forward path like so:
half4 fragForwardBaseTexel (VertexOutputForwardBase i) : SV_Target
{
UNITY_APPLY_DITHER_CROSSFADE(i.pos.xy);
FRAGMENT_SETUP(s)
// 1.) Snap fragment UVs to the center of the nearest texel
float2 snappedUVs = floor(i.tex.xy * _MainTex_TexelSize.zw) +
_MainTex_TexelSize.xy/2.0;
// 2.) Transform the snapped UV back to world space (aka ???)
float3 snappedPosWorld = float3(); // TBD: Transform the point!
// 3.) Assign the snapped world position for use in lighting calculations
s.posWorld = snappedPosWorld;
...
}
That way shadows, light source falloff, reflectance, everything will snap to the pixel grid. Seems like a really tidy idea. It also has the nice property of not overcalculating lighting when things are far away and texels are smaller than screen pixels.
But I cannot for the life of me figure out how to get the transform I need for step 2! The more I learn about the rendering pipeline, the more I worry that this might not even be conceptually possible. How do I go from an arbitrary UV location to world position in the fragment shader?
And beyond that, has anyone worked with or thought about per-texel lighting before? I am very open to other suggestions for how to accomplish this effect. Light maps calculated per frame, deferred rendering tricks, abandoning the standard shader and working on my own from scratch… I am open to anything. Thanks!