I’m putting together my lighting/shadowing engine on my 2D game (coming together great btw). I am having trouble wrapping my head around matrices involved with skewing and stretching.
I am trying to get SpriteBatch transformMatrix to skew, stretch and rotate the shadow of the light blocking obj from the light source. I have working, the transformMatrix rotating around based off the angle of light source to the light blocking obj through matrices. Based off the angle (which I have) and distance (which I have), I am trying to skew and stretch the shadow. I’m not sure how. Here are pieces of what I have working.
// Works for rotating the shadow around the object based off the light source
float RotationAngle = (float)(Math.Atan2(Light.Y - Block.Y, Light.X - Block.X));
if (RotationAngle != 0f)
RotationAngle -= 1.5708f; // 90 degrees in radians
Matrix.CreateTranslation(new Vector3(Position, 0.0f))
* Matrix.CreateTranslation(new Vector3(-Origin, 0.0f))
* Matrix.CreateTranslation(new Vector3(Origin, 0.0f));
Matrices are not my strong area, still trying to learn, they are powerful though. What Matrix function can skew for 2D?
m21 is the x-skew and m12 is the y-skew. IIRC tan(radians) is the skew for a slope of however many radians.
Skewing doesn’t behave like you might expect, given a -1 to 1 rect with 0,0 at the centroid, both edges will skew to opposite directions. So to skew with one edge fixed you usually need an offset matrix and its’ inverse, otherwise you have to account for the skew-offset elsewhere (adding it to your translation).
SkewOffset * Skew * InverseSkewOffset
I am trying to get SpriteBatch transformMatrix to skew, stretch and
rotate the shadow of the light blocking obj from the light source.
I’m assuming you’re talking about those 2d games where there’s a card shadow that stays locked at the feet and sort of rotates/skews around that while always locked at the feet.
You can’t mix rotation and skew for that.
That’s a non-affine transform, trying to do that in an affine matrix is going to be NAN-city.
// innertVert is at `base` position
var outerVertDir = Vector3.Normalize(innerVert - lightOriginOnFloor);
// get position of outer shadow vertex
var outerVertPos = innerVert + outerVertDir * ComputeAzimuthRange(innerVert, objectHeight, lightOrigin3D);
// TODO: compensate for horizon squash
var squashFactor = Vector3.Dot(Vector3.UnitX, outerVertDir);
if (squashFactor > 0.9f)
outerVertPos += Vector3.UnitY * (outerIsRightMost ? global_squashCompensation : -global_squashCompensation);
else if (squashFactor < -0.9f)
outerVertPos += Vector3.UnitY * (outerIsRightMost ? -global_SquashCompensation: global_SquashCompensation);
Even if you clamp angles so you don't hit the perpendicular NAN-explosion you're going to end up with a matrix chain that looks like `LightSideScale * SkewOffset * Skew * InverseSkewOffset * Scale * Rotate * Translate` (though in reality you wouldn't need the rotate) ... which is so many mults that you'd be better off just calculating the right vertex positions anyways - whatever you may lose to a few `sqrt` for normalizing vectors you'll gain in correct height intercepts.
Thanks AcidFaucet for pointing me in the right direction again. Matrices are so new to me still. It never occurred to me to go into the matrix points, I was relying on the Matrix create* options way too much. That makes total sense. If I can’t rotate and skew a matrix because it will toss NAN’s like crazy, I have a few other options for rotation outside of the matrix which I’m okay with.
Awesome, this will get me back on track. Thanks!
@AcidFaucent I am getting closer now. I figured I should draw out the type of skew I’m trying to do since you weren’t sure.
So the light source (yellow) can move around the obj (red). If the light source is under the obj, there would be no skew above it. The more it swings around the obj, the skew grows but hits a peak and starts to lessen to no skew again as the object moves to the left of the object. I hope that makes sense.
I see what you mean by skew behaves different than you expect.
I got it working were I am happy now. It behaves like the image above but on the left and right “no skews” don’t happen. I am not using extreme skewing, so no Tan(). I ended up using Sin()/2 and it applies subtle skewing along with my shadow stretching makes it look really nice. Here is what I ended up doing, just doing pseudo code for others reference if need be.
double AngleRaw = Math.Atan2(Light.Y - Block.Y, Light.X - Block.X);
float Angle = (float)AngleRaw - 1.5708f; // turned via radians to fit monogames xy rotation
Matrix ShadowMatrix = (pseudo-code follows…) Position * -Origin;
float Skew = (float)Math.Sin(Angle) / 2;
ShadowMatrix.M21 -= Skew;
ShadowMatrix = (pseudo-code follows…) Rotate * Origin;
// Also doing Shadow Stretching based of distance of light source and Color transparency calculations based on power of light source vs distance.
Pics or it didn’t happen
Not caring about those East/West horizons makes your life a lot easier.
Hard to see in the vid but the closer you get the darker the shadow gets and stretching gets shorter. Works great for single objects. Running through a forest looks cool. However, a wall or fence shadow is another story. So there will adjusting yet.