C# DirectX - Problem mit Lichter

Xtremebergi

Lt. Junior Grade
Registriert
Feb. 2008
Beiträge
312
Hallo, zusammen

Ich programmiere zurzeit in C# mit dem DirectX SDK (nicht XNA) und bin vor kurzem
auf etwas Seltsames gestoßen.

Anscheinend bleiben die Lichter, die man z. B. mit "device.Lights[0].Type = ..." definiert,
von den World-Matrix-Transformationen (z. B. device.Transform.World = Matrix.RotationX )
völlig unberührt.



Zum Beispiel:

Ich habe z. B. als 3D-Objekt eine Bodenplatte und positioniere die Kamera so,
als ob man "draufsteht". Das Licht ist directional und scheint schräg auf den Boden. Wenn ich nun "hinunter schaue" bzw "auf den Boden schaue" (mit device.Transform.World *= Matrix.RotationX(...)) dann wird er immer dunkler.

Also anscheinend wird nur der Boden gedreht aber das licht bleibt unverändert, sodass die Kamera nun im Schatten der Bodenplatte ist.

Ich hoffe man kann sich das halbwegs vorstellen^^
Ich hab zur Sicherheit noch 3 Bilder angehängt.



Nun eben meine Frage: Wie kann ich das beheben? Es würde theoretisch gehen, indem ich nicht mit der Welt-Matrix-Transformation rotiere sondern die Kamera rotiere (device.Transform.View) aber ich hab gelesen dass es normalerweise mit der Transform.World-Variante gemacht wird. Kann man die Lichter "fixieren"? Muss man sie explizit mitrotieren?


Danke im Voraus!

Xtremebergi
 

Anhänge

  • Perspective1.png
    Perspective1.png
    26,4 KB · Aufrufe: 260
  • Perspective2.png
    Perspective2.png
    31,3 KB · Aufrufe: 247
  • Perspective3.png
    Perspective3.png
    28,6 KB · Aufrufe: 233
Zuletzt bearbeitet:
Directionales Licht hat bei keine Position sondern nur eine Richtung. Somit haben die Weltkoordinatentransformationen keine Auswirkungen auf die Position des Lichtes. Dadurch dass du aber den Rest der Welt drehst erscheint dir die Bodenplatte mal hell und mal dunkel.

Edit1: Mh, Nein das waer Quatsch :) Klar wird das Licht mit transformiert. Kann also sein, dass dein Drehpunkt der Platte nicht im Koordinatenursprung liegt.

Ahja, ohne Gewaehr aber das sollte so in etwa stimmen.

EDIT2: Ich muss da morgen nochmal drueber nachdenken. Jetzt habe ich mich selber etwas verunsichert. Wie war das doch gleich mit den ganzen Matrizenmultiplikationen und der Renderpipeline ;)
 
Zuletzt bearbeitet:
jaja, die Matrizen :) ...

Ich hab auch schon im Internet nach fertigen Beispielcodes oder fertigen Spielen gesucht, da dort dieses Problem ja gelöst sein müsste. Bisher hab ich jedoch nur Codes gefunden die zwar dieses Problem haben, aber bei denen es nicht auffällt, da sie z.B. keine freihe Rotation haben bzw noch nicht so fortgeschritten sind.

Hach, mich nervt das enorm...irgendwie muss es doch einen "normale" Lösung dafür geben, da dies ja etwas ganz Grundlegendes ist...


mfg
Xtremebergi
 
Zeig doch mal dein Code. Jetzt bin ich etwas klarer :)

Und ich gehe immernoch davon aus, dass dein Objekt einen flaschen Drehpunkt hat. Also wenn du eine Welttransformation machst und das Objekt nicht im Ursprung ist dann dreht es sich sozusagen um den Ursprung auf einer Kreisbahn inkl. des Abstandes zum Ursprung. Dann entsteht ein solcher Effekt. Zumal eine Kamera schon etwas mehr ist als nur Rotation um die Achsen. Am besten du schaust dir das mal an : http://wiki.delphigl.com/index.php/Tutorial_Matrix2

Ist zwar OpenGL aber das gilst genauso fuer DX.
 
Alles kann ich hier nicht posten, da das Projekt schon etwas größer ist...
Aber hier die wesentlichen Teile:


Okay, also hier die abstracte Klasse "Scene", von der alle verschiedenen Szenen (zur Zeit Menü und Simulator) abgeleitet werden:

