いくつかの三角法を使用して、光源で 3D ドットをシミュレートできます。
オンラインデモはこちら
これはあなたができる1つの方法であり、もちろん他にもあります(これが最初に思い浮かびました):
- メインキャンバスにいくつかのドットでグリッドを描画します
- オフスクリーン キャンバスに放射状グラデーションをレンダリングする
- コンポジションモードを変更して、既存のピクセルに何かが描画されるようにします
- 光源までの距離と角度を計算し、角度/距離でオフセットされた各グリッド ポイントにドットを描画します。
これを行うデモのコードを次に示します。
ドットでグリッドを描く
後で各ドットをグラデーションドットで塗りつぶすため、グリッドポイントを1つスキップします。そうしないと、隣接するドットが塗りつぶされます。
/// draw a grid of dots:
for (y = 0; y < ez.width; y += gridSize * 2) {
for (x = 0; x < ez.height; x += gridSize * 2) {
ctx.beginPath();
ctx.arc(x + offset, y + offset, radius, 0, arcStop);
ctx.closePath();
ctx.fill();
}
}
光の「反射」を作る
グラデーション ドットをオフスクリーン キャンバス ( dctx
= dot-context ) に準備します。私はセットアップに easyCanvas を使用しており、中心点が既に計算されたオフスクリーン キャンバスを提供していますが、もちろんこれを手動でセットアップすることもできます。
grd = dctx.createRadialGradient(dot.centerX, dot.centerY, 0,
dot.centerX, dot.centerY, gridSize);
grd.addColorStop(0, '#fff');
grd.addColorStop(0.2, '#777'); // thighten up
grd.addColorStop(1, '#000');
dctx.fillStyle = grd;
dctx.fillRect(0, 0, gridSize, gridSize);
計算する
次に、すべての計算とオフセットを行います。
/// change composition mode
ctx.globalCompositeOperation = 'source-atop';
/// calc angle and distance to light source and draw each
/// dot gradient offset in relation to this
for (y = 0; y < ez.width; y += gridSize) {
for (x = 0; x < ez.height; x += gridSize) {
/// angle
angle = Math.atan2(lightY - y, lightX - x);
//if (angle < 0) angle += 2;
/// distance
dx = lightX - x;
dy = lightY - y;
dist = Math.sqrt(dx * dx + dy * dy);
/// map distance to our max offset
od = dist / maxLength * maxOffset * 2;
if (od > maxOffset * 2) od = maxOffset * 2;
/// now get new x and y position based on angle and offset
offsetX = x + od * Math.cos(angle) - maxOffset * 0.5;
offsetY = y + od * Math.sin(angle) - maxOffset * 0.5;
/// draw the gradient dot at offset
ctx.drawImage(dot.canvas, x + offsetX, y + offsetY);
}
}
影
影については、既に描画されたピクセルの外側destination-over
に描画する合成モードを使用しているときに、オフセットを逆にするだけです。
/// Shadow, same as offsetting light, but in opposite
/// direction and with a different composite mode
ctx.globalCompositeOperation = 'destination-over';
for (y = 0; y < ez.width; y += gridSize) {
for (x = 0; x < ez.height; x += gridSize) {
/// angle
angle = Math.atan2(lightY - y, lightX - x);
//if (angle < 0) angle += 2;
/// distance
dx = lightX - x;
dy = lightY - y;
dist = Math.sqrt(dx * dx + dy * dy);
/// map distance to our max offset
od = dist / maxLength * maxOffset * 2;
if (od > maxOffset * 4) od = maxOffset * 4;
/// now get new x and y position based on angle and offset
offsetX = x - od * Math.cos(angle) + gridSize * 0.5;
offsetY = y - od * Math.sin(angle) + gridSize * 0.5;
ctx.beginPath();
ctx.arc(x + offsetX, y + offsetY, radius, 0, arcStop);
ctx.fill();
}
}
もちろん、これはすべて 1 つのループ ペアに最適化できますが、概要を説明するためにコードを分けています。
追加
デモでは、マウス トラッキングを追加して、マウスが光源になり、マウスを動かしながらドット反射の変化を確認できるようにしました。最高のパフォーマンスを得るには、Chrome を使用してください。
必要に応じて、使用している値を縮小するか、大きなオフスクリーン キャンバスに描画し、それを使用drawImage
してメイン キャンバスに縮小します。