Why Multiplying UV Coordinates Changes Everything in GLSL

in #learning7 days ago

If you have not already read the previous lesson in this mini series, I also recommend starting there.

Understanding fract() in GLSL: The Function That Makes Patterns Repeat
https://ecency.com/@hey2d/understanding-fract-in-glsl-the-secret-behind-repeating-patterns-fcr

In that article, we learned what the fract() function does and why it is responsible for creating repeating patterns. We saw that fract() removes the whole number from a value and keeps only the decimal part, allowing coordinates to restart over and over again. In this lesson, we will look at the line that makes fract() useful in the first place.


The Most Important Multiplication You Will See

When beginners first encounter this line of code,

vec2 g = fract(vUv * density);

their attention usually goes straight to fract().

While that function is important, there is another operation hiding in plain sight.

vUv * density

Without this multiplication, fract() would not create any visible repetition.

Understanding why this works will make many procedural shader techniques much easier to understand.


A Quick Reminder About UV Coordinates

Before we multiply anything, remember what vUv looks like.

Across the width of the screen, the x coordinate moves smoothly from 0 to 1.

Left ------------------------ Right

0.0                       1.0

The y coordinate behaves the same way from bottom to top.

That means every pixel already knows its position.

If we display the x coordinate directly,

vec3 color = vec3(vUv.x);

we get one smooth gradient stretching across the entire screen.

The important word here is one.

The coordinates only make one trip from 0 to 1.


Stretching the Coordinates

Now imagine multiplying every UV coordinate by 2.

vUv * 2.0

Instead of moving between 0 and 1, the values now move between 0 and 2.

Before

0 → 1

After

0 → 2

Nothing has repeated yet.

The numbers have simply been stretched.

Now try multiplying by 5.

vUv * 5.0

The range becomes

0 → 5

And with

vUv * 10.0

the values now travel from

0 → 10

The larger the multiplier, the larger the coordinate values become.


Why Does This Matter?

At this point you might wonder why larger numbers are useful.

After all, the screen itself has not changed.

The answer is that fract() only becomes interesting when the values grow beyond 1.

Suppose the coordinates look like this after multiplication.

0.0

0.3

0.7

1.1

1.5

1.9

2.2

These values now contain whole numbers.

That means fract() has something to remove.

The result becomes

0.0

0.3

0.7

0.1

0.5

0.9

0.2

Instead of continuing upward forever, the values restart every time they pass a whole number.

That is exactly how repeated patterns are created.


Understanding Density

The multiplier is usually stored in a variable called density.

float density = 10.0;

The name is descriptive.

A higher density means more repeated sections fit into the same screen space.

A lower density means fewer repetitions.

Think of drawing squares on a piece of paper.

If you divide the page into four squares, each square is large.

If you divide the same page into one hundred squares, every square becomes much smaller.

Nothing about the paper changes.

Only the number of divisions changes.

The density variable works in exactly the same way.


Putting It Together

Now the complete line begins to make sense.

vec2 g = fract(vUv * density);

First, the UV coordinates are multiplied.

This stretches the values beyond their normal range.

Next, fract() removes the whole number from every coordinate.

The decimal portion remains.

Each time a whole number is crossed, the coordinates begin again from zero.

Every restart becomes another tile.


Exercise

Use the following shader.

#ifdef GL_ES
precision mediump float;
#endif

varying vec2 vUv;

void main()
{
    float density = 2.0;

    vec2 g = fract(vUv * density);

    vec3 color = vec3(g.x);

    gl_FragColor = vec4(color, 1.0);
}

Start with a density of 2.0.

Notice how the gradient repeats only a few times.

Now change the value to

float density = 5.0;

The repeated sections become smaller.

Next, try

float density = 10.0;

Finally, experiment with

float density = 20.0;

Each increase creates more repetitions because the coordinates are being stretched further before fract() resets them.


A Common Misunderstanding

Many people think the multiplication itself creates the repeating pattern.

It does not.

If you remove fract() and write

vec3 color = vec3(vUv.x * 10.0);

you will not get ten repeating gradients.

Instead, the values simply become brighter because they continue increasing beyond 1.

The repetition only appears when fract() continuously removes the whole number.

The multiplication and fract() work together.

Neither one produces the effect on its own.


Why This Technique Appears Everywhere

Once you understand this idea, you will start recognizing it in many shaders.

Repeating circles.

Checkerboard patterns.

Bricks.

Tiles.

Noise.

Pixel art.

Many of these effects begin with the same two steps.

Stretch the coordinates.

Restart them with fract().

Everything else is built on top of that foundation.


Posted Using INLEO

Sort:  

Congratulations @hey2d! You have completed the following achievement on the Hive blockchain And have been rewarded with New badge(s)

You published more than 30 posts.
Your next target is to reach 40 posts.
You received more than 3250 upvotes.
Your next target is to reach 3500 upvotes.

You can view your badges on your board and compare yourself to others in the Ranking
If you no longer want to receive notifications, reply to this comment with the word STOP