Code:
public abstract class Scene
    {
        ////////////////////////////////////////////////////////////////////////////////////
        //                                   HARDWARE                                     //

        protected Microsoft.DirectX.Direct3D.Device     graphicCard;
        protected Microsoft.DirectX.DirectInput.Device  keyboard;
        protected Microsoft.DirectX.DirectInput.Device  mouse;

        //                                                                                //
        ////////////////////////////////////////////////////////////////////////////////////

        
        
        protected List<IDrawable> drawnObjects = new List<IDrawable>();

        protected BergXWindow window;



        public Scene(Microsoft.DirectX.Direct3D.Device      graphicCard ,
                     Microsoft.DirectX.DirectInput.Device   keyboard    ,
                     Microsoft.DirectX.DirectInput.Device   mouse       ,
                     BergXWindow                            window      )
        {
            this.graphicCard    = graphicCard;
            this.keyboard       = keyboard;
            this.mouse          = mouse;
        }


        

        public abstract Scene Proceed(long frameTime);



        public void Draw()
        {
            foreach (IDrawable d in drawnObjects)
                d.Draw(graphicCard);
        }



        protected bool KeyPressed(Key[] array, Key toCheck)
        {
            foreach (Key k in array)
                if (k == toCheck)
                    return true;

            return false;
        }
    }


Nun der Simulator, der dann erstellt wird, wenn im Menü auf "Start" geklickt wird. Ab diesem Zeitpunkt wird in jedem Frame die Methode "Proceed" des Simulators aufgerufen.

