Uv-map random offset

Hi everyone!
I need to randomize the material.map.offset of a preloaded obj. I can’t just clone the texture for performance reasons and can’t call onBeforeRender beacuse object is already rendered. I’m wondering if there’s a way to do that by changing the UV coordinates.
My objects are simple 12 faces boxes, think of those like panels (doors, sides, shelves) of a wardrobe.
So:

  • I load the model with MTLLoader and add it to a group;
  • I traverse the group to find which is which (based on their dimensions);
  • then apply a mapped material;
  • mess a bit the uv map in order to have the correct ratio and edges;

as a result I’ve got a model with all the textures starting from the bottom left corner, giving an ugly overall impression.
Ah, I can’t use Blender to change the uvmap before: I’m trying to automate the visualization process directly from a SW that outputs non mapped OBJs.

Can you help me?

You could make modifications to your UVs in the shader. See this example in Shadertoy applies a sin() calculation to the x-value of the UVs. The only thing is that you’d need to learn a bit of GLSL and then replace the shader code in the material using .onBeforeCompile()

https://www.shadertoy.com/view/MsX3DN

2 Likes

Thank you @marquizzo.
I was afraid coding shader was the only chance to solve my issue. Now I can’t help but learning GLSL. I’ll let you know how is it going.

Thanks again

Ok, I found a way out avoiding again to learn GLSL :sweat_smile:
just after loading the obj, I traverse it and assign to every object inside it a custom attribute containing a random number:

obj.traverse(child => {
	if (child instanceof THREE.Mesh) {	
		child.castShadow = true;
		child.receiveShadow = true;
		child.userData.rndmOffset = Math.random();
		group.add(panel);
        }
});

then i wrote a function to set up the uv map (obviously you need to create material, import texture and assign material to obj first.
I understand that to offset a texture map working on the uvs of a geometry is as simple as adding the same number to all uv vertex.
Imagine you have a box and you want to custom the uv map; you can traverse an object and set the UV for one or more face at the time;
Here’s an example:

scene.traverse(child => {
    let geom = child.geometry;
    if (child instanceof THREE.Mesh) {
        let uvs = geom.faceVertexUvs;
        let la = 0; // layer
        let f1 = [0,2,8,10]; // face: 0=dx 2=sx 8=top 10=bottom
        let f2 = [1,3,9,11]; // face: 1=dx 3=sx 9=top 11=bottom
        for (var i = f1.length - 1; i >= 0; i--) {
            uvs[la][f1[i]][0].x = 0.5;
            uvs[la][f1[i]][0].y = 0;
            uvs[la][f1[i]][1].x = 0;
            uvs[la][f1[i]][1].y = 0;
            uvs[la][f1[i]][2].x = 0.5;
            uvs[la][f1[i]][2].y = 1;

            uvs[la][f2[i]][0].x = 0;
            uvs[la][f2[i]][0].y = 0;
            uvs[la][f2[i]][1].x = 0;
            uvs[la][f2[i]][1].y = 1;
            uvs[la][f2[i]][2].x = 0.5;
            uvs[la][f2[i]][2].y = 1;
        }
        geom.uvsNeedUpdate = true;
    }
});

in this piece of code f1 is an array representing the first half of a face (a triangle) and f2 the second half. The goal is to have right, left, top, bottom faces (from camera perspective) mapped the same way.
You can of course change numerical values (0, 0.5) with variables and shorten the code using vector2.

Now if you want to offset that texture randomly for every children, what you have to do is to add child.userData.rndmOffset to every value, like this:

for (var i = f1.length - 1; i >= 0; i--) {
    uvs[la][f1[i]][0].x = 0.5 + child.userData.rndmOffset;
    uvs[la][f1[i]][0].y = 0 + child.userData.rndmOffset;
    uvs[la][f1[i]][1].x = 0 + child.userData.rndmOffset;
    uvs[la][f1[i]][1].y = 0 + child.userData.rndmOffset;
    uvs[la][f1[i]][2].x = 0.5 + child.userData.rndmOffset;
    uvs[la][f1[i]][2].y = 1 + child.userData.rndmOffset;

    uvs[la][f2[i]][0].x = 0 + child.userData.rndmOffset;
    uvs[la][f2[i]][0].y = 0 + child.userData.rndmOffset;
    uvs[la][f2[i]][1].x = 0 + child.userData.rndmOffset;
    uvs[la][f2[i]][1].y = 1 + child.userData.rndmOffset;
    uvs[la][f2[i]][2].x = 0.5 + child.userData.rndmOffset;
    uvs[la][f2[i]][2].y = 1 + child.userData.rndmOffset;
}

in this case you have an offset equal on both axis and based on a random number generated earlier.

I hope that this can be of any help to anyone.
Thank you.