three.jsで背景が透明なcanvasからCanvasTextureを作成したときに、canavs内のオブジェクトと透明な背景の境界部分にエッジ上のアーティファクトが生じる場合の対処方法です。
具体例として以下のようなコードを見ていきます。このコードではCanvas 2Dで透明な背景に白色で四角形と文字をcanvasに描き、そのcanvasからCanvasTextureを生成して平面のメッシュに張り付けています。マテリアルのtransparent
をtrue
にしているので、Cavnas 2Dで透明にしている部分はthree.jsの背景色に設定している薄紫色が見えるようになっています。
const width = 640;
const height = 480;
const scene = new THREE.Scene();
scene.background = new THREE.Color('rgb(240, 230, 255)');
const camera = new THREE.PerspectiveCamera(30, width / height, 0.1, 100);
camera.position.z = 2.0;
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
const canvas2d = document.createElement('canvas');
canvas2d.width = 512;
canvas2d.height = 512;
const context2d = canvas2d.getContext('2d');
context2d.fillStyle = 'rgb(255, 255, 255)';
context2d.fillRect(10, 10, 400, 130);
context2d.fillRect(200, 400, 250, 70);
context2d.font = '100px sans-serif';
context2d.fillText('Hello', 10, 250);
context2d.fillText('World!', 120, 340);
const texture = new THREE.CanvasTexture(canvas2d);
const geometry = new THREE.PlaneBufferGeometry();
const material = new THREE.MeshBasicMaterial({
map: texture,
transparent: true,
depthWrite: false,
});
scene.add(new THREE.Mesh(geometry, material));
renderer.render(scene, camera);
このコードの描画結果は以下のようになり、Canvas 2Dで描いたオブジェクト(四角形と文字)と背景の境界部分に灰色のエッジが生じています。
なぜこのようなアーティファクトが生じるかというと、canvasはPremultiplied Alphaという形式で取得されるにも関わらず通常のテクスチャと同じように使用しているためのようです。
- Alpha Edge Quality of Canvas-based Textures · Issue #1864 · mrdoob/three.js · GitHub
- コンポジターに必要なアルファチャンネルの知識(後編) - コンポジゴク
- WebGL and Alpha
対処方法としては、CanvasTextureをPremultiplied Alphaと認識されるようにして、マテリアルのアルファブレンディングの設定をそれに合わせたものにします。具体的には次のように設定するとよさそうです。
const texture = ...
texture.premultiplyAlpha = true;
const material = ...
material.blending = THREE.CustomBlending;
material.blendSrc = THREE.OneFactor;
material.blendDst = THREE.OneMinusSrcAlphaFactor;
material.blendEquation = THREE.AddEquation;