Rotate UV along normal and repeat the UVs per face?

Hi again!

I’m trying to rotate the UV of a texture (per face) along a normal map - if that makes sense. The end-goal is a “foam” layer around an island where the foam texture is a simple horizontal squiggly line, so to speak. I’m trying to rotate the UVs of this texture in such a way that it wraps itself around an island.

Here’s what I got so far.

The island:

The normals around the island (the normals are a generated texture):

The foam texture applied where it should go:

The result so far:

Here’s my fragment shader. I think I’m going in the right direction. I just can’t figure out how to have the same repetition in textures like in the image before the last if I apply the UV rotation.

varying vec2 vUv;

uniform sampler2D tMap;
uniform sampler2D tFoam;

// Rotates a vector by {a}
vec2 rotate(vec2 v, float a)
    float s = sin(a);
    float c = cos(a);
    mat2 m  = mat2(c, -s, s, c);

    return m * v;

void main()
    // Foam map (generated normal map), offset slightly to match terrain location.
    vec4 foamMap = texture2D(tMap, vUv + 0.005);

    // Figure out the normal angle.
    float angle = acos(dot(vec2(0.5), foamMap.rg));

    // Rotate the UV along the normal.
    vec2 uv    = rotate(vUv + 0.005, angle);
    vec4 color = texture2D(tFoam, fract(uv));

    gl_FragColor = vec4(color.rrr, color.r * foamMap.a);


I figured out how to repeat, using fract(uv * 100.0), but the rotation is (still) completely off. It should repeat/rotate the texture like a ‘curve’ around the island. I’m starting to doubt if this is even possible using this approach :thinking:

1 Like

Any chance to provide an editable live code example?

Here’s a fiddle with all the relevant code. It’s a bit finnicky since everything is part of a larger framework that I can’t just put in a fiddle. The texture you see in the fiddle should be wrapped around a box.

Move your mouse to the upper left corner of the viewport to see the generated “normal” texture.
The functions that create the textures are defined at the bottom (over-simplification of the real thing, but the best I could come up with).

1 Like

Updated fiddle for better visibility:

This is the closest I can get it to represent the actual project. I’ve scaled down the plane and texture repetition (from 100 to 5) for better visibility in the fiddle.

Edit: Final version (promise :sweat:) (lowered normal resolution. This is now exactly representing what I’m struggling with).

Is it not an option to encode rotation in THREE.DataTexture?

I went with this approach so I can let the GPU interpolate the normals smoothly. I have no idea how to go about this in the data texture. I’m basically using the normal texture for the sole purpose of rotating the image. The foam image itself is just a repeating png image that “wraps around” an Island.

Here is an example with THREE.DataTexture:

I’m pretty sure, this is not the option you want, so I’ll think on the approach with a normal map :slight_smile:

1 Like

Lovely idea!

However that won’t work. Look:

The lines should be connected to each other. I was hoping by going the ‘normal map’ route that the GPU would interpolate the corners nicely (plus, I’m using this texture as well for opacity to smoothen out the foam texture around the edges from land to sea).

I think I made one mistake, that is to not take the b (blue) color into account with the normal map approach. I’ve tried it by creating a cross product between the normal and uv.x, 0.5, uv.y, but unforunately that wasn’t it either… It’s close though, until you start tiling the texture 10x or more, then it starts acting funky again.

My math skills are not really up to par, and I’ve never done this much in GLSL like…ever… So i’m pretty much guessing and doing trial and error all day :sweat_smile:

I figured out an intermediate solution for now. Without repeating and using the following shader, I applied a time float that wraps between 0~1 to scroll the UV in the direction its facing. Since half of the foam is under the terrain, you won’t notice any mirroring.

I would still very much like a method that would allow for texture repeating this way, but from what I gather by all the trial & error I’ve been doing, that’s impossible because of the double sided gradient that is produced by the generated normal map. Since my maps are just 2D-arrays with x/y coordinates indicating where a tile is, I simply generate a normal map based on where a tile is that is connected to water (nothing).

1 Like