如何在 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>