2

基本的に私がやろうとしているのは、非常に基本的なレイキャスティング システムを使用して 2D ハイトマップをシェーディングすることです。しかし、それは正しく機能しておらず、私はこれについて数時間頭を悩ませてきました.それを見て、または非常に複雑であるため、頭を悩ませることは決してありません。

私はこのような地図を持っています: 地図

そして、レイキャスティングは私にこれを与えています(これは単なるデバッグ色であることに注意してください。赤はレイインターセプトですが、意図した位置の前(シェーディング)、青は正しい場所でのレイインターセプトです(ハイライトまたはそのまま)、そして黄色は、while ループのカットアウトの前に光線の相互作用がまったくなかったことを意味します)。 Badx2

結果は、後ろ向きの斜面と大きな山の後ろの領域 (影) が赤になり、太陽に面した斜面 (ハイライト) が青になります。黄色があってはなりません。したがって、この画像は、すべての光線が間違った場所に当たっているか、光線がターゲットに到達する前に常に別の場所で交差していることを示していますが、これは不可能です。

この時点で、問題は私のトリガーにあると強く思います。

レイクラスは次のとおりです。

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

3 に答える 3

2

Bresenham's Line Algorithmを使用したはるかに単純なアプローチを使用してインターセプト ポイントを見つけることになりました。私がやろうとしていた方法よりもはるかに高速で効率的だと思います。

于 2011-10-02T15:07:03.693 に答える
0

私の推測では、Directionベクトルが に適用されると、サーフェスにヒットする前にPosition下限 ( ) を超えます ( )。Position.Y > -1Position.Y <= Terrainmap[(int)Position.X, y].Height

下限を下げるか、if/whileテストの順序を変更してみてください。

もう 1 つの問題はDirection、高さの範囲に比べてベクトルが大きすぎることです。隣接する 2 つのピクセル間の距離は 1 ですが、高さの差の全範囲は範囲 (-1,1) に含まれます。これにより、レイキャスターの観点から非常に平らな表面が得られます。Directionベクトルが適用されると、ベクトルPositionは長さ方向に比較的小さいステップを取り、高さ方向に比較的大きなステップを取ります。

于 2011-07-24T09:52:59.703 に答える