基本上我要做的是使用一个非常非常基本的光线投射系统来遮蔽2D高度图,该系统基本上只是检查光线是否在被遮挡之前被拦截。然而它没有正常工作,我现在已经敲了好几个小时了,所以我认为将它转交给你们是不会有什么伤害的,因为我觉得它可能是非常明显的,我不会看到它太复杂了,我永远不会把它包裹起来。
我有这样的地图:
光线投射给了我这个(请记住它只是调试颜色;红色是光线拦截,但在预定位置之前(如此着色),蓝色将在正确的位置进行光线拦截(因此高光或仅仅是原样) ),黄色表示在while循环切除之前,该点根本没有光线相互作用)。
结果应该是背面斜坡上的红色和大山(阴影)后面的区域和面向太阳的斜坡上的蓝色(高光)。不应该有任何黄色。所以这个图像表明要么所有的光线都到达了错误的位置,要么光线在到达目标之前总是在其他地方相交,这是不可能的。
此时我非常怀疑问题出在我的触发上。
这是Ray类:
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;
}
}
同样正如形式一样,投射每条光线的函数以及我如何调用它:
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);
}
答案 0 :(得分:2)
我最终使用更简单的方法Bresenham's Line Algorithm来找到拦截点;我想它比我试图做的更快更有效率。
答案 1 :(得分:0)
我的猜测是,当Direction
向量应用于Position
时,它会超越下限(Position.Y > -1
),然后才有可能触及表面(Position.Y <= Terrainmap[(int)Position.X, y].Height
})。
您可以尝试降低下限,或重新订购if
/ while
次测试。
另一个问题可能是Direction
Vector与您的身高范围相比太大了。两个相邻像素之间的距离是1,而整个高度差范围包含在范围(-1,1)中。从射线脚轮的角度来看,这给出了非常平面。将Direction
向量应用于Position
向量时,在整个长度上采取相对较小的步骤,并在高度上迈出相对较大的一步。
答案 2 :(得分:0)
@Maltor:我其实想评论你自己的答案,但由于我的声誉目前无法做到。
我还使用了bresenham的线方法并将计算时间减少到1/10!
可以在我的github项目TextureGenerator-Online中查看其运行示例。 terrain工具使用这种方法。
上的功能setTerrainShadow()