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

发布于 2024-11-25 22:24:12 字数 3047 浏览 2 评论 0 原文

我正在尝试使用基本的光线投射来对 2D 高度图进行着色,该光线投射会检查光线在对其进行着色之前是否被拦截。但是它不起作用。

地图 : Map

光线投射给了我这个:

  • 红色是光线拦截,但在预期位置之前(因此着色),
  • 蓝色是光线在正确的位置进行拦截(因此突出显示或按原样),
  • 黄色表示在 while 循环剪切之前没有光线交互。

Badx2

结果在背向斜坡和大山(阴影)后面的区域应为红色,在朝阳斜坡上结果应为蓝色(重点)。不应该有任何黄色。因此,该图像表明,要么所有光线都击中了错误的位置,要么总是在其他地方相交,这是不可能的。我怀疑问题出在我的三角函数上。

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);
}

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 :
Map

And the raycasting is giving me this:

  • Red is ray interception, but before intended position (so shading),
  • blue is ray interception in the correct place (so highlights or just as-is), and
  • yellow means no ray interaction before the while loop cut-out.

Badx2

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);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

爱你不解释 2024-12-02 22:24:12

我最终使用了一种更简单的方法,即 Bresenham 直线算法 来找到截取点;我想这比我尝试的方式更快、更高效。

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.

国际总奸 2024-12-02 22:24:12

我的猜测是,当您的 Direction 向量应用于 Position 时,它超出了之前的下限 (Position.Y > -1)有机会撞击地面 (Position.Y <= Terrainmap[(int)Position.X, y].Height)。

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

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

My guess is that when your Direction vector is applied to Position, it oversteps the lower limit (Position.Y > -1) before it has a chance to hit the surface (Position.Y <= Terrainmap[(int)Position.X, y].Height).

You could try to decrease the lower limit, or re-order your if/while tests.

Another problem might be that the Direction Vector is too large in comparison to your height-range. The distance between two neighboring pixels is 1, while the whole range of height differences is contained in the range (-1,1). This gives a very flat surface from the ray-casters point of view. When the Direction vector is applied to the Position vector is takes a relatively small step over the length, and a relatively large step over the height.

许仙没带伞 2024-12-02 22:24:12

我还使用了 Bresenham 直线算法,并将计算时间减少到 1/10!可以在我的 GitHub 项目 TextureGenerator-Online 中查看示例。地形工具使用这种方法。

地形工具

请参阅 setTerrainShadow() rel="nofollow noreferrer">tex_terrain.js

I also used Bresenham's line algorithm and decreased calculation time to 1/10! Example can be viewed at my GitHub project TextureGenerator-Online. The terrain tool uses this approach.

Terrain tool

See function setTerrainShadow() at tex_terrain.js.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文