Hello there.
I have a mesh with ExtrudeGeometry and texture, but it applyes correctly for front face only. I want it to look like wrapped, not extruded.
Front view:
Side view:
I have some MaterialProcessor, which gets config from server with some material properties and create StandardMaterial with expected texture/offset/repeat/etc.
const material = new THREE.MeshBasicMaterial({
color: 0x161616
});
const geometry = new THREE.ExtrudeGeometry(shape, {
steps: 2,
depth: 40,
bevelEnabled: false,
});
const mesh = new THREE.Mesh(geometry, material);
/* some code to get StandardMaterial */
mesh.material = nextMaterial;
I guess there is something I need to do with uv, but I don’t know what.
I saw some legacy code where multiple materials added to mesh (like material for each face), but this code is very old. Also there is no extrudeGeometry.faces now.
Thanks for any help!
Have you set .wrapS
and .wrapT
for the texture? When I try texturing an extruded object, I see no problems:
https://codepen.io/boytchev/full/KKxVzzp
Yes, I use this code for both map and normalMap:
mesh.material.map.wrapS = THREE.RepeatWrapping;
mesh.material.map.wrapT = THREE.RepeatWrapping;
mesh.material.map.repeat.set( toSetRepeat[ 0 ], toSetRepeat[ 1 ] );
toSetRepeat is a coeff calculated with server stuff.
===
Also maybe problem can be in this method, that I use after creating geometry. It was needed to compute uv for BufferGeometry in 2d, and I guess there is something I need to add for it to work in 3d.
geometry.computeBoundingBox();
let {min, max} = geometry.boundingBox;
let offset = new Vector2(0 - min.x, 0 - min.y);
let range = new Vector2(max.x - min.x, max.y - min.y);
const position = geometry.attributes.position;
const uvs = [];
for ( let i = 0; i < position.count; i ++ ) {
const v3 = new Vector3().fromBufferAttribute( position, i );
uvs.push( (v3.x + offset.x) / range.x );
uvs.push( (v3.y + offset.y) / range.y );
}
geometry.setAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( uvs ), 2 ) );
geometry.setAttribute( 'uv2', new THREE.BufferAttribute( new Float32Array( uvs ), 2 ) );
geometry.attributes.uv.needsUpdate = true;
geometry.attributes.uv2.needsUpdate = true;
Yes, you are correct. If you define your own UV coordinates, you must take care for the third dimension. Currently you use only v3.x
and v3.y
. You must also use v3.z
, but the exact modification depends on your specific case:
uvs.push( (v3.x + offset.x) / range.x );
uvs.push( (v3.y + offset.y) / range.y );
I have done similar things, but for another type of geometries. See this example:
https://boytchev.github.io/suica/examples/convex-dynamic.html
The code for calculating 3D UVs is in lines 47-82:
https://github.com/boytchev/suica/blob/e676dd9cc19beacf1822853b418a735c5786f39c/src/suica-convex.js
2 Likes
Thanks, gonna experiment with this!
Could you explain a bit about this code.
Why do you use normal?
You compare normals for each coord dimension and push to uvs two minimal positions.
var max = MAX_X;
if( ny>=nx && ny>=nz ) max = MAX_Y
else
if( nz>=nx && nz>=nx ) max = MAX_Z;
So if we have normal like (0, 1, 0), so we push x and z.
But what if we have (1, 1, 0)? Why do you use exactly this conditions?
For my case I extract the largest component of the normal vector and this defines which coordinates to use for the texture.
Maybe it would be easier to look at a simpler example, which uses a traditional box. The left box uses UV mapping the way you do it – using only X and Y (and you can see the sides of the box are not textured well). The right box uses UV mapping that is different for different sides of the box:
https://codepen.io/boytchev/full/rNZxLLK
1 Like
You are my superhero today! It works, the only thing is to play with dividers. Thanks!
1 Like
Actually it works with rectangular shapes, where faces are parallel to coordinate axis. But for triangular mesh (or custom meshes with non-right angles).
It seems like we need to use something more complex for sides, not just Math.abs(coord) > 0.5.
Any ideas?
As I see it, the triangular shapes are OK. Just change the scaling factor for the sides (it looks like the texture there it extremely stretched vertically).
Only if it is does not work, then have a look at the more complex solution (the one with ny>=nx && ny>=nz
and so on) , that checks what component of the normal vector is the largest, and then it uses the other two coordinates for UV.
If both things do not work, you might need to calculate coordinates manually.
What do you mean by calculating manually?
By using some 3D modeling software (like Blender) where you can manually do UV mapping.
This is not the best way for me tbh, I guess it is possible to calculate all inside method by some formula.
What do you think of this fiddle threejs Box UV Mapping - JSFiddle - Code Playground
If the method shown in that JSFiddle works for your scenario, then you have a solution for your geometries.
The method that I proposed, is rather simplistic. Here is how it looks like with triangular shapes:
In any case, good luck with your project. When you are done, you may show it here. I’d love to see it.
1 Like
Can you show what exactly you changed in order to get this result as on your image please? If it works, it really can be much better
I have modified the pen. There is nothing special. The new pen has function resetUVs
(at line 112) that is used on 4 different objects – a box, a triangle, a reversed triangle and a dodecahedron. It is the same function for all objects.
https://codepen.io/boytchev/full/rNZxLLK
The link that you have found also looks nice. It might provide better results in the general case.
2 Likes