Building Your First Procedural Grid in GLSL

avatar

If you have not read the previous lessons in this series, I recommend doing that first.

Understanding fract() in GLSL: The Function That Makes Patterns Repeat
Understanding fract() in GLSL: The Secret Behind Repeating Patterns

In that lesson, we explored how fract() continuously resets UV coordinates, making repeating patterns possible.

Why Multiplying UV Coordinates Changes Everything in GLSL
Why Multiplying UV Coordinates Changes Everything in GLSL

There, we learned why multiplying vUv by a density value creates more repeated sections before fract() resets them.

Today, we finally use those repeated coordinates to draw something visible.

From Stripes to Grids

In the previous article, our shader only looked for one edge.

float line = step(1.0 - lw, g.x);

This checked the x coordinate inside every repeated tile.

Whenever the coordinate reached the right side of the tile, a line appeared.

The result looked something like this.

|   |   |   |   |
|   |   |   |   |
|   |   |   |   |

Only vertical lines existed.

The next question is simple.

What happens if we do exactly the same thing with the y coordinate?


The Missing Half

Remember that every repeated tile contains two coordinates.

g.x

g.y

The previous lesson only used

g.x

Now we add another line.

step(1.0 - lw, g.y)

This works exactly the same way.

Instead of detecting the right edge of every tile, it detects the top edge.

The result becomes a series of horizontal stripes.

-------------
-------------
-------------
-------------

On their own, these horizontal lines are not very exciting.

The interesting part happens when we combine both directions.


Combining the Two

The complete line looks like this.

float line =
step(1.0 - lw, g.x)
+
step(1.0 - lw, g.y);

Look closely at what is happening.

The first step() creates vertical lines.

The second step() creates horizontal lines.

Adding them together means both sets of lines appear at the same time.

Instead of stripes, the shader now produces a grid.


Thinking About the Grid

Imagine placing transparent tracing paper over a notebook.

One sheet contains only vertical lines.

Another sheet contains only horizontal lines.

Separately, they are simple.

Stack them together, and suddenly you have graph paper.

That is exactly what this shader is doing.

One function draws every vertical line.

Another function draws every horizontal line.

Adding them together combines both patterns into one.


Why Does This Work?

Remember that step() only returns two values.

0

or

1

When a pixel is not close to an edge, the result is

0

When it reaches the edge, the result becomes

1

Since both step() functions work independently, every pixel asks two questions.

"Am I close to the right edge?"

"Am I close to the top edge?"

If either answer is yes, a line appears.

If both answers are yes, the pixel belongs to a corner where two lines meet.

We will return to those corners in the next lesson.


The Exercise

The original shader begins with

float line = 0.0;

Since the value never changes, every pixel uses only the background color.

Replace it with

float line =
step(1.0 - lw, g.x)
+
step(1.0 - lw, g.y);

Run the shader.

Instead of a blank screen, you will immediately see a repeating grid.

This is one of those satisfying moments where only two extra lines of code completely change the result.


Try Each Step Separately

To better understand what is happening, try each version on its own.

First, use only the x coordinate.

float line =
step(1.0 - lw, g.x);

You should see vertical stripes.

Next, replace it with

float line =
step(1.0 - lw, g.y);

Now the stripes become horizontal.

Finally, combine both.

float line =
step(1.0 - lw, g.x)
+
step(1.0 - lw, g.y);

The grid suddenly appears.

By testing each version individually, it becomes much easier to understand where every line comes from.


Complete Example

#ifdef GL_ES
precision mediump float;
#endif

varying vec2 vUv;

void main()
{
    float density = 10.0;

    vec2 g = fract(vUv * density);

    float lw = 0.05;

    float line =
        step(1.0 - lw, g.x)
      + step(1.0 - lw, g.y);

    vec3 color = mix(
        vec3(0.1, 0.15, 0.25),
        vec3(1.0),
        line
    );

    gl_FragColor = vec4(color, 1.0);
}

If you run this shader, you should see a clean repeating grid covering the entire screen.


One Small Problem

Although the shader already looks correct, there is one issue hiding inside the code.

Every time a vertical line crosses a horizontal line, both step() functions return

1

Adding them together produces

2

That is outside the normal range used for blending colors.

The shader still works, but those intersection points now contain values larger than we actually need.

Fortunately, fixing the problem only requires one extra line of code.

Posted Using INLEO



0
0
0.000
0 comments