Question

En fait ce que je suis en train de faire est l'ombre d'un heightmap 2D à l'aide d'un système de raycasting très très basique qui fondamentalement juste vérifie si le rayon est intercepté avant qu'il devrait être à l'ombre il. Cependant, il ne fonctionne pas correctement et je l'ai été me cogner la tête pendant plusieurs heures maintenant ce donc je pensais qu'il ne pouvait pas faire du mal à le remettre à vous les gars, parce que je pense qu'il est probablement quelque chose soit si évident que je aveuglante ne sera pas voir ou si complexe que je ne serai jamais ma tête envelopper il.

J'ai une carte comme celle-ci: Carte

Et le raycasting me donne ce (garder à l'esprit, il est juste les couleurs de débogage, le rouge est l'interception de rayons, mais la position avant prévue (si l'ombrage), le bleu serait l'interception de rayons au bon endroit (donc les faits saillants ou comme-est ), et des moyens jaunes ce point avait pas d'interaction des rayons du tout avant tout découpe en boucle). Badx2

Le résultat devrait être de rouge sur les pentes et les zones face masquée derrière de grandes montagnes (ombres) et bleu sur les pentes face au soleil (highlights). Il ne devrait pas y avoir de jaune. Donc, cette image indique que ce soit tous les rayons frappent le mal placés ou les rayons sont en cours recoupé toujours un autre endroit avant d'atteindre leur cible, ce qui est impossible.

À ce stade, je soupçonne fortement le problème est avec mon TRIG.

Voici la classe 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;
        }
    }

En outre comme une formalité, la fonction qui est coulée chaque rayon et comment je fais appel que:

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);
    }
Était-ce utile?

La solution

Je fini par utiliser une approche beaucoup plus simple avec algorithme de ligne Bresenham pour trouver l'interception point; J'imagine qu'il est beaucoup plus rapide et plus efficace que la façon dont je tentais de le faire aurait été.

Autres conseils

Je pense que lorsque votre vecteur Direction est appliqué à Position, il outrepasse la limite inférieure (Position.Y > -1) avant qu'il ait une chance de toucher la surface (Position.Y <= Terrainmap[(int)Position.X, y].Height).

Vous pouvez essayer de diminuer la limite inférieure ou réordonner vos tests if / while.

Un autre problème pourrait être que le Direction Vector est trop grand par rapport à votre taille de gamme. La distance entre deux pixels voisins est égal à 1, alors que l'ensemble des différences de hauteur est contenue dans la plage (-1,1). Cela donne une très surface plane du rayon-roulettes point de vue. Lorsque le vecteur de Direction est appliquée au vecteur Position est fait un pas relativement petit sur la longueur, et une étape relativement grande au-dessus de la hauteur.

@Maltor: Je voulais vraiment commenter votre propre réponse, mais en raison de ma réputation ne suis pas actuellement en mesure de

.

J'ai aussi utilisé l'approche de la ligne et une diminution du temps de calcul de la Bresenham à 1/10!

Un exemple courant de qui peut être consulté à mon projet github TextureGenerator-Online . L'outil de terrain utilise cette approche.

Voir la fonction setTerrainShadow() tex_terrain.js

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top