easy-three

create.canvasTexture

create.canvasTexture(proc, props : Object) : Texture

proc - テクスチャを描画するための関数。引数に2Dコンテキストとキャンバスが渡されます。 (デフォルト : () => { })
props - 設定オブジェクト。
  • size (Number | Array) : テクスチャのサイズ (デフォルト : [500, 500])。

キャンバス要素を用いてテクスチャを作成します。

平面以外のオブジェクトにテクスチャを表示する場合に使用します。
平面に表示したい場合は create.canvas を使用してください。

動的な変更

作成したテクスチャは、 update メソッドで動的に変更することができます。
update メソッドの引数に、テクスチャを変更するための関数(引数はコンテキストとキャンバス)を渡すと、テクスチャが更新されます。

const texture = create.canvasTexture();

texture.update((ctx, canvas) => {
  ctx.fillStyle = "red";
  ctx.fillRect(0, 0, canvas.width, canvas.height);
})

作成したテクスチャの userData.context からコンテキストを取得して、直接描画することもできます。
ただし、直接描画した場合は、テクスチャの更新が反映されないため、texture.update() を呼び出す必要があります。
また、userData.canvas からキャンバス要素を取得することもできます。

const texture = create.canvasTexture();
const ctx = texture.userData.context;
const canvas = texture.userData.canvas;

ctx.fillStyle = "red";
ctx.fillRect(0, 0, canvas.width, canvas.height);
texture.update()

コードの例

キャンバステクスチャの作成

描画していない部分を透過するかどうかは、optiontransparent プロパティで設定できます。

const { camera, create, animate, THREE } = init()
camera.position.set(0, 0, 3);

create.ambientLight();
create.directionalLight();
create.sky()

const texture = create.canvasTexture(
  (ctx) => {
    ctx.fillStyle = "red";
    ctx.fillRect(50, 50, 100, 100);
    ctx.fillStyle = "green";
    ctx.fillRect(100, 100, 100, 100);
    ctx.fillStyle = "blue";
    ctx.fillRect(150, 150, 100, 100);
  },
  {
    size: 300,
  }
);

const cube = create.cube({
  size: 1,
  position: [-1, 0, 0],
  option: {
    map: texture,
    transparent: true,
    side: THREE.DoubleSide,
  },
});

const sphere = create.sphere({
  size: 0.7,
  position: [1, 0, 0],
  option: {
    map: texture,
  },
});

animate(({ delta }) => {
  cube.rotation.x += delta;
  cube.rotation.y += delta;
  sphere.rotation.x += delta;
  sphere.rotation.y += delta * 0.7;
});

描画内容の変更1

update メソッドで描画内容を変更することで、テクスチャの見た目を動的に変えることができます。

const { camera, create, THREE, animate } = init()
camera.position.set(0, 0, 2);

create.ambientLight();
create.directionalLight();
create.sky();

const texture = create.canvasTexture();

const cube = create.cube({
  size: 1,
  option: {
    transparent: true,
    map: texture,
    side: THREE.DoubleSide,
  },
});

const draw = (ctx, canvas, frameCount) => {
  ctx.save();
  // clear
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  // translate to center
  ctx.translate(canvas.width / 2, canvas.height / 2);
  // draw rainbow circles
  const maxR = Math.ceil(Math.hypot(canvas.width, canvas.height) / 2);
  for (let r = maxR; r > 0; r -= 10) {
    ctx.beginPath();
    ctx.arc(0, 0, r, 0, Math.PI * 2);
    ctx.fillStyle = `hsla(${(r * 2 - frameCount * 5) % 360}, 100%, 50%, 0.1)`;
    ctx.fill();
  }
  ctx.restore();
}


animate(({delta, frameCount}) => {
  cube.rotation.x += delta;
  cube.rotation.y += delta;
  texture.update((ctx, canvas) => draw(ctx, canvas, frameCount));
});

描画内容の変更2

複数の描画用関数を用意し、update メソッドの引数に渡す関数を切り替えることで、描画内容を変更することができます。

const { camera, create, THREE, animate } = init()
camera.position.set(0, 0, 2);

create.ambientLight();
create.directionalLight();
create.sky();

const texture = create.canvasTexture();

const cube = create.cube({
  size: 1,
  option: {
    transparent: true,
    map: texture,
    side: THREE.DoubleSide,
  },
});

const draw1 = (ctx, canvas) => {
  ctx.save();
  // red frame
  ctx.fillStyle = "red";
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  ctx.clearRect(10, 10, canvas.width - 20, canvas.height - 20);
  // text
  ctx.fillStyle = "black";
  ctx.font = "90px Arial"
  ctx.textAlign = "center";
  ctx.textBaseline = "middle";
  ctx.fillText("easy-three", canvas.width / 2, canvas.height / 2);
  ctx.restore();
}

const draw2 = (ctx, canvas) => {
  ctx.save();
  // clear
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  // translate to center
  ctx.translate(canvas.width / 2, canvas.height / 2);
  // draw rainbow circles
  const maxR = Math.ceil(Math.hypot(canvas.width, canvas.height) / 2);
  for (let r = maxR; r > 0; r -= 10) {
    ctx.beginPath();
    ctx.arc(0, 0, r, 0, Math.PI * 2);
    ctx.fillStyle = `hsl(${(r * 2) % 360}, 100%, 50%)`;
    ctx.fill();
  }
    ctx.restore();
}

texture.update(draw1);

animate(({delta, frameCount}) => {
  cube.rotation.x += delta;
  cube.rotation.y += delta;

  switch (true) {
    case frameCount % 180 === 0:
      texture.update(draw1);
      break;
    case frameCount % 90 === 0:
      texture.update(draw2);
      break;
  }
});