2
\$\begingroup\$

I'm new to THREE.js and I have a frustrating problem. I can't get my grass texture to size "normally" on the terrain.

I get it, the terrain is steep, but that doesn't mean the grass should be stretched as well!

I don't want the textures to scale, resize, stretch, or distort. I want the textures to render the same size as the original image. The image is 128x128, so why can't the texture be the exact same?

THREE.js doesn't allow me to make the texture repeat as much as it needs to fill the plane. It only lets me set a fixed number of "repeats." That's what most likely causes the distortion.

texture.repeat.set(10, 10);
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;

How can make the size of the texture not be affected by the size of the polygon it's on? If the image is 128x128, it should still be 128x128 on the terrain.

EDIT: Is it possible to use pixel coordinates instead of UV?

\$\endgroup\$
5
+50
\$\begingroup\$

This isn't really a THREE.js problem (although THREE.js may limit your options for a solution); it happens with any terrain implementation of that type.

From the screenshot, it looks like you're using a regularly-tessellated grid of geometry where each vertex is mapped to a height map point. Such a grid generally generates triangles in a pattern, for example:

 A -- B -- C -- ...
 | \  |  / | \
 |  \ | /  |  \
 D -- E -- F -- ...
 |  / | \  |  /
 | /  |  \ | /
...  ...  ...

The triangle mesh generally maps UV coordinates to textures regularly along that grid (for example A may have UVs of 0,0 while B may have UVs of 1, 0.

The grid is regular in the X and Y dimensions, but it's not regular in Z (which comes from the heightmap). This means while the similar edges in each cell are the same length prior to being displaced vertically by the height map, they probably aren't the same length afterwards. In particular, extreme elevation changes (like in your screenshot) create longer triangle edges.

Because the UV coordinates are regular, this means longer triangles "stretch" that texture over a longer physical region, resulting in the artifacts you see.

You can try to fix this by scaling your UV generation, but this will generally just make small terrain cells look muddy as your texture is compressed into a small space; it's not a good solution. It is not really possible to "use pixel coordinates" instead of UV coordinates to map the texture to the geometry, as you asked in on of your edits. That's not how the 3D pipeline works.

There are solutions though. Common ones include:

  • adaptive tessellation of the grid; you can "peek" at the height map values for a cell and its neighbors and if the height differential is above a threshold you set, try to split that cell into smaller cells, adjusting UV coordinates proportionally. This can be tricky, because you want to make sure all the seems line up well and need to avoid creating unlinked T-junction constructs.

  • decals or similar detail texture splatting; use these to simply cover up the stretched terrain like you might patch a hole in a shirt.

  • props or other 3D meshes. Terrain with those kinds of elevation differentials rarely looks like that, with the grass just wrapping down around it. Often there are rocks there, or a dirt bank with old tree roots or whatever sticking out.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.