如何在 THREE.js 中的 Image-Loaded 纹理上获得 Clamp-To-Border 效果?
How can I get a Clamp-To-Border effect on an Image-Loaded Texture in THREE.js?
我的场景中有一个平面,图像加载到纹理上,据我所知,纹理没有 Clamp-To-Border 选项,只有 Clamp-To-Edge,重复包裹,和镜像环绕。
这是一张带有默认 ClampToEdge 效果的图像。
这是我想要实现的目标。
我能想到的一个解决方案是为图像添加白色边框,这将使整个部分变白,但我不知道如何拍摄图像并为其应用白色边框。
图像是通过 base64 字符串加载的,也许有办法向该字符串添加白色边框?
非常感谢任何建议,谢谢!
最明显的解决方案就是将图像加载到图像编辑器中,将其在每个方向上扩大 1 个像素并添加边框颜色,将其保存回来,使用带边框的新图像。
第二个最明显的解决方案是将您的图像绘制成 canvas,宽高 2 像素。
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = img.width + 2;
canvas.height = img.height + 2;
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'white'; // border color
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 1, 1);
// now make a texture using `new THREE.CanvasTexture(canvas)`
};
img.src = urlForImage;
示例:
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 5;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 2;
const scene = new THREE.Scene();
const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
const cubes = []; // just an array we can use to rotate the cubes
// create a usable texture to start. We'll update it when the image loads
const imgCanvas = document.createElement('canvas');
const texture = new THREE.CanvasTexture(imgCanvas);
texture.minFilter = THREE.LinearFilter;
texture.wrapS = THREE.ClampToEdgeWrapping;
texture.wrapT = THREE.ClampToEdgeWrapping;
texture.offset.set(-0.5, -0.5);
texture.repeat.set(2, 2);
const img = new Image();
img.onload = () => {
imgCanvas.width = img.width + 2;
imgCanvas.height = img.height + 2;
const ctx = imgCanvas.getContext('2d');
ctx.fillStyle = 'white'; // border color
ctx.fillRect(0, 0, imgCanvas.width, imgCanvas.height);
ctx.drawImage(img, 1, 1);
texture.needsUpdate = true;
};
img.crossOrigin = 'anonymous'; // only needed if image is from another origin
img.src = 'https://threejsfundamentals.org/threejs/resources/images/wall.jpg';
const material = new THREE.MeshBasicMaterial({
map: texture,
});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
cubes.push(cube); // add to our list of cubes to rotate
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
cubes.forEach((cube, ndx) => {
const speed = .2 + ndx * .1;
const rot = time * speed;
cube.rotation.x = rot;
cube.rotation.y = rot;
});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
body {
margin: 0;
}
#c {
width: 100vw;
height: 100vh;
display: block;
}
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r112/build/three.min.js"></script><canvas id="c"></canvas>
我的场景中有一个平面,图像加载到纹理上,据我所知,纹理没有 Clamp-To-Border 选项,只有 Clamp-To-Edge,重复包裹,和镜像环绕。
这是一张带有默认 ClampToEdge 效果的图像。
这是我想要实现的目标。
我能想到的一个解决方案是为图像添加白色边框,这将使整个部分变白,但我不知道如何拍摄图像并为其应用白色边框。
图像是通过 base64 字符串加载的,也许有办法向该字符串添加白色边框?
非常感谢任何建议,谢谢!
最明显的解决方案就是将图像加载到图像编辑器中,将其在每个方向上扩大 1 个像素并添加边框颜色,将其保存回来,使用带边框的新图像。
第二个最明显的解决方案是将您的图像绘制成 canvas,宽高 2 像素。
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = img.width + 2;
canvas.height = img.height + 2;
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'white'; // border color
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 1, 1);
// now make a texture using `new THREE.CanvasTexture(canvas)`
};
img.src = urlForImage;
示例:
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 5;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 2;
const scene = new THREE.Scene();
const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
const cubes = []; // just an array we can use to rotate the cubes
// create a usable texture to start. We'll update it when the image loads
const imgCanvas = document.createElement('canvas');
const texture = new THREE.CanvasTexture(imgCanvas);
texture.minFilter = THREE.LinearFilter;
texture.wrapS = THREE.ClampToEdgeWrapping;
texture.wrapT = THREE.ClampToEdgeWrapping;
texture.offset.set(-0.5, -0.5);
texture.repeat.set(2, 2);
const img = new Image();
img.onload = () => {
imgCanvas.width = img.width + 2;
imgCanvas.height = img.height + 2;
const ctx = imgCanvas.getContext('2d');
ctx.fillStyle = 'white'; // border color
ctx.fillRect(0, 0, imgCanvas.width, imgCanvas.height);
ctx.drawImage(img, 1, 1);
texture.needsUpdate = true;
};
img.crossOrigin = 'anonymous'; // only needed if image is from another origin
img.src = 'https://threejsfundamentals.org/threejs/resources/images/wall.jpg';
const material = new THREE.MeshBasicMaterial({
map: texture,
});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
cubes.push(cube); // add to our list of cubes to rotate
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
cubes.forEach((cube, ndx) => {
const speed = .2 + ndx * .1;
const rot = time * speed;
cube.rotation.x = rot;
cube.rotation.y = rot;
});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
body {
margin: 0;
}
#c {
width: 100vw;
height: 100vh;
display: block;
}
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r112/build/three.min.js"></script><canvas id="c"></canvas>