I have a circle which orbits every 10 seconds. And i am trying to cast a shadow which is angled towards the orbit origin (the light source) whilst also taking into account the camera angle as well.
The shadow works for some angles but as the camera goes more edge on or more top down, it starts to look less accurate and I have no idea how to correct for it - it seems like a complicated math issue that I am struggling to figure out how to solve.
This is the animation: http://jsfiddle.net/8y2bm88w/
And my draw code for the shadow:
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();
The code makes more sense via the JSFiddle as it puts the code into more context.
So i think this is to do with the maths for offsetX
and offsetY
variables, as the user changes the camera angle the offset's need to accommodate and change the way in which the shadow moves. But, this is really confusing how to solve.
You're hitting this because your obj[i].angle
is based on the ellipse coördinates and not the x-y coördinates of the top-down view (which is actually what you're generating everything else from so we don't need to do calculations for it).
You will need both angles, so I've added a second property for the other angle. The ellipse angle is needed for the rotation of the context so you can be perpendicular to the vector from the origin as viewed. The new angle is for the calculations. You can think of it as a non-2D-mapped version.
obj[i].trueAngle = angle;
Now we have the value to work with, I suggest drawing a semi-circle for the "dark side" and then adding to or cutting out from this region as required using a curve of your choice to complete the shadow, e.g.
// 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();
Here I also made the shadow a couple of px bigger than the planet, I just played with numbers until I found some that looked good.
Please note I set up rotation so 0, 0
is the middle of the planet and the x-axis moves along the terminator
DEMO
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With