Code:
    public class Simulator : Scene
    {
        ////////////////////////////////////////////////////////////////////////////////////
        //                                 START SETTINGS                                 //

        private Vector3 worldRot        = new Vector3(      0.0f,     0.0f,     0.0f      );
        private Vector3 worldPos        = new Vector3(    100.0f,   -11.8f,   100.0f      );

        private Vector3 cameraPosition  = new Vector3(      0.0f,     0.0f,     0.0f      );
        private Vector3 cameraTarget    = new Vector3(      1.0f,     0.0f,     0.0f      );
        private Vector3 cameraUpVector  = new Vector3(      0.0f,     1.0f,     0.0f      );

        //                                                                                //
        ////////////////////////////////////////////////////////////////////////////////////
       

        ////////////////////////////////////////////////////////////////////////////////////
        //                                CONTROLSETTINGS                                 //
        
        private const float MOUSEINTENSITY  = 0.002f;

        private const float MOVESPEED       = 10f;

        private const Key   KEYFOWARD       = Key.W;
        private const Key   KEYLEFT         = Key.A;
        private const Key   KEYBACKWARD     = Key.S;
        private const Key   KEYRIGHT        = Key.D;

        //                                                                                //
        ////////////////////////////////////////////////////////////////////////////////////


        ////////////////////////////////////////////////////////////////////////////////////
        //                                  3D-OBJECTS                                    //

        private Cylinder cylinder;

        //                                                                                //
        ////////////////////////////////////////////////////////////////////////////////////



        public Simulator(Microsoft.DirectX.Direct3D.Device      graphicCard ,
                         Microsoft.DirectX.DirectInput.Device   keyboard    ,
                         Microsoft.DirectX.DirectInput.Device   mouse       ,
                         BergXWindow                            window      ) : base(graphicCard, keyboard, mouse, window)
                         
        {
            this.graphicCard = graphicCard;

            graphicCard.Transform.View = Matrix.LookAtLH(cameraPosition, cameraTarget, cameraUpVector);
        
            cylinder = new Cylinder(graphicCard, 350, 10, 0, 0, 0, 6);

            drawnObjects.Add(cylinder);
        }



        public override Scene Proceed(long frameTime)
        {
            ///////////////////MOUSECONTROL////////////////////
                                                             //
            MouseState mouseState = mouse.CurrentMouseState; //
            worldRot.Y += mouseState.X * MOUSEINTENSITY;     //
            worldRot.Z += mouseState.Y * MOUSEINTENSITY;     //



            //////////////////////////////////////KEYBOARDCONTROL/////////////////////////////////////////
                                                                                                        //
            Key[] pressedKeys = keyboard.GetPressedKeys();                                              //
                                                                                                        //
            if (KeyPressed(pressedKeys, KEYFOWARD))                                                     //
            {                                                                                           //
                worldPos.X +=   -   (float)(Math.Cos(worldRot.Y) * Math.Cos(worldRot.Z) * MOVESPEED);   //
                worldPos.Z +=   +   (float)(Math.Sin(worldRot.Y) * Math.Cos(worldRot.Z) * MOVESPEED);   //
                worldPos.Y +=   +   (float)(                       Math.Sin(worldRot.Z) * MOVESPEED);   //
            }                                                                                           //
                                                                                                        //
            if (KeyPressed(pressedKeys, KEYBACKWARD))                                                   //
            {                                                                                           //
                worldPos.X +=    +   (float)(Math.Cos(worldRot.Y) * Math.Cos(worldRot.Z) * MOVESPEED);  //
                worldPos.Z +=    -   (float)(Math.Sin(worldRot.Y) * Math.Cos(worldRot.Z) * MOVESPEED);  //
                worldPos.Y +=    -   (float)(                       Math.Sin(worldRot.Z) * MOVESPEED);  //
            }                                                                                           //
                                                                                                        // 
            if (KeyPressed(pressedKeys, KEYLEFT))                                                       //
            {                                                                                           //
                worldPos.X +=   -   (float)(Math.Cos(worldRot.Y - Math.PI / 2)          * MOVESPEED);   //
                worldPos.Z +=   +   (float)(Math.Sin(worldRot.Y - Math.PI / 2)          * MOVESPEED);   //
            }                                                                                           //
                                                                                                        //
            if (KeyPressed(pressedKeys, KEYRIGHT))                                                      //
            {                                                                                           //
                worldPos.X +=    +   (float)(Math.Cos(worldRot.Y - Math.PI / 2)          * MOVESPEED);  //
                worldPos.Z +=    -   (float)(Math.Sin(worldRot.Y - Math.PI / 2)          * MOVESPEED);  //
            }                                                                                           //
                                                                                                        //
            if (KeyPressed(pressedKeys, Key.Escape))                                                    //
            {                                                                                           //
                return new BergXMenu(graphicCard,                                           //
                                     keyboard   ,                                           //
                                     mouse      ,                                           //
                                     window     );                                          //
            }                                                                                           //



            ////////////////////ROTATION CORRECTION///////////////////////
                                                                        //
            worldRot.Y = worldRot.Y % ((float)Math.PI * 2f);            //
            worldRot.Z = Math.Min((float)Math.PI / 2f, worldRot.Z);     //
            worldRot.Z = Math.Max((float)-Math.PI / 2f, worldRot.Z);    //



            ///////////////////////EXECUTE TRANSFORMATION/////////////////////////////
                                                                                    //
            graphicCard.Transform.World = Matrix.Translation(worldPos);             //
            graphicCard.Transform.World *= Matrix.RotationY((float)-worldRot.Y);    //
            graphicCard.Transform.World *= Matrix.RotationZ((float)worldRot.Z);     //

            return this;
        }
    }


Hier das Codestück, in dem ich mein Grafik-Device initialisiere:

Code:
        private static void initializeDevice()
        {
            PresentParameters presentParams = new PresentParameters();
            presentParams.Windowed = true;
            presentParams.PresentationInterval = PresentInterval.One;
            presentParams.SwapEffect = SWAPEFFECT;
            presentParams.AutoDepthStencilFormat = DepthFormat.D16;
            presentParams.EnableAutoDepthStencil = true;
            presentParams.MultiSample = MultiSampleType.None;
            graphicCard = new Microsoft.DirectX.Direct3D.Device(0, Microsoft.DirectX.Direct3D.DeviceType.Hardware, window, CreateFlags.SoftwareVertexProcessing, presentParams);
            graphicCard.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, (window.Width / window.Height) * (window.Width / 3f + window.Height) / window.Height, 0.001f, 1000f);
            graphicCard.RenderState.Lighting = true;

            graphicCard.Lights[0].Type = LightType.Directional;
            graphicCard.Lights[0].Diffuse = Color.White;
            graphicCard.Lights[0].Direction = new Vector3(-0.5f, -1f, -1f);
            graphicCard.Lights[0].Enabled = true;
        }


Hier die OnPaint-Methode:

