首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >2D Heightmap上的基本(假)光线投射

2D Heightmap上的基本(假)光线投射
EN

Stack Overflow用户
提问于 2011-07-24 10:56:54
回答 3查看 3K关注 0票数 2

基本上,我尝试做的是使用非常基本的光线投射系统对2D高度贴图进行着色,该系统基本上只是检查光线是否在应该对其进行着色之前被拦截。然而,它不能正常工作,我已经在这个问题上绞尽脑汁好几个小时了,所以我想把它交给你们是没有坏处的,因为我认为它可能是如此明显以至于我看不到它,或者是太复杂了,我永远不会去考虑它。

我有一张这样的地图:

光线投射给了我这个(记住这只是调试颜色;红色是光线截取,但在预期的位置之前(所以是阴影),蓝色是光线截取在正确的位置(所以突出显示或只是按原样),黄色意味着在while循环剪切之前该点根本没有光线交互)。

结果应该是背向坡面上的红色和大山后面的区域(阴影)和朝向阳光的坡面上的蓝色(高光)。不应该有任何黄色。因此,这张图像表明,要么所有的光线都击中了错误的位置,要么光线在到达目标之前总是在其他地方相交,这是不可能的。

在这一点上,我高度怀疑问题出在我的trig上。

下面是Ray类:

代码语言:javascript
复制
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);

代码语言:javascript
复制
    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);
    }
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-10-02 23:07:04

我最终在Bresenham's Line Algorithm中使用了一种简单得多的方法来找到截取点;我想它比我尝试的方法要快得多,效率也更高。

票数 2
EN

Stack Overflow用户

发布于 2011-07-24 17:53:00

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

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

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

票数 0
EN

Stack Overflow用户

发布于 2018-01-09 14:58:29

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

参见tex_terrain.js上的function setTerrainShadow()

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6804664

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档