2D高度图上的基本(假)光线投射

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2D高度图上的基本(假)光线投射相关的知识,希望对你有一定的参考价值。

基本上我要做的是使用一个非常非常基本的光线投射系统对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);
    }
答案

我最终使用Bresenham's Line Algorithm更简单的方法来找到拦截点;我想它比我尝试做的方式更快,更有效率。

另一答案

我的猜测是,当你的Direction矢量应用于Position时,它会超过下限(Position.Y > -1),然后才有机会击中表面(Position.Y <= Terrainmap[(int)Position.X, y].Height)。

您可以尝试降低下限,或重新排序if / while测试。

另一个问题可能是Direction Vector与你的高度范围相比太大了。两个相邻像素之间的距离是1,而整个高度差范围包含在范围(-1,1)中。从射线脚轮的角度来看,这给出了非常平坦的表面。当将Direction矢量应用于Position矢量时,在整个长度上采取相对较小的步长,并且在高度上采取相对较大的步长。

另一答案

@Maltor:我其实想评论你自己的答案,但由于我的声誉目前无法做到。

我还使用了bresenham的线方法并将计算时间减少到1/10!

一个正在运行的例子可以在我的github项目TextureGenerator-Online上查看。 terrain工具使用此方法。

Terrain tool

请参阅setTerrainShadow()tex_terrain.js函数

以上是关于2D高度图上的基本(假)光线投射的主要内容,如果未能解决你的问题,请参考以下文章

Libgdx Box2d Raycast无法投射多条光线

使用光线投射防止破折号穿过墙壁

Unity 2D 发送 X 数量的“部队”,这些数量等于我通过光线投射或其他方式向特定目标投入的数量 [关闭]

在three.js中使用正交相机进行不精确的光线投射

光线投射算法中的精度问题

用于游戏开发和其他目的的光线投射教程