Code:
protected override void OnPaint(PaintEventArgs e)
        {
            scene = scene.Proceed(frameTime.ElapsedMilliseconds);
            frameTime.Reset();
            frameTime.Start();

            Invalidate();
            graphicCard.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);
            graphicCard.BeginScene();
            scene.Draw();
            graphicCard.EndScene();
            graphicCard.Present();

            Cursor.Hide();
            Cursor.Clip = new Rectangle(this.Location, this.Size);
        }


Und hier noch die Cylinder-Klasse, mit der ich die Bodenplatte erstelle:

Code:
public class Cylinder : IDrawable
    {
        private Mesh    mesh;   

        public Cylinder(Device graphicCard, float radius, float height, float xPos, float yPos, float zPos, int axisDivisions)
        {
            ////////////////////////////////////VERTICES//////////////////////////////////////
            
            CustomVertex.PositionColored[] vertices = new CustomVertex.PositionColored[2 * axisDivisions + 2];    //

            vertices[0] = new CustomVertex.PositionColored(xPos, yPos + height, zPos, Color.Gray.ToArgb());
            for (int i = 1; i <= axisDivisions; i++)
            {
                vertices[i] = new CustomVertex.PositionColored(xPos + (float)Math.Cos(2 * Math.PI / axisDivisions  * i) * radius, yPos + height, zPos + (float)Math.Sin(2 * Math.PI / axisDivisions * i) * radius, Color.Gray.ToArgb());
            }

            for (int i = axisDivisions + 1; i <= axisDivisions * 2 + 1; i++)
            {
                vertices[i] = new CustomVertex.PositionColored(vertices[i - axisDivisions - 1].X, yPos, vertices[i - axisDivisions - 1].Z, Color.Gray.ToArgb());
            }


            //////////////////////Indices/////////////////////
            short[] indices = new short[12 * axisDivisions];

            for (int i = 0; i < axisDivisions; i++)
            {
                indices[i * 3 + 0] = (short)(i + 2);
                indices[i * 3 + 1] = (short)(i + 1);
                indices[i * 3 + 2] = 0;
            }

            indices[3 * axisDivisions - 3] = 1;

            for (int i = axisDivisions; i < 2 * axisDivisions; i++)
            {
                indices[i * 3 + 0] = (short)(axisDivisions + 1);
                indices[i * 3 + 1] = (short)(i + 2);
                indices[i * 3 + 2] = (short)(i + 3);
            }

            indices[6 * axisDivisions - 1] = (short)(axisDivisions + 2);

            for (int i = 0; i < axisDivisions; i++)
            {
                indices[6 * axisDivisions + i * 6 + 0] = (short)(i + 1);
                indices[6 * axisDivisions + i * 6 + 1] = (short)(i + axisDivisions + 3);
                indices[6 * axisDivisions + i * 6 + 2] = (short)(i + axisDivisions + 2);

                indices[6 * axisDivisions + i * 6 + 3] = (short)(i + 1);
                indices[6 * axisDivisions + i * 6 + 4] = (short)(i + 2);
                indices[6 * axisDivisions + i * 6 + 5] = (short)(i + axisDivisions + 3);
            }

            indices[6 * axisDivisions + (axisDivisions - 1) * 6 + 1] = (short)(axisDivisions + 2);
            indices[6 * axisDivisions + (axisDivisions - 1) * 6 + 4] = 1;
            indices[6 * axisDivisions + (axisDivisions - 1) * 6 + 5] = (short)(axisDivisions + 2);


            mesh = new Mesh(4 * axisDivisions, 2 * axisDivisions + 2, MeshFlags.Managed, CustomVertex.PositionColored.Format, graphicCard);
            mesh.SetVertexBufferData(vertices, LockFlags.None);
            mesh.SetIndexBufferData(indices, LockFlags.None);

            int[] adjac = new int[mesh.NumberFaces * 3];
            mesh.GenerateAdjacency(0.5f, adjac);
            mesh.OptimizeInPlace(MeshFlags.OptimizeVertexCache, adjac);

            mesh = mesh.Clone(mesh.Options.Value, CustomVertex.PositionNormalColored.Format, graphicCard);
            mesh.ComputeNormals();
        }



        public void Draw(Device graphicCard)
        {
            graphicCard.VertexFormat = CustomVertex.PositionColored.Format;

            int numSubSets = mesh.GetAttributeTable().Length;
            for (int i = 0; i < numSubSets; ++i)
                mesh.DrawSubset(i);
        }
    }

