Domanda

In pratica quello che sto cercando di fare è ombreggiare una heightmap 2D utilizzando un sistema raycasting molto molto di base che fondamentalmente solo controlla se il raggio è stato intercettato prima che dovrebbe essere quello di ombra esso. Tuttavia non funziona correttamente e sono stato sbattere la testa per diverse ore ora in poi questo così ho pensato che non poteva male a capovolgerlo a voi ragazzi, perché penso che sia probabilmente qualcosa sia così assolutamente ovvio che non lo farò vederlo o così complesso che non sarò mai avvolgere la mia testa intorno ad esso.

Ho una mappa simile a questo: Mappa

E la raycasting mi sta dando questa (tenere a mente che è solo colori di debug; rosso è raggi intercettazione, ma posizione prima intesa (così ombreggiatura), blu sarebbero intercettazioni ray nel posto giusto (quindi mette in evidenza o solo come-è ), e mezzi di gialli che punto aveva alcuna interazione ray a tutti prima del ciclo while cut-out). Badx2

Il risultato dovrebbe essere con rosso sul backfacing piste e le aree dietro le grandi montagne (ombre) e blu sui pendii di fronte al sole (luci). Non ci dovrebbe essere alcun giallo. Quindi questa immagine indica che o tutti i raggi stanno colpendo il mal collocati o raggi vengono intersecati sempre altrove prima di raggiungere il loro obiettivo, che è impossibile.

A questo punto ho altamente sospetto che il problema è con il mio Trig.

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

Anche solo come una formalità, la funzione che sta gettando ogni raggio e come sto chiamando che:

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);
    }
È stato utile?

Soluzione

Ho finito per usare un approccio molto più semplice con l'algoritmo Linea di Bresenham per trovare l'intercetta punto; Immagino che è molto più veloce e più efficiente rispetto al modo in cui stavo cercando di farlo sarebbe stato.

Altri suggerimenti

La mia ipotesi è che quando il vostro vettore Direction viene applicato a Position, si oltrepassa il limite inferiore (Position.Y > -1) prima che abbia la possibilità di colpire la superficie (Position.Y <= Terrainmap[(int)Position.X, y].Height).

Si potrebbe provare a diminuire il limite inferiore, o ri-ordinare il vostro if / test while.

Un altro problema potrebbe essere che il Direction Vector è troppo grande in confronto alla tua altezza-gamma. La distanza tra due pixel adiacenti è 1, mentre l'intera gamma di differenze di altezza è contenuta nell'intervallo (-1,1). Questo dà un molto superficie piana dal punto di vista ray-rotelle. Quando il vettore Direction viene applicato al vettore Position è prende un relativamente piccolo passo sulla lunghezza, e una relativamente grande passo sull'altezza.

@Maltor: in realtà ho voluto commentare la propria risposta, ma a causa della mia reputazione non sono attualmente in grado di

.

ho usato anche l'approccio linea del Bresenham e diminuzione del tempo di calcolo per 1/10!

Un esempio di esecuzione di che può essere visionato a mio progetto github TextureGenerator-Online . Lo strumento del terreno utilizza questo approccio.

strumento Terrain

vedi funzione setTerrainShadow() a tex_terrain.js

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top