I'm trying to shade a 2D heightmap using basic raycasting that checks if the ray is intercepted before it should shade it. However it's not working.
Map :
And the raycasting is giving me this:
The result should be red on back-facing slopes and areas behind large mountains (shadows) and blue on sun-facing slopes (highlights). There should not be any yellow. So this image indicates that either all rays are hitting the wrong place, or are intersected always somewhere else, which is impossible. I suspect the problem is with my trig.
Ray
class:
class Ray
{
public Vector2 Position;
public Vector2 Direction; // Think in XZ coordinates for these (they are on a perpendicular plane to the heightmap)
// Angle is angle from horizon (I think), and height is height above zero (arbitrary)
public float Angle, Height;
private TerrainUnit[,] Terrainmap;
private float U, V;
public Ray(ref TerrainUnit[,] Terrainmap, float height, float angle)
{
this.Terrainmap = Terrainmap;
this.Angle = angle;
this.Height = this.V = height;
// Create new straight vector
this.Direction = new Vector2(0, 1);
// Rotate it to the values determined by the angle
this.Direction = Vector2.Transform(Direction, Matrix.CreateRotationX(Angle));
//this.Direction = new Vector2((float)Math.Sin(angle), -(float)Math.Cos(angle));
// Find the horizontal distance of the origin-destination triangle
this.U = V / (float)Math.Tan(Angle);
// Bleh just initialize the vector to something
this.Position = new Vector2(U, V);
}
public void CastTo(int x, int y)
{
// Get the height of the target terrain unit
float H = (float)Terrainmap[x, y].Height;
// Find where the ray would have to be to intersect that terrain unit based on its angle and height
Position = new Vector2(x - U, H + V);
float Z = 1000 * (float)Terrainmap[0, y].Height;
// As long as the ray is not below the terrain and not past the destination point
while (Position.Y > Z && Position.X <= x)
{
// If the ray has passed into terrain bounds update Z every step
if (Position.X > 0) Z = 1000 * (float)Terrainmap[(int)Position.X, y].Height;
Position.X += Direction.X;
Position.Y += Direction.Y;
}
Terrainmap[x, y].TypeColor = Color.Yellow;
if ((int)Position.X == x) Terrainmap[x, y].TypeColor = Color.Blue;
else Terrainmap[x, y].TypeColor = Color.Red;
}
}
Function that is casting each ray and how I am calling that:
if (lighting) CastSunRays(1f, MathHelper.PiOver4);
private void CastSunRays(float height, float angle)
{
Ray ray = new Ray(ref Terrainmap, height, angle);
for (int x = 0; x < Width; x++)
for (int y = 0; y < Height; y++)
ray.CastTo(x, y);
}
I ended up using a much simpler approach with Bresenham's Line Algorithm to find the intercept point; I imagine it's much faster and more efficient than the way I was trying to do it would have been.