Visualizzazione dei risultati da 1 a 10 su 10
  1. #1
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308

    [C# / Xna] "Fotografare" lo schermo

    Ho deciso di mandare qualche "foto" agli amici di quel che vedo quando navigo nel mio sistema solare. E' la prima volta che affronto questo aspetto di Xna e mi ricordo che questa operazione si chiama "reindirizzamento del Render Target". Ho studiato un pò e come al solito "rubo" il codice che trovo nei tutorials.
    Mi serve un nuovo RenderTarget per cambiare la destinazione dell'output e una Texture2D che salvi quello che prima andava disegnato sullo schermo: fatto.
    Creo un'immagine bitmap vuota (la shadowMap) delle dimensioni del mio schermoe la metto nel Content.
    Creo il nuovo renderTarget con un costruttore che ancora devo studiare bene nei suoi parametri, ma prima di capirlo a fondo voglio vedere cosa succede. Comunque mi sembra intuitivo.
    Infine, all'inizio del Draw(), primissima cosa, reindirizzo tutto sul nuovo RenderTarget, e alla fine, ultimissima cosa, annullo questo reindirizzamento (???) e "copio" sulla shadowMap.

    codice:
    namespace Progetto_Albireo   
    {
        public class My : Microsoft.Xna.Framework.Game
        {
            //Generals          
            public static GraphicsDeviceManager Graphicsdevicemanager;
            RenderTarget2D renderTarget;
            Texture2D shadowMap;
    
            .......... 
     
            public My()
            {
                Graphicsdevicemanager = new GraphicsDeviceManager(this);
                Content.RootDirectory = "Content";
            }
    
            protected override void Initialize()
            {
                .......... 
            }
            protected override void LoadContent()
            {
                ..........             
                shadowMap = Content.Load<Texture2D>("shadow");
                
                PresentationParameters pp = GraphicsDevice.PresentationParameters;
                renderTarget = new RenderTarget2D(GraphicsDevice, pp.BackBufferWidth, pp.BackBufferHeight,
                    true, GraphicsDevice.DisplayMode.Format, DepthFormat.Depth24);
           }
            protected override void Update(GameTime gametime)
            {
                ..........
            }
            protected override void Draw(GameTime gametime)
            {
                GraphicsDevice.SetRenderTarget(renderTarget); 
               ..........
                 
                GraphicsDevice.SetRenderTarget(null);
                shadowMap = (Texture2D)renderTarget;
           }
        }
    }
    Eseguo il programma e non si vede un tubo. Giusto! Tutte le "viste" della camera della mia astronave stanno andando sulla shadowMap. Termino il programma e clicco sulla shadowMap nel Content, sicuro di vedere una "foto".... è completamente bianca.
    Mi sembrava troppo facile....
    Boh vabbè devo salvare la renderMap in qualche posto. Studio il Framework di Xna e l'unico metodo che salva una Texture2D usa un MemoryStream.... mmmmmm.... OK diamogli un MemoryStream

    codice:
    namespace Progetto_Albireo   
    {
        public class My : Microsoft.Xna.Framework.Game
        {
            //Generals          
            public static GraphicsDeviceManager Graphicsdevicemanager;
            RenderTarget2D renderTarget;
            Texture2D shadowMap;
            MemoryStream stream = new MemoryStream();
       ..........
    A questo punto cominciano le risate. Salvo la shadowMap nel MemoryStream, gli dò una dimensione di prova 500x500 ma come scrivo lo stream su un file? Un MemoryStream non ha un metodo di salvataggio, ma mi ricordo che un oggetto Bitmap ce l'ha. Ma come legare una bitmap ad un MemoryStream? Facile, c'è un costruttore Bitmap apposta. Non rimane che salvare la bitmap dove voglio:

    codice:
            ..........
             
            protected override void Draw(GameTime gametime)
            {
                GraphicsDevice.SetRenderTarget(renderTarget); 
                .......... 
                 
                GraphicsDevice.SetRenderTarget(null);
                shadowMap = (Texture2D)renderTarget;
                
                shadowMap.SaveAsJpeg(stream, 500, 500); 
              System.Drawing.Bitmap b = new System.Drawing.Bitmap(stream);
              b.Save(@"C:\prova.bmp");
       }
    
        }
    }
    Eseguo il programma, termino, guardo il file prova.bmp e .... FUNZIONA!
    Però mi sembra di aver fatto un giro troppo incasinato, c'è qualche riga di troppo? C'è un metodo più "diretto" ed "elegante"? E perchè quella riga
    GraphicsDevice.SetRenderTarget(null);

    Infine, che notizie avete sul futuro di Xna? Posso continuare a studiarlo o perdo tempo?
    Ultima modifica di escocat; 05-11-2013 a 10:59

  2. #2
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    Intanto vi mando questa foto in avvicinamento al pianeta C. Sono riuscito a beccare il passaggio del pianeta interno B sul disco della stella Albireo
    Immagini allegate Immagini allegate

  3. #3
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    Ho sistemato le cose in modo che premendo un tasto si attiva il PhotoMode e si ha un "click" sul file frame.bmp.

    codice:
            protected override void Draw(GameTime gametime)
            {
                if (PhotoMode)
                    GraphicsDevice.SetRenderTarget(renderTarget);
                    
     
                ............
    
    
                if (PhotoMode)
                {
                    GraphicsDevice.SetRenderTarget(null);
                    shadowMap = (Texture2D)renderTarget;
                    shadowMap.SaveAsJpeg(stream, RisX, RisY); 
                    System.Drawing.Bitmap b = new System.Drawing.Bitmap(stream);
                    b.Save(@"C:\frame.bmp");
                    PhotoMode = false;
                }
     
            }
    
    
    Mi sono accorto, però, che posso fare una sola foto nel corso di ogni esecuzione del programma. Nel runtime ogni click successivo non modifica la prima frame.bmp ottenuta! Se voglio una foto diversa devo rieseguire il programma. Ho tentato 1000 soluzioni, compresi array di shadowMap, array di renderTarget, array di frame.bmp....ho pure provato a spostare le costruzioni di renderTarget in Draw() anzichè in Initialize() o LoadContent() ma niente....

  4. #4
    Utente di HTML.it L'avatar di rsdpzed
    Registrato dal
    Aug 2001
    Messaggi
    764
    dove assegni l'eventhandler del tasto che attiva il photomode? molto probabilmente il problema è li

  5. #5
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    Non cominciamo a parlare difficile.
    eventhandler?
    La gestione della tastiera è addebitata al metodo KeyControl() in Updatr() nel file My.cs e richiama il metodo Ctrls della struttura Discovery (l'astronave) nel file Discovery.cs.
    Ecco le parti salienti dei due files:

    My.cs:

    codice:
    namespace Progetto_Albireo
    {
       public class My : Microsoft.Xna.Framework.Game
        {
            //Generals          
            public static GraphicsDeviceManager Graphicsdevicemanager;
    #if WIDE
            public static int RisX, RisY;
    #else
            public static int RisX = 1350;
            public static int RisY = 650;
    #endif
            RenderTarget2D renderTarget;
            Texture2D shadowMap;
            MemoryStream stream = new MemoryStream();
           ..........
     
            //Discovery
            public static Discovery Discovery =
                new Discovery(new Vector3(0f, 0f, 30000f),  //Position
                -Vector3.UnitZ,                               //Go
                10f,                                       //Speed
                -Vector3.UnitZ,                               //To
                Vector3.Up);                                  //Up
           ..........
           public static bool PhotoMode = false;
           ..........
           public void KeyControl()
            {
                KeyState = Keyboard.GetState();
               if ((KeyState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape)) & (OldState != KeyState))
                    this.Exit();
               if (OldState != KeyState)
                    Discovery.Ctrls(KeyState);
               OldState = KeyState;
            }
           public My()
            {
                Graphicsdevicemanager = new GraphicsDeviceManager(this);
                Content.RootDirectory = "Content";
            }
    
            protected override void Initialize()
            {
                ..........
               shadowMap = new Texture2D(GraphicsDevice, RisX, RisY);
               PresentationParameters pp = GraphicsDevice.PresentationParameters;
               renderTarget = new RenderTarget2D(GraphicsDevice, pp.BackBufferWidth, pp.BackBufferHeight,
                           false, GraphicsDevice.DisplayMode.Format, DepthFormat.Depth24);
                
                base.Initialize();
            }
           protected override void LoadContent()
            {
                ..........
            }
           protected override void Update(GameTime gametime)
            {
                .......... 
               KeyControl();
               //DISCOVERY
               Discovery.Move();
            }
           protected override void Draw(GameTime gametime)
            {
               if (PhotoMode)
                    GraphicsDevice.SetRenderTarget(renderTarget);
                    
                ..........
               if (PhotoMode)
                {
                    GraphicsDevice.SetRenderTarget(null);
                    shadowMap = (Texture2D)renderTarget;
                    shadowMap.SaveAsJpeg(stream, RisX, RisY);//o SaveAsPng()
                    System.Drawing.Bitmap b = new System.Drawing.Bitmap(stream);
                    b.Save(@"C:\frame.bmp");
                    PhotoMode = false;
               }
     
            }
    
        }
    }
    Discovery.cs:

    codice:
    namespace Progetto_Albireo
    {
        public struct Discovery
        {
            //principali
            public Vector3 Position;
            public Vector3 Go;
            public float Speed;
            public Vector3 To;
            public Vector3 Up;
            
            ..........
           public Discovery(Vector3 P, Vector3 G, float S, Vector3 T, Vector3 U)
            {
               ..........
            }
    
            ..........
           public void Ctrls(KeyboardState KeyState)
            {
                ..........
               if (KeyState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Space))
                {
                    if (My.PhotoMode)
                        My.PhotoMode = false;
                    else
                        My.PhotoMode = true;
                    
                }
               .......... 

  6. #6
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    Scusate non so nemmeno inserire il codice per bene..

    My.cs:

    codice:
    namespace Progetto_Albireo  // NUOVO
    {
        public class My : Microsoft.Xna.Framework.Game
        {
            //Generals          
            public static GraphicsDeviceManager Graphicsdevicemanager;
    #if WIDE
            public static int RisX, RisY;
    #else
            public static int RisX = 1350;
            public static int RisY = 650;
    #endif
            RenderTarget2D renderTarget;
            Texture2D shadowMap;
            MemoryStream stream = new MemoryStream();
            ..........
            //Discovery
            public static Discovery Discovery =
                new Discovery(new Vector3(0f, 0f, 30000f),  //Position
                -Vector3.UnitZ,                               //Go
                10f,                                       //Speed
                -Vector3.UnitZ,                               //To
                Vector3.Up);                                  //Up
            ..........
            public static bool PhotoMode = false;
            public static KeyboardState KeyState;
            public static KeyboardState OldState = Keyboard.GetState();
            ..........
            public void KeyControl()
            {
                KeyState = Keyboard.GetState();
                if ((KeyState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape)) & (OldState != KeyState))
                    this.Exit();
                if (OldState != KeyState)
                    Discovery.Ctrls(KeyState);
                OldState = KeyState;
            }
            public My()
            {
                Graphicsdevicemanager = new GraphicsDeviceManager(this);
                Content.RootDirectory = "Content";
            }
    
            protected override void Initialize()
            {
                ..........
                shadowMap = new Texture2D(GraphicsDevice, RisX, RisY);
                PresentationParameters pp = GraphicsDevice.PresentationParameters;
                renderTarget = new RenderTarget2D(GraphicsDevice, pp.BackBufferWidth, pp.BackBufferHeight,
                           false, GraphicsDevice.DisplayMode.Format, DepthFormat.Depth24);
                
                base.Initialize();
            }
            protected override void LoadContent()
            {
                ..........
            }
            protected override void Update(GameTime gametime)
            {
                ..........
                KeyControl();
                //DISCOVERY
                Discovery.Move();
            }
    
            protected override void Draw(GameTime gametime)
            {
                if (PhotoMode)
                    GraphicsDevice.SetRenderTarget(renderTarget);
                ..........
                if (PhotoMode)
                {
                    GraphicsDevice.SetRenderTarget(null);
                    shadowMap = (Texture2D)renderTarget;
                    shadowMap.SaveAsJpeg(stream, RisX, RisY);//o SaveAsPng()
                    System.Drawing.Bitmap b = new System.Drawing.Bitmap(stream);
                    b.Save(@"C:\frame.bmp");
                    PhotoMode = false;
                }
     
            }
    
        }
    }
    Discovery.cs:

    codice:
    namespace Progetto_Albireo
    {
        public struct Discovery
        {
            //principali
            public Vector3 Position;
            public Vector3 Go;
            public float Speed;
            public Vector3 To;
            public Vector3 Up;
            ..........
            public Discovery(Vector3 P, Vector3 G, float S, Vector3 T, Vector3 U)
            {
               ..........
            }
    
            ..........
            public void Ctrls(KeyboardState KeyState)
            {
                ..........
                if (KeyState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Space))
                {
                    if (My.PhotoMode)
                        My.PhotoMode = false;
                    else
                        My.PhotoMode = true;
                    
                }
                ..........
            }
            ..........
    
        }
    }
    Ultima modifica di escocat; 07-11-2013 a 15:55

  7. #7
    Utente di HTML.it L'avatar di rsdpzed
    Registrato dal
    Aug 2001
    Messaggi
    764
    La gestione del tasto sembra giusta.
    Intanto correggiamo tutta la parte del salvataggio dell'immagine:

    togli via questa riga dai campi private della classe:
    codice:
    MemoryStream stream =newMemoryStream();
    poi nel draw cambia il tuo pezzo di codice con questo

    codice:
                if(PhotoMode)
                {
                    GraphicsDevice.SetRenderTarget(null);
                    shadowMap =(Texture2D)renderTarget;
                    using (MemoryStream stream = new MemoryStream())
                    {
                      shadowMap.SaveAsJpeg(stream,RisX,RisY);
                      stream.Seek(0, 0); // <-- potrebbe non servire ma mettilo comunque!
                      using (System.Drawing.Bitmap b =newSystem.Drawing.Bitmap(stream))
                      {
                        b.Save(@"C:\frame.bmp");
                      }
                    }
                    PhotoMode=false;
                }
    dimmi come va cosi.

  8. #8
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    PERFETTO

    Hai ragione anche su quella riga: è superflua. Sei un mago di Xna, mi ricorderò di te se avrò ancora bisogno. Grazie. Per sdebitarmi ti mando tre foto scattate a intervalli di pochi secondi.
    Immagini allegate Immagini allegate

  9. #9
    Utente di HTML.it L'avatar di rsdpzed
    Registrato dal
    Aug 2001
    Messaggi
    764
    belle
    in realtà il problema non è legato a xna in senso stretto. Le immagini jpeg venivano memorizzate nello stream in coda a quella precedente e cosi via ma il Bitmap veniva creato solo dalla prima immagine presente nello stream.
    Per ovviare al problema basta ricreare lo stream da zero ad ogni salvataggio. Un altra cosa importante è il modo con cui si crea lo stream e il bitmap. Sono due classi che implementano IDisposable (te ne accorgi perche hanno il metodo Dispose()). Questi oggetti vanno "chiusi" con il metodo Dispose() quando non servono piu oppure vanno aperti usando la using come ho fatto io che è una direttiva che chiama il dispose automaticamente alla fine del suo blocco di istruzioni.

  10. #10
    Utente di HTML.it L'avatar di escocat
    Registrato dal
    Feb 2012
    Messaggi
    308
    Si infatti avevo pensato a reinizializzare tutto (shadowMap, renderTarget, il Frame....) tranne quello che serviva veramente (lo Stream!). Terrò presente le using per il futuro. Ciao.

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.