Threejs - 仅在网格的一部分中渲染图像纹理的最佳方式
Threejs - Best way to render image texture only in a portion of the mesh
我有一件 T 恤的 .obj,它包含一些网格和材料,我正在使用由内联 svg 提供的 CanvasTexture
为其着色。
现在我应该在特定位置(或多或少在心脏上方)添加徽标,但我很难理解哪种 best/proper 方法(我对3D 图形和 Three.js)。这是我到目前为止尝试过的:
由于我是通过 CanvasTexture
由内联 svg 提供的 T 恤着色,我认为将徽标绘制到特定坐标处的 svg 中会很容易.这确实很容易,但是徽标在 texture/mesh 上未呈现(或以某种方式不可见),尽管它在内联 svg 中可见。所以 CanvasTexture
可能不适用于嵌入式图像(我尝试了 base64 和 URL)
所以,我开始研究更多的 3d "native" 方法,但我还没有找到对我来说真正有意义的方法。我知道 threejs 中有 ShaderMaterial
,我可以用它来选择性地渲染徽标的像素或布料的像素,但这意味着要进行大量复杂的计算才能确定徽标应该放在哪里,但我做不到相信绘制具有特定坐标和大小的简单 JPEG 或 PNG 可能会如此复杂......我一定错过了一个明显的解决方案。
编辑
这是我将图像添加到内联 svg 的方式(上面的选项 1)。
- 将图像添加到内联 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);
- 将内联 svg 绘制为 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'));
}
});
};
- 绘制二维canvas到threejs纹理
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...]
出于某种原因,canvases 不喜欢带有嵌入图像的 SVG,因此对于一个类似的项目,我必须分两步进行,分别渲染 SVG 和图像:
首先,在 canvas 上渲染 SVG,然后在其上渲染图像(在同一个 canvas 上)。
const ctx = canvas.getContext('2d');
ctx.drawImage(imgSVG, 0, 0);
ctx.drawImage(img2, 130, 10, 65, 90);
const texture = new THREE.CanvasTexture(canvas);
我有一件 T 恤的 .obj,它包含一些网格和材料,我正在使用由内联 svg 提供的 CanvasTexture
为其着色。
现在我应该在特定位置(或多或少在心脏上方)添加徽标,但我很难理解哪种 best/proper 方法(我对3D 图形和 Three.js)。这是我到目前为止尝试过的:
由于我是通过
CanvasTexture
由内联 svg 提供的 T 恤着色,我认为将徽标绘制到特定坐标处的 svg 中会很容易.这确实很容易,但是徽标在 texture/mesh 上未呈现(或以某种方式不可见),尽管它在内联 svg 中可见。所以CanvasTexture
可能不适用于嵌入式图像(我尝试了 base64 和 URL)所以,我开始研究更多的 3d "native" 方法,但我还没有找到对我来说真正有意义的方法。我知道 threejs 中有
ShaderMaterial
,我可以用它来选择性地渲染徽标的像素或布料的像素,但这意味着要进行大量复杂的计算才能确定徽标应该放在哪里,但我做不到相信绘制具有特定坐标和大小的简单 JPEG 或 PNG 可能会如此复杂......我一定错过了一个明显的解决方案。
编辑
这是我将图像添加到内联 svg 的方式(上面的选项 1)。
- 将图像添加到内联 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);
- 将内联 svg 绘制为 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'));
}
});
};
- 绘制二维canvas到threejs纹理
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...]
出于某种原因,canvases 不喜欢带有嵌入图像的 SVG,因此对于一个类似的项目,我必须分两步进行,分别渲染 SVG 和图像:
首先,在 canvas 上渲染 SVG,然后在其上渲染图像(在同一个 canvas 上)。
const ctx = canvas.getContext('2d');
ctx.drawImage(imgSVG, 0, 0);
ctx.drawImage(img2, 130, 10, 65, 90);
const texture = new THREE.CanvasTexture(canvas);