October 20th, 2024

The Best Darn Grid Shader (Yet)

Ben Golus is developing a grid shader to surpass texture-based grids, focusing on user-configurable line widths, perspective thickness, and minimizing aliasing for improved visual quality in real-time rendering.

Read original articleLink Icon
The Best Darn Grid Shader (Yet)

Ben Golus discusses his long-standing challenge of creating an effective grid shader that surpasses the quality of texture-based grids. He highlights the difficulties in achieving a grid shader that maintains visual fidelity, particularly in terms of aliasing and Moiré patterns, as lines recede into the distance. Golus reviews various existing techniques, including the filtered pulsetrain and box filtered grid, noting their strengths and weaknesses, particularly in handling aliasing. He emphasizes his desire for a shader that allows user-configurable line widths, maintains perspective thickness, and minimizes aliasing across all distances. The article details his iterative approach to developing a grid shader, starting with basic line drawing techniques and gradually refining them to achieve the desired visual quality. Golus ultimately aims to create a shader that is not only visually appealing but also practical for real-time rendering applications.

- Ben Golus aims to create a grid shader that outperforms texture-based grids.

- The article reviews existing grid shader techniques and their limitations, particularly regarding aliasing.

- Golus emphasizes the need for user-configurable line widths and perspective thickness in grid shaders.

- The development process involves refining basic line drawing techniques to improve visual quality.

- The goal is to create a shader suitable for real-time rendering with minimal visual artifacts.

Related

Eight million pixels and counting: improving texture atlas allocation in Firefox (2021)

Eight million pixels and counting: improving texture atlas allocation in Firefox (2021)

Improving texture atlas allocation in WebRender with the guillotiere crate reduces texture memory usage. The guillotine algorithm was replaced due to fragmentation issues, leading to a more efficient allocator. Visualizing the atlas in SVG aids debugging. Rust's simplicity and Cargo fuzz testing are praised for code development and robustness. Enhancements in draw call batching and texture upload aim to boost performance on low-end Intel GPUs by optimizing texture atlases.

Introduction to Realtime Fluid Simulation for Programmers and Technical Artists

Introduction to Realtime Fluid Simulation for Programmers and Technical Artists

The article introduces real-time fluid simulation for programmers and artists, focusing on geometrical concepts in Unity 3D. It discusses fundamental behaviors, initial flaws, and encourages developing a more accurate simulation.

My favorite tools and techniques for procedural gamedev

My favorite tools and techniques for procedural gamedev

Casey Primozic shares his procedural game development techniques, focusing on seamless textures, custom shaders, AI-generated textures, and volumetric effects, while exploring future applications like Constructive Solid Geometry for 3D manipulation.

CSS Grid Areas

CSS Grid Areas

CSS Grid Areas enable developers to create complex layouts easily. The article explains named grid areas, syntax, and practical examples, encouraging their use for efficient and visually appealing web designs.

Mitosis in the Gray-Scott model: writing shader-based chemical simulations

Mitosis in the Gray-Scott model: writing shader-based chemical simulations

The article explains the Gray-Scott model of reaction-diffusion systems, highlighting GPU advantages for simulations, introducing shader-based programming with GLSL ES, and encouraging experimentation to observe complex emergent patterns.

Link Icon 16 comments
By @nightowl_games - 3 months
"Which, if you’re like me and didn’t finish their college level math courses, means absolutely nothing. I dropped out of art school, so it’s mostly over my head."

This is why Ben Golus's posts on shaders are the best. Cause they're actually accessible.

By @singron - 3 months
This was really interesting. I don't love the fading out to solve the moire in the final solution. I wonder if some dithering would feel better and give the impression there are lines out there rather than a smooth gray surface? Or maybe some jitter to break up the pattern?

This isn't shown in the post, but sometimes the moire feels worse if you are walking around and the moire has a movement of its own (either flickering/shimmering or sweeping across in uv space), and it's probably a decent benefit to get rid of the moire even if it's imperfect.

By @tobr - 3 months
Looks very interesting. I’m confused by the note in the intro though:

> Note: I highly recommend viewing this article in dark mode.

I’m trying, but it seems like Medium doesn’t even have a dark mode? Is this reposted from somewhere else?

By @arandomhuman - 3 months
This is really awesome. to ask a dumb question, what’s a good way to get acclimated with running and building shaders? Just going straight to OpenGL tooling and extrapolating from there?
By @slimbuck - 3 months
I love this article! I used it as a basis for the multi-level grid in SuperSplat (https://playcanvas.com/supersplat/editor).
By @unclad5968 - 3 months
It's awesome so much effort was put into rendering a grid. Great write up.
By @bschwindHN - 3 months
This is great! I have a little viewer app for a code-based CAD tool and I've been delaying making a grid for it as I haven't found a satisfactory solution for it until I just read this article :)
By @Applejinx - 3 months
Beautiful work. I'm happy to read it from start to finish. Not at all sure I have an application for this, but I completely get why this is a beautiful thing :)
By @ansgri - 3 months
Very nice, I'd use it as one of the starting points if I was to learn 3D graphics as it touches upon a lot of math details in a seemingly simple problem.
By @exDM69 - 3 months
I have an even better darn grid shader that I use in my graphics projects.

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);
    }
By @erikerikson - 3 months
The whole article I was wondering why the lines that were naturally aligned to the pixel grid were privileged.

Why wouldn't you fade all lines according to their depth towards the horizon?

By @miniBill - 3 months
> At least until Mr. Quilez writes a shader that beats this one.

Made me chuckle. He truly is in a class of his own.

By @zadler - 3 months
Can’t wait to try this.