39otrebla
39otrebla

Reputation: 1442

Threejs - Best way to render image texture only in a portion of the mesh

I have the .obj of a T-Shirt, it contains a few meshes and materials and I'm coloring it using a CanvasTexture fed by an inline svg.

Now I should add a logo at a specific location (more or less above the heart), but I'm struggling to understand which is the best/proper way of doing it (I'm quite new to 3D graphics and Three.js). This is what I tried so far:

EDIT

Here is how I'm adding the image to the inline svg (option 1 above).

  1. Add the image to the inline svg
const groups = Array.from(svg.querySelectorAll('g'));
// this is the "g" tag where I want to add the logo into
const targetGroup = groups.find((group: SVGGElement) => group.getAttribute('id') === "logo_placeholder");

const image = document.createElement('image');
image.setAttribute('width', '64');
image.setAttribute('height', '64');
image.setAttribute('x', '240');
image.setAttribute('y', '512');
image.setAttribute('xlink:href', `data:image/png;base64,${base64}`);

targetGroup.appendChild(image);
  1. Draw inline svg to 2d canvas
static drawSvgToCanvas = async (canvas: HTMLCanvasElement, canvasSize: TSize, svgString: string) => {
  return new Promise((resolve, reject) => 
    canvas.width = canvasSize.width;
    canvas.height = canvasSize.height;

    const ctx = canvas.getContext('2d');

    const image = new Image(); // eslint-disable-line no-undef
    image.src = `data:image/svg+xml;base64,${btoa(svgString)}`;
    image.onload = () => {
      if (ctx) {
        ctx.drawImage(image, 0, 0);
          resolve();
        } else {
          reject(new Error('2D context is not set on canvas'));
        }
      };

    image.onerror = () => {
      reject(new Error('Could not load svg image'));
    }
 });
};
  1. Draw 2d canvas to threejs Texture
const texture = new Three.CanvasTexture(canvas);
texture.mapping = Three.UVMapping; // it's the default
texture.wrapS = Three.RepeatWrapping;
texture.wrapT = Three.RepeatWrapping; // it's the default
texture.magFilter = Three.LinearFilter; // it's the default
texture.minFilter = Three.LinearFilter;
texture.needsUpdate = true;

[...add texture to material...]

Upvotes: 0

Views: 953

Answers (1)

Sphinxxx
Sphinxxx

Reputation: 13047

For some reason, canvases don't like SVGs with embedded images, so for a similar project I had to do this in two steps, rendering the SVG and the image separately:

First, render the SVG on the canvas, and then render the image on top of that (on the same canvas).

const ctx = canvas.getContext('2d');
ctx.drawImage(imgSVG, 0, 0);
ctx.drawImage(img2, 130, 10, 65, 90);

const texture = new THREE.CanvasTexture(canvas);

Example: https://jsfiddle.net/0f9hm7gx/

Upvotes: 0

Related Questions