Cannot perform UV mapping on a custom shape

Hi everyone,

I’m having trouble performing UV mapping on a custom shape created using four curves (top, bottom, left, and right) in Three.js. Initially, I had success with just the top and bottom curves, but integrating the left and right curves to form a closed shape is proving challenging.

I have created curves using cubic bezier curves. I also added shader material and added image texture. I want to distort the image by dragging the control points. Control points are the points that are defined in the curves. It was doing fine when I used only top and bottom curves and did uv mapping and defined indices. But when I added left and right curves then uvs and indices are not mapping perfectly. I mean I can not control four curves at the same time.

Here’s what I have so far:

const sharedPointTopLeft = new THREE.Vector3(-100, 200, 0);
const sharedPointTopRight = new THREE.Vector3(200, 200, 0);
const sharedPointBottomRight = new THREE.Vector3(200, -200, 0);
const sharedPointBottomLeft = new THREE.Vector3(-100, -200, 0);

const sharedPointTopCenter = new THREE.Vector3(50, 200, 0);
const sharedPointBottomCenter = new THREE.Vector3(50, -200, 0);
const sharedPointLeftCenter = new THREE.Vector3(-100, 0, 0);
const sharedPointRightCenter = new THREE.Vector3(200, 0, 0);

const curvesLeft = [
  new THREE.CubicBezierCurve3(
    sharedPointTopLeft,
    new THREE.Vector3(-100, 150, 0),
    new THREE.Vector3(-100, 100, 0),
    sharedPointLeftCenter
  ),
  new THREE.CubicBezierCurve3(
    sharedPointLeftCenter,
    new THREE.Vector3(-100, -50, 0),
    new THREE.Vector3(-100, -100, 0),
    sharedPointBottomLeft
  ),
];

const curvesTop = [
  new THREE.CubicBezierCurve3(
    sharedPointTopLeft,
    new THREE.Vector3(-50, 200, 0),
    new THREE.Vector3(0, 200, 0),
    sharedPointTopCenter
  ),
  new THREE.CubicBezierCurve3(
    sharedPointTopCenter,
    new THREE.Vector3(100, 200, 0),
    new THREE.Vector3(150, 200, 0),
    sharedPointTopRight
  ),
];

const curvesRight = [
  new THREE.CubicBezierCurve3(
    sharedPointTopRight,
    new THREE.Vector3(200, 150, 0),
    new THREE.Vector3(200, 100, 0),
    sharedPointRightCenter
  ),
  new THREE.CubicBezierCurve3(
    sharedPointRightCenter,
    new THREE.Vector3(200, -50, 0),
    new THREE.Vector3(200, -100, 0),
    sharedPointBottomRight
  ),
];

const curvesBottom = [
  new THREE.CubicBezierCurve3(
    sharedPointBottomLeft,
    new THREE.Vector3(-50, -200, 0),
    new THREE.Vector3(0, -200, 0),
    sharedPointBottomCenter
  ),
  new THREE.CubicBezierCurve3(
    sharedPointBottomCenter,
    new THREE.Vector3(100, -200, 0),
    new THREE.Vector3(150, -200, 0),
    sharedPointBottomRight
  ),
];

function updateShaderGeometry() {

  pathTop = new THREE.CurvePath();
  curvesTop.forEach((curve) => pathTop.add(curve));

  pathBottom = new THREE.CurvePath();
  curvesBottom.forEach((curve) => pathBottom.add(curve));

  pathLeft = new THREE.CurvePath();
  curvesLeft.forEach((curve) => pathLeft.add(curve));

  pathRight = new THREE.CurvePath();
  curvesRight.forEach((curve) => pathRight.add(curve));

  let curvePointsTop = pathTop.getPoints(1000);
  let curvePointsBottom = pathBottom.getPoints(1000).reverse(); 
  let curvePointsLeft = pathLeft.getPoints(1000).reverse();
  let curvePointsRight = pathRight.getPoints(1000);

  const vertices = [];
  const uvs = [];
  const indices = [];

  curvePointsTop.forEach((point, index) => {
    vertices.push(point.x, point.y, 0);
    uvs.push(index / (curvePointsTop.length - 1), 1); 

    const bottomPoint = curvePointsBottom[index];
    vertices.push(bottomPoint.x, bottomPoint.y, 0);
    uvs.push(index / (curvePointsBottom.length - 1), 0); 
  });

  curvePointsLeft.forEach((point, index) => {
    vertices.push(point.x, point.y, 0);
    uvs.push(0, 1 - index / (curvePointsLeft.length - 1));

    const rightPoint = curvePointsRight[index];
    vertices.push(rightPoint.x, rightPoint.y, 0);
    uvs.push(1, 1 - index / (curvePointsRight.length - 1));
  });

  for (let i = 0; i < curvePointsTop.length - 1; i++) {
    const a = i * 2;
    const b = i * 2 + 1;
    const c = i * 2 + 2;
    const d = i * 2 + 3;
    indices.push(a, b, d);
    indices.push(a, d, c);
  }


  const offset = curvePointsTop.length * 2;
  for (let i = 0; i < curvePointsLeft.length - 1; i++) {
    const a = offset + i * 2;
    const b = offset + i * 2 + 1;
    const c = offset + i * 2 + 2;
    const d = offset + i * 2 + 3;
    indices.push(a, b, d);
    indices.push(a, d, c);
  }

  shaderGeometry.setAttribute(
    "position",
    new THREE.Float32BufferAttribute(vertices, 3)
  );
  shaderGeometry.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2));
  shaderGeometry.setIndex(indices);
  shaderGeometry.attributes.position.needsUpdate = true;
  shaderGeometry.attributes.uv.needsUpdate = true;
  shaderGeometry.index.needsUpdate = true;

  shaderMesh.geometry.dispose();
  shaderMesh.geometry = shaderGeometry;
  shaderMesh.position.z = 0.5;
  shaderMesh.geometry.computeVertexNormals();
}

First one is the initial stage and second one when I am trying to distort image.


My hypothesis is that to have a good deformation, you need to adjust the coordinates of some internal points too. You can check whether this is the case by removing the texture and drawing just the wireframe of the mesh. Looking at the snapshot, my guess is that when you deform the contour lines, the interior fragments got overlapped.

Thank you for the comment :smiley:,The issue has been fixed. I was not calculating uvs, indices and vertices correctly.