The shader in this article wants to emulate the look of a sampled texture so that it blurs to medium gray at distance while avoiding moire patterns. And it does indeed look quite good at what it's aiming for.
I on the other hand wanted an "infinitely zoomable grid paper" look with constant-ish pixel width lines, such that the density of the grid changes. If applied to a "ground" plane, the grid gets sparser near the horizon. When you zoom in, the grid gets denser with a new "decade" of grid fading in with the old grid fading out.
I generally apply this to a "full screen triangle", and do a raycast against the ground plane (e.g. the y = 0 plane) and extract the "uv" coordinates from the raycast result (that would be the x,z coordinate of the ray hit). I've also applied this technique to a skybox, where I take the view ray direction unit vector and convert it to spherical coordinates (latitude, longitude) for the UV.
This shader gives a very clean looking grid at almost any viewing angle and distance. If your world space units are meters, this can be zoomed in from nanometers to gigameters while looking crisp and clean. Unfortunately, floating point issues take over after that point and there are some pixel artifacts when the camera is very close to the ground plane and the viewing angle is extreme. This could be fixed by adding some clamping to the the `log_width` variable, but I haven't bothered with that as I don't work in nanometers in computer graphics projects. There's also some flickering at the horizon line, which I've solved with a trivial fade out factor (not visible in the source code below).
As it is shown below, it'll show a grid with a subdivision factor of 10, like millimeter paper with the primary grid in centimeters and secondary grid in millimeters. The subdivision can be changed by adjusting the `base` and `N` variables. See the comments for explanation.
Here's the thing in all its glory. Apologies that I don't have a screenshot hosted where I could share. Please let me know if you're trying this out in your projects.
float grid(vec2 uv, float linewidth) {
vec2 width = linewidth * fwidth(uv);
vec2 grid = abs(fract(uv - 0.5) - 0.5) / width;
float line = min(1.0, min(grid.x, grid.y));
return 1.0 - line;
}
float decade_grid(vec2 uv) {
// grid subdivision factor (logarithm base)
float base = 10.0;
// grid density, primary grid is approximately base^N pixels wide
// with N = 3, primary grid is 100 pixels, secondary grid 10 px
float N = 3.0; // 3.0 is the densest grid that does not have moire patterns
// approximate grid cell size using screen space uv partial derivatives
vec2 width = fwidth(uv);
//vec2 width = vec2(length(dFdx(uv)), length(dFdy(uv)));
float w = max(width.x, width.y);
//float w = length(width.xy);
// take logarithm of grid cell size to find zoom factor
float log_width = log2(w) / log2(base); // logarithm change of base
// round down to find grid zoom factor (power of ten)
float exponent = -floor(log_width) - N;
float blend = 1.0 - fract(log_width); // blend between powers of ten
// primary grid with wider lines
float grid1 = grid(uv * pow(base, exponent), 1.0 + blend);
// secondary grid with narrow lines
float grid2 = grid(uv * pow(base, exponent + 1.0), 1.0);
// mix primary and secondary grid with linear interpolation
return mix(grid1, grid2, blend);
}