SpriteBatch per effetti speciali – parte 4

By Davide Luzzu – Webcast by Giuseppe Maggiore

 

Il (resto del) codice C#

 

      // Generiamo un array di float che sono le altezze (y) dei punti di

 controllo.

      var tmp =

        Enumerable.Range(0, controlPoints.Width)

        // i [0,controlPoints.Width-1]

        .Select(i => i / (float)controlPoints.Width)

        // x [0,1]

        .Select(x => sin(-4.0f * t + 32.0f * pi * sigmoid(x + 0.5f) * x)

          * 0.1f + 0.5f)

        // y é l’altezza di un punto di controllo. La convertiamo in formato

  integrale.

        .Select(y => (UInt16)(y * (UInt16.MaxValue – 1)))

        .ToArray();

      // Salviamo l’array di altezze nella texture.

      controlPoints.SetData<UInt16>(tmp);

 

Per dare la forma al serpente definiamo i punti di controllo sulle x e sulle y. Per fare ciò ci serviremo delle funzioni sin e sigmoid, che abbiamo definito in precedenza. Definiamo un range da 0 a 256, e ne facciamo il rapporto tra 0 e 1 in modo che x [0,1]. Dato che x è nel range [0,1] possiamo definire la forma del serpente usando il seno. Tale forma varia in base al tempo    ( 4.0f * t…) ed ha, ad ogni istante di tempo, una forma curva
definita da  “…
32.0f * pi * sigmoid(x + 0.5f) * x…”; variando tali parametri determineremo un’alterazione a piacimento della forma o del tempo di esecuzione del serpente.

Una delle peculiarità derivanti da questa porzione di codice è il fatto che, ciò che viene disegnato, è ad una dimensione algoritmicamente, ma a due dimensioni visivamente; la x difatti è un seno, e ciò è sufficiente per disegnare il serpente. Ma la x è l’unica variabile da cui dipende effettivamente il controllo dello spazio. Quindi è monodimensionale.

A questo punto la y è libera, e la si utilizza per inserire i dati di illuminazione, quelli che avevamo previsto inserendo SurfaceFormat.Luminance16 nella definizione della texture. Convertiamo la y in intero a 16 bit Unsigned, e trasformiamo la struttura in un array. Quindi la variabile “tmp” è un array di interi a 16 bit Unsigned.

Non resta che inserire i dati dei punti di controllo nella Texture2D.

 

      // Carichiamo la texture di sfondo.

      var background = Content.Load<Texture2D>("background");

      /// Disegnamo la texture di sfondo. Questa operazione non dipende in alcun

 modo da come poi disegneremo il serpente.

 

      spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate,

                  SaveStateMode.SaveState);

      spriteBatch.Draw(background, new Rectangle(0, 0, 800, 600), Color.White);

      spriteBatch.End();

 

Per rendere l’applicazione più gradevole prevediamo uno sfondo, di nostro gradimento, che disegnamo con lo SpriteBatch. Ci preme ribadire che tale operazione non dipende in alcun modo da come disegneremo il serpente.

 

      /// Disegnamo il serpente con il nostro shader in alpha blending sulla

 scena precedente. L’operazione di disegno genera un serpente che passa

 per i punti di controllo della texture "controlPoints".

 

      new Vector3[] { new Vector3(0, 1, 1}

        .Select((color, index) => new {color, index})

        .ToList().ForEach(snake =>

        {

          fx.Parameters["SnakeColor"].SetValue(snake.color);

 

          spriteBatch.Begin(SpriteBlendMode.AlphaBlend,

                        SpriteSortMode.Immediate, SaveStateMode.SaveState);

 

          fx.Begin();

          pass(0, p => p.Begin());

 

          spriteBatch.Draw(controlPoints, new Rectangle(0, 100 + 100 *

                              snake.index, 800, 400), Color.White);

 

          pass(0, p => p.End());

          fx.End();

 

          spriteBatch.End();

        });

 

      base.Draw(gameTime);

}

 

Finalmente disegnamo il serpente!

Definiamo innanzittutto il colore con un vettore, un azzurro tenue.

Inseriamo il colore in ogni posizione di un array di Vector3 e lo modifichiamo con la tecnica dello Shader.

Il primo passo è impostare il parametro “SnakeColor” dello Shader con l’azzurro appena definito.

Iniziamo lo SpriteBatch con il metodo “Begin()” e avviamo il rendering dello shader con

 

fx.Begin();

 

che provoca, inoltre, la sostituzione automatica dello Shader di default nello SpriteBatch.

A questo punto non resta che aprire il passo zero di rendering con

 

pass(0, p => p.Begin());

 

e disegnamo sullo schermo la Texture2D.

Chiudiamo il passo e indichiamo che lo shader ha terminato la sua tecnica con

 

fx.End();

 

ricordandoci di chiudere anche le funzioni dello SpriteBatch con la funzione “End()”.

 

This entry was posted in Uncategorized and tagged , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>