Базовое (поддельное) Raycasting на 2D-карте высот - PullRequest
2 голосов
/ 24 июля 2011

По сути, я пытаюсь закрасить 2D карту высот, используя очень простую систему лучевого вещания, которая в основном просто проверяет, перехватывается ли луч, прежде чем его затенять.Однако это не работает должным образом, и я уже несколько часов бьюсь головой об этом, поэтому я подумал, что не повредит передать это вам, ребята, потому что я думаю, что это, возможно, что-то настолько ослепительно очевидное, что я не будувижу это или так сложно, что я никогда не буду оборачиваться вокруг него.

У меня есть такая карта: Map

И радиовещание дает мне это (имейте в виду, что этопросто отладка цветов, красный - это перехват лучей, но перед намеченным положением (таким образом, затенение), синий будет перехватывать лучи в правильном месте (таким образом, выделение или просто как есть), а желтый означает, что точка вообще не имела взаимодействия лучей допока петля вырезана).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);
    }

Ответы [ 3 ]

2 голосов
/ 02 октября 2011

В итоге я использовал гораздо более простой подход с Алгоритмом линии Брезенхема , чтобы найти точку пересечения;Я полагаю, что это гораздо быстрее и эффективнее, чем то, что я пытался сделать.

0 голосов
/ 09 января 2018

@ Maltor: Я на самом деле хотел прокомментировать ваш собственный ответ, но из-за своей репутации на данный момент не могу.

Я также использовал линейный подход bresenham и сократил время расчета до 1/10!*

Пример этого можно посмотреть в моем проекте на github TextureGenerator-Online .Инструмент ландшафта использует этот подход.

Terrain tool

См. Функцию setTerrainShadow() в tex_terrain.js

0 голосов
/ 24 июля 2011

Я предполагаю, что когда ваш Direction вектор применен к Position, он переступает нижний предел (Position.Y > -1), прежде чем у него появляется шанс попасть на поверхность (Position.Y <= Terrainmap[(int)Position.X, y].Height).

Вы можете попытаться уменьшить нижний предел или изменить порядок ваших if / while испытаний.

Другая проблема может заключаться в том, что вектор Direction слишком велик по сравнению с диапазоном высот,Расстояние между двумя соседними пикселями равно 1, а весь диапазон перепадов высот содержится в диапазоне (-1,1).Это дает очень плоскую поверхность с точки зрения заклинателей лучей.Когда вектор Direction применяется к вектору Position, выполняется относительно небольшой шаг по длине и относительно большой шаг по высоте.

...