Ich hoffe der Code ist halbwegs überschaubar^^

Edit: Wenn "ich" mich an der Position 0 | 0 | 0 befinde, dann ist die Bodenplatte genau im Koordinatenursprung, das Problem bleibt aber.




Edit2: Wie auch immer, ich werde solange einfach keine World-Transformationen durchführen sondern die View drehen. Ich lerne während ich dieses Projekt programmiere laufend weiter und vielleicht lässt sich dann ja mit HLSL eine vernünftige Lösung finden...
 
Zuletzt bearbeitet:
Herje, DX ist schon lange her bei mir und ich war auch nie wirklich fit darin. Aber das Problem liegt ja auf der Hand in deinem Code und du scheinst es ja schon selber erkannt zu haben. Du drehst ja die Platte weg anstatt einfach die Projektion neu zuberechnen. Das Objekt sollte sich also garnicht bewegen.

Dx stellt ja aber schon aehnlich wie in OGL Funktionen zur Verfuegung um die Camera zu positionieren. Der ganze Code mit WorldTransform usw ist also voellig ueberfluessig.

HLSL ist eine Shadersprache. Die wird bei diesem Problem nicht helfen koennen.

Ich denke du solltest dich wirklich nochmal in die Mathematik dahinter einlesen. Also wie genau funktioniert die Projection und Worldmatrix und wie werden diese berechnet.

Um also eine FirstPersonKamera zu bekommen, brauchst du eine CameraKlasse welche fuer dich die ganzen Transformationen uebernimmt. In etwa sowas:

Code:
protected override void OnPaint(PaintEventArgs e)
        {
            scene = scene.Proceed(frameTime.ElapsedMilliseconds);
            frameTime.Reset();
            frameTime.Start();

            Invalidate();
            graphicCard.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);
            [B][COLOR="Red"]camera.doTransform();[/COLOR][/B]
            graphicCard.BeginScene();
            scene.Draw();
            graphicCard.EndScene();
            graphicCard.Present();

            Cursor.Hide();
            Cursor.Clip = new Rectangle(this.Location, this.Size);
        }

Dazu findest du jede Menge im Netz.
 
Zuletzt bearbeitet:
Okay, vielen Dank!

Ich hab es jetzt geschafft, die Projektion mit Hilfe von Transform.View zu verändern, jetzt passt alles.
Und eine eigene Klasse dafür zu erstellen...ja, keine schlechte Idee.

lg
Xtremebergi
 
So weit ich weiß werden Lichter nicht mittransformiert. Ich verwende die DX C++ SDK, vielleicht ist das da anders, glaube ich aber nicht. Die Weltmatrix hat keine Auswirkung. Ich finde das auch eher schlecht.
 
Kommt natuerlich aufs Licht drauf an. Wenn du ein Licht haben willst was eine Bewegung ausfuehrt geht das nur ueber die Weltmatirx. Bei directionalem Licht natuerlich nicht.
 
Ich hab mittlerweile eine Lösung gefunden und schreibe sie hier als Abschluss noch her:

Ja, direktionale Lichter werden einfach nicht mittransformiert, man muss sie "händisch" mittransformieren, dabei reicht es nicht aus, einfach nur z. B. <graphicDevice>.Transform.Licht[0].Direction.TransformCoordinate(<Matrix>)
da man damit anscheinend nur eine Kopie des Lichts verändert und keinen Zugriff auf das eigentliche Objekt erhält (die get()-Methode gibt anscheinend nur eine Kopie zurück)

Man muss sich den Vector, mit dem das Licht scheinen soll, speichern und für jeden Frame diesen Vector hernehmen, ihn transformieren und dem Licht zuweisen:

Code:
        private DX.Vector3 m_vLight1Direction;

        private void UpdateDirectionalLights(D3D.Device p_CGraphicDevice)
        {
            DX.Vector3 l_vDummyVector = m_vLight1Direction;
            l_vDummyVector.TransformCoordinate(DX.Matrix.RotationY(m_vCameraRotation.Y) * DX.Matrix.RotationX(m_vCameraRotation.X));
            p_CGraphicDevice.Lights[0].Direction = l_vDummyVector;
            p_CGraphicDevice.Lights[0].Update();
        }

Vielleicht hilft es ja jemandem...
 
Zuletzt bearbeitet:
Zurück
Oben