Перемещение тени вокруг круга

У меня есть круг, который вращается каждые 10 секунд. И я пытаюсь наложить тень, которая направлена ​​к источнику орбиты (источник света), а также с учетом угла камеры.

Тень работает под некоторыми углами, но по мере того, как камера становится более красной или сверху вниз, она начинает выглядеть менее точной, и я понятия не имею, как ее исправить - похоже, сложная математическая проблема, с которой я борюсь чтобы выяснить, как решить.

Это анимация: http://jsfiddle.net/8y2bm88w/

И мой код рисования для тени:

ctx.beginPath();

//rotate shadow with the planet
ctx.translate(originX + obj[i].x, originY + obj[i].y);
ctx.rotate(obj[i].angle); //rotate around origin
ctx.translate(-(originX + obj[i].x), -(originY + obj[i].y));


var offsetX = -(10 * Math.sin(obj[0].angle)); //i feel this is the issue
var offsetY = 0; //this too

ctx.rect(originX + obj[i].x + offsetX, originY + obj[i].y + offsetY - 10, 20, 20);
ctx.fillStyle = 'rgba(213,0,0,0.9)'; //red shadow - easier to see
ctx.fill();

Код имеет больше смысла с помощью JSFiddle, поскольку он помещает код в больший контекст.

Итак, я думаю, что это связано с математикой для переменных offsetX и offsetY, поскольку пользователь меняет угол камеры, необходимый смещению, и меняет способ перемещения тени. Но, это действительно запутывает, как решить.

Ответы

Ответ 1

Вы попадаете в это, потому что ваш obj[i].angle основан на эллипсе coördinates, а не на xy coördinates сверху вниз (на самом деле это то, что вы генерируете все остальное, поэтому нам не нужно делать расчеты для него).

Вам понадобятся оба угла, поэтому я добавил второе свойство для другого угла. Угол эллипса необходим для поворота контекста, поэтому вы можете перпендикулярно вектору от начала координат, если смотреть. Новый угол для расчетов. Вы можете думать о нем как о не-2D-версии.

obj[i].trueAngle = angle;

Теперь у нас есть значение для работы, я предлагаю рисовать полукруг для "темной стороны", а затем добавлять или вырезать из этого региона по мере необходимости, используя кривую по вашему выбору для завершения тени, например

// centre on planet
ctx.translate(originX + obj[i].x, originY + obj[i].y);
ctx.rotate(obj[i].angle - Math.PI / 2);
// semicircle dark side
ctx.arc(0, 0, 12, 0, Math.PI, false);
// path along terminator
var offset_terminator = -20 * Math.sin(obj[0].trueAngle) * (1 - camera.angle);
ctx.bezierCurveTo(-7, offset_terminator, 7, offset_terminator, 12, 0);
//fill
ctx.fillStyle = 'rgba(213,0,0,0.9)'; //red shadow - easier to see
ctx.fill();

Здесь я также сделал тень на пару пикселей больше, чем на планете, я просто играл с числами, пока не нашел некоторые, которые выглядели хорошо.

Обратите внимание, что я устанавливаю вращение, поэтому 0, 0 является средой планеты, а ось x перемещается вдоль терминатора

DEMO