Hi ,
I’m Kamal Hinduja based Geneva, Switzerland(Swiss) . Can anyone suggest What’s the correct way to generate UVs for custom geometries?
Thanks, Regards
Kamal Hinduja Geneva, Switzerland
Hi ,
I’m Kamal Hinduja based Geneva, Switzerland(Swiss) . Can anyone suggest What’s the correct way to generate UVs for custom geometries?
Thanks, Regards
Kamal Hinduja Geneva, Switzerland
By definition, a custom geometry can be anything arbitrary.
I’m afraid, there’s no general one rule how to generate UVs for arbitrary geometries. Think for instance of the different ways planes, cylinders and spheres place their UVs.
Make the objects in Blender, texture them and export them as glb files. You can export them without textures and the UVs will be preserved. Then you can load the glb files into your three.js program and (if you want) load and add textures separately.
methods for generating UVs are completely open ended.
It’s a stylistic/function choice based on the needs of the model.
Sometimes you want a texture mapped completely to each face. Sometimes you want them mapped to the whole model like a box unwrap..
Sometimes for more organic forms you need something far more complex, like a true topologically aware unwrap, and there are entire phd’s based on different techniques for these kinds of advanced unwraps.
If you’re only talking about the actual mechanism for adding UVs to a model without them…
You can do something like (pseudocode):
let uvArray = new Float32Array( geometry.attributes.position.count * 2 )
//do your unwrapping algorithm here on uvArray based on the vertices in the position attribute
geometry.setAttribute("uv",new THREE.BufferAttribute( uvArray, 2 ));
Here is a “box uv unwrap” I wrote a while ago that does a simple box unwrap, which is usable for a lot of cases where you just need Something.
import *as THREE from "three"
export function boxUnwrap(geometry) {
const position = geometry.attributes.position;
const normalAttr = geometry.attributes.normal;
if (!position || !normalAttr) return;
const uvArray = new Float32Array(position.count * 2);
const normal = new THREE.Vector3();
const pos = new THREE.Vector3();
for (let i = 0; i < position.count; i++) {
pos.fromBufferAttribute(position, i);
normal.fromBufferAttribute(normalAttr, i).set(Math.abs(normal.x), Math.abs(normal.y), Math.abs(normal.z));
let u = 0, v = 0;
if (normal.x >= normal.y && normal.x >= normal.z) {
u = pos.y; v = pos.z; // X projection (YZ plane)
} else if (normal.y >= normal.x && normal.y >= normal.z) {
u = pos.x; v = pos.z; // Y projection (XZ plane)
} else {
u = pos.x; v = pos.y; // Z projection (XY plane)
}
uvArray[i * 2] = (u - geometry.boundingBox.min.x) / geometry.boundingBox.getSize(new THREE.Vector3()).x;
uvArray[i * 2 + 1] = (v - geometry.boundingBox.min.y) / geometry.boundingBox.getSize(new THREE.Vector3()).y;
}
geometry.setAttribute('uv', new THREE.BufferAttribute(uvArray, 2));
}