Sviluppo PC e Console: una panoramica

Prima che inizi il flame, mi preme dire che in questo articolo non entrerò nel merito dell’eterna polemica “giochi console VS. giochi PC”, bensì vi racconterò qualche cosa sullo sviluppo per console confrontandolo con lo sviluppo per PC.

Ordinerò i pensieri per categorie.

MEMORIA

Come sappiamo, su PC abbiamo come minimo 2GB di memoria più la memoria video , mentre su console ne abbiamo un quantitativo decisamente inferiore: se consultiamo Wikipedia, scopriamo che le console next-gen hanno circa 512MB di RAM tra memoria video e memoria di sistema.

Sapendo questo, capiamo che l’approccio all’uso della memoria deve essere estremamente attento. Non solo: il modo in cui si tratta la memoria cambia completamente.

Per farvi un esempio: quando su PC si libera 1MB di memoria, non cambia praticamente niente, mentre su console è un quantitativo che viene accolto con gaudio e tripudio.

Inoltre il PC è una macchina dinamica, che potrebbe avere più o meno memoria, quindi un gioco potrebbe voler utilizzare quella memoria aggiuntiva. Nonostante questo, dovrà stare attento: il gioco non è l’unica applicazione che sta funzionando sul PC, quindi le risorse potrebbero essere già utilizzate in piccola o larga parte.

Su console questo problema non esiste: la memoria è una risorsa nota e possiamo utilizzarla come preferiamo.

Questo ovviamente cambia l’approccio: su console una soluzione conveniente è quella di utilizzare delle pool di memoria che allocano dei grossi blocchi per poi gestire le piccole allocazioni internamente, e queste pool possono essere fisse.

Su PC questa soluzione può funzionare, ma non sfrutterà il maggior quantitativo di memoria. Sarà quindi necessario avere un sistema che dimensiona le pool a seconda del sistema.

Inoltre, abbiamo il problema della frammentazione della memoria: siccome su PC la memoria potrebbe essere in qualunque stato, potrebbe succedere ad esempio che abbiamo 8GB di RAM di cui 4 liberi, ma il blocco più grosso è, poniamo, di 4MB. Se il gioco vuole allocare una pool da 16M, il sistema non troverà niente da usare e quindi utilizzerà la memoria virtuale, ossia l’hard disk.

A quel punto il nostro gioco inizierà a scattare orrendamente mentre l’hard disk verrà stressato.

Ci verrebbe da pensare che quindi su PC è meglio allocare la memoria in pezzi più piccoli, magari con l’allocatore standard. Questa soluzione comunque non va bene, poiché potrebbe aumentare ulteriormente la frammentazione in un ambiente in continuo mutamento: se una pool viene allocata in memoria, fintanto che è lì tutte le allocazioni saranno in memoria. Se invece uso l’allocatore standard, magari la prima allocazione va in memoria, poi va via e l’antivirus si prende quel pezzo. Poi ne arriva un’altra e finisce nella memoria virtuale. Capiamo che non vogliamo che questo accada: per quanto possibile vogliamo predicibilità nelle nostre azioni.

Per questo, nonostante tutto, su PC è comunque meglio usare delle pool la cui dimensione sappia adattarsi alla memoria fisica presente. Magari non automaticamente, ma semplicemente linkata alle varie impostazioni nelle opzioni (qualità della grafica, qualità dell’audio e così via).

L’utente cambierà le impostazioni in modo che il gioco vada bene sul suo PC. Oppure metterà tutto a manetta e quando tutto scatterà e laggherà sul suo Pentium 3 con 512MB di RAM, andrà sui forum gridando ai programmatori che non sanno ottimizzare.

CPU

Saltando l’ovvietà (ossia che i PC hanno tanti tipi di CPU mentre le console hanno lo stesso modello per tutta la loro vita), in genere i PC offrono dei set di istruzioni standard (altivec, MMX, SSE e così via). Le console, invece, hanno praticamente sempre delle CPU custom.

Non è che in realtà siano proprio custom, cioè costruite da zero apposta per la console: sono più che altro processori standard con istruzioni da altri modelli, con l’aggiunta magari di qualche istruzione aggiuntiva fatta apposta e una modifica alla memoria di cache.

Questa soluzione porta a scrivere codice per console in maniera molto più a basso livello rispetto a quello che si farebbe su PC.

Oltre a questo, il fatto che i processori per PC siano tutti diversi porta a scrivere codice in maniera “scolastica”, ossia pulita, ordinata e standard: sarà il compilatore a ottimizzare dove può. Su console, invece, l’avere una sola CPU significa che possiamo fare ottimizzazioni molto forti: ad esempio possiamo lavorare sull’eliminazione dei branch se essi sono un problema per quella CPU. Magari il compilatore avrà delle direttive speciali, che noi potremo usare abbondantemente  durante la stesura del nostro gioco. Questo porterà a codice di certo meno leggibile, ma nettamente più veloce per quella specifica CPU.

GRAFICA

Qui parlo di GPU e di memoria video. Scavalcando la questione “PC = tante schede, Console = una soluzione”, vale la stessa regola delle ottimizzazioni già vista per la CPU.

Oltre a questo, le console potrebbero offrire soluzioni impensabili nel mondo PC: ad esempio una soluzione che su PC dovrebbe essere eseguita su GPU, su console potrebbe essere conveniente farla su CPU. Pensiamo alla PS3 e le sue SPU.

La grafica è generalmente il punto su cui i giochi PC e console possono differenziarsi maggiormente, poiché i PC moderni (tipo Intel i7 con una scheda DX11, Win7 64bit con 8GB di RAM) sono nettamente più potenti delle console presenti al momento sul mercato.

Questo ovviamente impone dei problemi nel fare giochi multipiattaforma: fare una versione PC che utilizzi tutto il suo ben di Dio significa fare shader apposta, avere asset apposta e un’organizzazione della memoria apposta.

Questo impatta tutti gli stadi di produzione:

  • I grafici dovranno fare texture più grandi e modelli più dettagliati apposta per il PC;
  • I programmatori dovranno fare degli shader apposta, che su console potrebbero proprio non esistere (o potrebbero non essere shader);
  • Soluzioni “brute force” che su PC possono essere fatte senza particolari accorgimenti, su console potrebbero richiedere uno studio nettamente piu’ attento;
  • I tool dovranno gestire delle pipeline di conversione piuttosto differenti tra le varie versioni;
  • Gli screenshot andranno fatti dalle varie versioni per sottolineare le differenze. Allo stesso tempo non devono creare danno alle versioni meno spinte, poiché anche quelle versioni devono andare sul mercato;
  • Il marketing dovrà veicolare attentamente le informazioni delle varie versioni.

Capiamo facilmente che una versione PC dei giochi moderni che ne sfrutti appieno la potenza diventa un problema più che altro di costi.

AUDIO

Qui si entra in un campo incredibilmente variegato, forse ancora più  di quello della grafica.

La musica è un asset che può essere piuttosto pesante (per occupazione della memoria, per la decompressione e per il mix), per questo il suo trattamento può essere completamente diverso da macchina a macchina. Ad esempio, su Nintendo DS si usa molto il formato MOD (o XM), poiché consente una qualità eccellente con poca memoria usata e un basso costo computazionale per la decodifica. Purtroppo, ad oggi, è diventato un po’ difficile trovare persone che conoscano e sappiano usare i tracker (nonostante esistano programmi eccezionalmente moderni come Renoise).

Sulle console più potenti e sui PC, invece, non c’è problema nell’usare MP3 , OGG o qualunque formato di compressione che utilizzi modelli cocleari. Nonostante questo, potrebbero esserci delle situazioni (ad esempio per degli effetti sonori molto piccoli o particolari) in cui l’uso di WAV a 8 bit o a un basso sample rate potrebbe generare file più piccoli rispetto alle compressioni MP3, con la medesima qualità e senza il costo aggiuntivo della decompressione.

Tutti questi problemi, su PC, possono probabilmente essere del tutto ignorati (fintanto che l’audio non prende troppo tempo ad essere elaborato, caricato e decompresso), mentre su console sono un problema costante.

Direi che ho detto abbastanza per stimolare le vostre idee: per le domande vi lascio ai commenti.

Tutti i diritti di questo articolo sono riservati. Vietata la riproduzione.

Voglio fare il programmatore!

Allora: da piccolo giocavi coi videogiochi e dicevi “da grande farò il programmatore dei videogiochi!”.

Così hai fatto una scuola superiore con specializzazione in Informatica, lamentandoti che non spiegassero le DirectX. Poi, una volta diplomatoti, hai deciso di fare l’Università con un indirizzo informatico, sempre lamentandoti che non spiegavano mai le DirectX.

Ed ora che hai finito, hai deciso che non vuoi rinunciare come tutti i tuoi compagni (che sono andati a fare gestionali che stampano bolle e fatture) e vuoi davvero fare il programmatore di videogiochi.

Come devi fare? Non lo so, ma ti dirò come ho fatto io.

Vediamo un po’: come avrai capito, mio amato e fedele lettore (o lettrice, ma temo sia dura), l’iter sopra indicato è proprio il mio. Tutto quello che ho scritto non è però necessario, anche se utile.

L’ITIS è stata una scelta vincente: mi ha dato le basi, però mi dicono che non tutti sono buoni. Io feci l’ITIS di Busto (e ci ho pure insegnato), quindi, nel caso scegli quello :D.

L’Università è utile ma, credimi, devi fare qualcosa a casa. Io, nel mio piccolo, iniziai a comprare libri come “Game Programming Gems” o “ShaderX” e realizzai il mio primo engine casalingo.

Faceva schifo, e non disegnava altro che cubi e sfere. Erano come figli, quei cubi. Le sfere no, non voglio figlie femmine: al giorno d’oggi, le ragazze di 15 anni a 10 anni hanno già conosciuto gli uomini.

Ci sono delle scuole alternative, come l’AIV, ma tu spenderesti 7000 euro per andare in una scuola privata i cui professori sono completamente sconosciuti? Io no. Poi vedi tu: comunque una piccola percentuale dei suoi alunni ha trovato lavoro in ottimi posti. Quella piccola percentuale avrebbe trovato gli stessi posti senza frequentare quella scuola, comunque.

Perfetto, ora che abbiamo parlato della scuola, magari vorrai sapere cosa deve sapere un programmatore di videogiochi. L’ambito è talmente ampio che forse sarà bene specializzarsi: io sono specializzato in Rendering, quindi ti parlerò di quel campo.

Per fare Rendering è bene che tu sappia a dovere la geometria e la matematica ad esso relative: trigonometria, matrici, vettori e tutte queste belle cose. Un po’ di analisi non ti farà schifo quando vedrai le formule con gli integrali, da trasformare da continuo a discreto.

Poi devi sapere il C++. Lo so che nei forum il C++ è considerato vecchio e brutto e tutti gli altri linguaggi invece sono considerati belli. Purtroppo non ho mai visto un SDK che non fosse in C o C++, quindi c’è poco da fare: studialo; e lascia perdere le eccezioni, perché sono lente per le nostre applicazioni (mica facciamo gestionali).

Di certo ti interesserà un’API per fare rendering: guardati le DX9. Se hai voglia le DX10. Sì sì, le OpenGL sono libere ed il tuo amore per Stallman ti farebbe propendere per queste. Lo so bene, davvero, conosco i fans dell’opensource meglio di quanto tu possa credere. Però, purtroppo, se vuoi fare questo lavoro, ti consiglio di tenere il tuo amore per l’opensource come un hobby, per cui guardati le OGL nel tuo tempo libero.

Ed ora il meglio: un po’ di ingegneria del software. Non c’è niente di più schifoso di un software scritto di getto, perché è sempre un casino e quando va espanso e debuggato è odioso. Guardati i pattern, provane un po’ e vedi quali ti piacciono (parti dai GOF – “Gang of Four“, perchè erano in quattro. Noi informatici facciamo ridere un casino).

E poi? Dunque, fammi ricapitolare: la matematica te l’ho detta, il C++ pure, le DirectX pure, l’ingegneria pure.

Se hai già fatto tutto questo, che aspetti a mandarci il curriculum? 😀

Programmare bene

Da quando è uscito Crysis (ma anche da prima) continuo a leggere questa frase:

Crysis è programmato male perchè sul mio uber-pc scatta con tutto al massimo“.

Cosa possiamo dire di Crysis? Probabilmente che le stime di crescita della potenza dei PC sono state troppo ottimistiche.

Possiamo dire che sia stato programmato male? No. Perchè no? Perchè programmare bene non vuol dire programmare in modo che il gioco sia veloce. Quello è solo un punto, probabilmente nemmeno il più importante.

Vediamo cos’altro è importante. Un gioco è ben programmato se segue le regole della buona programmazione che valgono per ogni software:

Codice documentato;

– Accoppiamento tra i moduli al minimo possibile;

Sviluppo il più possibile Data-Driven;

Chiarezza;

Solidità;

Ottimizzazione.

Prima si affronta il design, poi si scrive il codice nella maniera migliore possibile in modo che rispetti le richieste e infine, se il collo di bottiglia si trova nei nostri listati, si procede con l’ottimizzazione.

Consideriamo in dettaglio tutti i punti.

CODICE COMMENTATO

Se il codice non è commentato e la documentazione non viene scritta, qualora il suo autore dovesse abbandonare l’azienda, con lui andrà perso anche il suo know-how. Questo non deve succedere, perché una qualunque modifica al software nella parte di sua competenza sarebbe estremamente complessa e di certo genererebbe grossi bug di arduo tracciamento. Il codice va dunque commentato e la documentazione redatta. Crysis soddisfa questo punto? Siccome l’engine viene venduto, possiamo desumere che venga venduto con documentazione annessa. In passato ho lavorato con Renderware e tutto era documentato, sia nel codice sia nella documentazione allegata. Non vedo perchè Crysis avrebbe dovuto toppare su questo punto, ma siccome non possiamo saperlo, lasciamolo lì dov’è.

ACCOPPIAMENTO TRA I MODULI AL MINIMO POSSIBILE

Supponiamo che la sezione dell’input ad un certo punto voglia usare un pezzo del gioco, che gestisce magari il suono delle collisioni tra due oggetti.

E’ così creato un accoppiamento tra due moduli. Ciò può portare a mille problemi: ad esempio se modifico la collisione, di colpo l’input potrebbe non funzionare più. Per tale ragione si cerca di tenere gli accoppiamenti al minimo, limitandoli a ciò che è logicamente collegato ed ai tipi base.

Un esempio estremo: se ho un dato condiviso da più moduli e uno di questi lo modifica, magari il programmatore nemmeno immagina che questo viene utilizzato da qualcos’altro, quindi questo qualcos’altro non funzionerà più. Errori del genere portano via giorni. Crysis è a posto o ha tanti accoppiamenti? Se leggiamo le feature, notiamo che non può essere che con pochi accoppiamenti: una tale struttura permette un fortissimo riutilizzo dei componenti (giacché vivono in maniera autonoma). Già la sola integrazione perfetta con un ambiente di authoring ci fa capire che i moduli sono distinti.

SVILUPPO IL PIU’ POSSIBILE DATA-DRIVEN

Ed è qui che i fanatici dell’ottimizzazione gridano: una gestione data driven è più lenta di una gestione code-driven. Questo perché il dato va letto, interpretato e gestito, mentre se piazzato direttamente nel codice, questo è già pronto quando il gioco si compila.

Ad esempio: di che colore vogliamo le scritte sullo schermo? Se mettiamo il colore nel codice, questo sarà nell’eseguibile, pronto ad essere assegnato quando richiesto. Se lo mettiamo in un XML, questo andrà letto e interpretato, per poi prendere il dato e metterlo nella variabile dopo, magari, una conversione di tipo.

Quindi, perchè si fa? In primo luogo perché se la lettura del file si realizza in maniera oculata (ad esempio con un parser SAX invece che DOM per l’XML, oppure con un parsing in memoria per un binario), allora allungherà i tempi di startup di un tempo risibile, e inoltre in secondo luogo renderà il gioco immensamente più modificabile e tunabile per i designer ed i grafici (oltre che per l’utente). Pensiamo solo alle mesh: cosa succederebbe se il programmatore mettesse tutte le posizioni dei vertici nei sorgenti? Succederebbe che se al grafico una mesh non piace, bisognerebbe cambiare il dato a mano e ricompilare; ovviamente non ha senso: ha molto più senso che il grafico modifichi la mesh incriminata in 3dStudio Max, la riesporti e la ricarichi in gioco. Oppure pensiamo ad un designer che voglia inserire i nomi dei giocatori in un titolo calcistico, o ad un programmatore che intenda scrivere un nuovo shader.

Insomma, i benefici nei tempi di sviluppo sono talmente grandi che le perdite in fase di inizializzazione vengono del tutto riassorbite. Se consideriamo che il concetto di gioco data-driven risultava già ben presente ai tempi del C64, possiamo facilmente notare quanto sia importante.

Crysis è provvisto di tale approccio? In maniera massiccia: abbiamo editor per gli effetti, editor per gli stati, editor per i menù, editor per il gioco, editor per l’AI, editor per l’audio, editor per tutto 😀 Non c’è niente di hardcodato, tutto è data driven, per la massima modificabilità senza dover ricompilare alcunché. Questo ne conferma anche la modularità: il fatto che possiamo mettere vari moduli per creare degli effetti ci fa intuire che essi siano ben distinti e non connessi tra loro.

CHIAREZZA

Quando si scrive un programma, lo si deve fare pensando che saranno degli occhi umani a leggerlo. Il compilatore non ha molto interesse nella chiarezza: lui prende tutto quello che la grammatica del linguaggio accetta.

Se iniziamo a indentare il codice a caso e a dare alle variabili nomi inconsistenti (come ad esempio “deltaT” a variabili che indicano il tempo assoluto), allora andremo incontro a sessioni di debug molto faticose. Parimenti, se abusiamo del preprocessore dovremo affrontare simili avversità.

Crysis soddisfa questo requisito? Boh, spero che i lead controllino il codice, ma immagino di sì 😀

SOLIDITA’

Un software è solido quando, di fronte a tutti gli usi previsti dall’interfaccia d’uso, dagli input e dagli output, rimane funzionante e operativo nei tempi richiesti. Se di fronte ad una sequenza di input considerata corretta, il software entra in deadlock o crasha, allora non può essere ritenuto solido.

La solidità viene valutata con test mirati a stressare il sistema, come ad esempio saturare l’input, oppure lasciare il gioco a girare per giorni e giorni.

Come si ottiene la stabilità? In primo luogo con la disciplina: tutte le variabili allocate vanno prima o poi (quando serve) disallocate. Tutte le risorse hardware vanno rilasciate quando non servono più e tutto il sistema deve pensare di avere una serie di check per valutare che queste condizioni siano rispettate. In genere un gioco stabile è un gioco programmato con attenzione, come tutti i software e comunque per qualunque opera di ingegneria. Un ponte non crolla se è stato progettato con attenzione, giusto?

Quando, d’altro canto un software è instabile? Quando di fronte ad una precisa sequenza di operazioni considerate “safe” il programma inaspettatamente si blocca (nel caso in cui un software funzioni in multithreading, questa sequenza potrebbe non essere sempre riproducibile con facilità, rendendo il debug un autentico bagno di sangue).

Per evitare tali eventualità, si cerca di tenere il codice il più semplice possibile, tralasciando voli pindarici o ottimizzazioni spinte sulla macchina (tanto quelle ottimizzazioni il compilatore le sa fare meglio di noi, quindi perché preoccuparci?).

In Crysis è così? Non possiamo saperlo. Ma il gioco è stabile? Per quanto ho visto, funziona su una pletora di configurazioni senza crashare per molte ore di fila. Un miracolo, su PC.

OTTIMIZZAZIONE

Quando si trova un collo di bottiglia, si cerca di eliminarlo ottimizzando. Già in passato abbiamo visto che l’ottimizzazione sull’assembly senza cambiare algoritmo porta a guadagni tanto risibili da non avere senso.

Sì, amighisti, sì, lo so che sul vostro Amiga girava Lionhead: anche lì non pensiate che l’ottimizzazione sia stata fondamentalmente sulla macchina, bensì è stata effettuata su algoritmi custom e appositamente sviluppati per l’uso richiesto. Sono quelle le cose che consentono di ottimizzare tanto e davvero.

Vediamo: se c’è un collo di bottiglia sul processing dei pixel shader che si fa? Dove si controlla: nei conti degli shader o nei campionamenti delle texture? Se nei campionamenti, la scelta giusta consiste nell’ottimizzare le dimensioni delle texture: texture piccole per la roba lontana o piccola. Se è nei vertex shader, stessa cosa: sono i conti o il fatto che le mesh hanno una barca di vertici? E così via, per tutti i reparti del gioco.

Per Crysis, proviamo a pensarci: dov’è il collo di bottiglia? Ovviamente sui dati: si vuole mostrare sullo schermo TROPPO. Se vi fosse stata la necessità di andare più veloci, al fine di rendere l’insieme più gestibile, allora si sarebbe proceduto ad una scrematura, adoperando mesh meno dense e così via. Non può essere altro: il solo fatto che tutta quella roba SI MUOVA e sia giocabile in tempo reale ci fa capire che è stato fatto tutto nel modo migliore.

E la faccenda dei multicore? Beh, la solita fregnaccia da redattore (anche se in questo caso è peggio: da redattore esperto di hardware). Ne parliamo prossimamente, ok?

CONCLUSIONI

In definitiva che possiamo dire? Possiamo sostenere che Crysis sia programmato male? No, perché quello che si vede è fatto in maniera eccezionale, mentre quello che non si vede non lo possiamo sapere.

E ora via col flame!

Oscurità proiettate

Le ombre sono una cosa divertente: se non ci fossero, i progettisti di sistemi per il rendering sarebbero felici e contenti.

Purtroppo ci sono, quindi quando un giocatore guarda lo schermo e non le vede, grida “Non ci sono le ombre! Questo gioco è becero sterco!”.

Perchè ogni tanto i giochi non presentano le ombre dei personaggi (o degli edifici) oppure, quando lo fanno, non sono poi bellissime?

Il motivo è semplice: fare le ombre è molto complesso.

Vediamo un’ombra vera:

In questa immagine possiamo notare varie peculiarità: in primo luogo l’ombra non è completamente nera, perché la luce, nell’aria, non viaggia bella tranquilla ma incontra continuamente pulviscolo ed altre belle cose che la riflettono di qua e di là, quindi anche dentro l’ombra.

In più notiamo che l’ombra dell’uomo è molto netta, mentre quelle delle… ehmmm… vabbè, quel che è, sono molto meno nette.

Perchè? Per lo stesso motivo visto prima: la precisione dell’ombra cambia a seconda della distanza del soggetto dall’oggetto su cui viene proiettata: più è distante più i raggi saranno deviati. Da ciò deduciamo che in un luogo senz’aria, le ombre saranno perfette. Così è, infatti, se guardiamo le foto dalle missioni lunari.

Come possiamo fare a simulare tutto questo? Il proiettare raggi che intersecano cose è fuori discussione: si chiama ray-tracing e in tempo reale, al momento non è una cosa semplice. Anzi, diciamo che al momento ce lo scordiamo (anche se Carmack dice di avere qualcosa in mano a riguardo). Esiste un engine di rendering per Quake 3 in raytracing fatto da appassionati, ma purtroppo va un po’ pianino 😀 . Tipo 1 frame ogni 30/40 secondi.

Quindi, siccome il proiettare i raggi è fuori discussione, come facciamo? Dobbiamo inventarci qualcosa che ci risolva il problema.

In più, facendo così, avremmo ombre nettissime ad ogni distanza, a meno di calcolare la deviazione della luce a causa del pulviscolo (si chiama “scattering”). A questo punto però ci sarebbero tanti bei problemi di precisione. Al momento il real time è fuori discussione.

Possiamo, per ora, utilizzare tre differenti soluzioni:

Proiezione Planare

Purtroppo non so se si chiama così, questa tecnica, poiché non è tanto efficace da esser degna un nome. Diciamo che consiste nel proiettare tutti i punti di un modello 3D su un piano, lungo la linea della direzione della luce:

Questo sistema mostra subito il fianco all’hard core gamer: in pratica se c’è un oggetto sul piano, la tecnica non lo vedrà e andrà semplicemente sul piano.

Certo, il piano può essere inclinato, però comunque se c’è un muro su quel piano, l’ombra non ci andrà sopra. Questo sistema può andare bene per i giochi di calcio, dove le ombre dei nostri miliardari preferiti sono proiettate praticamente sempre e solo sul piano di gioco.

Shadow Volumes

Carmack le ha amate. Non è il solo, c’è anche Starbreeze che ama questa tecnica: sia Chronicles of Riddick che The Darkness usano questo sistema.

Vediamo come funziona: per prima cosa supponiamo di avere il nostro modello 3d (tipo un pallone) ed un piano comunque modellato (cioè non una formula matematica, ma proprio un modello 3d a forma di piano). Ci mettiamo su anche un muro. Ora mettiamo una luce in modo che l’ombra cada sia sul piano che sul muro.

Fatto questo, ci sono da fare tre passaggi:

1) Si disegna la mesh, tirando tutti suoi vertici che “non guardano” la luce molto in là. Questo creerà un volume (da qui il nome della tecnica) che noi speriamo intersechi tutti gli oggetti della scena. In questo passaggio diremo all’hardware di disegnare solo i triangoli che guardano in camera (diciamo quelli “davanti” e non quelli “dietro” alla nostra palla)

2) Si fa un’altra bella passata, disegnando quelli “dietro”. Queste passate non vengono fatte sullo schermo, ma su una struttura chiamata “Stencil buffer”. In pratica lo stencil non si becca il colore, ma se qualcuno scrive su un dato pixel, lui somma “1” al valore di quel pixel. Così sappiamo se qualcuno ha scritto lì.

In seguito “aggiungiamo” la prima “passata” allo stencil, mentre “sottraiamo” la seconda; ci rimarrà così nello stencil una pletora di numerelli che rappresentano proprio l’ombra.

Fatto questo, disegnamo un bel quadratone scuro su tutto lo schermo, premurandoci di dire all’hardware di disegnare solo dove lo stencil è diverso da 0. Così facendo disegneremo la nostra ombra perfetta.

Problemi: in primo luogo dobbiamo disegnare l’oggetto tre volte: una volta per farlo vedere normalmente ed altre due per lo stencil. Fortunatamente qui l’hardware ci viene in soccorso: le moderne schede fanno le due passate in un colpo solo.

Però se vogliamo farlo su tutti gli oggetti della scena, dobbiamo farlo con tutti gli oggetti. Se lo facciamo anche al mondo, siamo fritti. Ma non è finita qui: se inizio a proiettare questi volumi in maniera massiccia, rischio di saturare tutta la sezione finale della pipeline, quella che disegna i pixel, perché in pratica devo disegnare una quantità esagerata di pixel (nello stencil) a causa di questi volumi. Quindi abbiamo due colli di bottiglia.

In più ci sono problemi con modelli molto poco densi poligonalmente, oppure con forme allungate (come un parallelepipedo bello allungato).

Questa tecnica è stata usata fondamentalmente solo da Doom3 (e da chi usa il suo motore) e dai giochi Starbreeze, poiché presto si è rivelata inadeguata: oltre ai due colli di bottiglia, presenta enormi difficoltà a fare ombre sfumate sui bordi (la cosa richiederebbe ulteriori passate. C’è una demo ATI che lo fa in 16 passate 😀 ).

Shadow Map

Questa tecnologia è quella che al momento risulta più studiata in assoluto: in genere in tutti i seminari sulla computer grafica sono presenti delle dimostrazioni dei passi avanti compiuti utilizzando questa procedura.

Come mai? In primo luogo la tecnica dello shadow mapping è di base più leggera: richiede solo un rendering in più oltre a quello standard. Richiede però un hardware capace di fare conti per vertice e per pixel. I pixel shader, per intenderci. Qualche anno fa (diciamo il 2000, la prima volta in cui lessi di questa tecnica) questo poteva essere un limite, ma oggi non più.

Il sistema basilare funziona così:

1) Facciamo il rendering della nostra scena.

2) Facciamo il rendering della scena, mettendo la camera dove sta la luce ed angolata nella stessa direzione di questa.

A questo punto confrontiamo gli Z-Buffer dei due rendering: se lo Z-Buffer della luce in un dato punto è minore (o maggiore, a seconda della piattaforma) dello Z del rendering normale, allora quel punto è in ombra.

Spiego velocemente cos’è lo Z-Buffer: è una superficie grande come lo schermo (se lo schermo è 1.024 x 768, allora anche lo Z-Buffer è 1.024 x 768). In questa superficie viene salvato il valore della coordinata Z di uno punto sullo schermo. La coordinata Z è la profondità. Questa superficie serve per fare lo Z-Test: quando disegnamo qualcosa sullo schermo, prima di farlo, controlliamo lo Z-Buffer. Se lo Z che vogliamo scrivere è minore (o maggiore; comunque più “vicino” alla camera) di quello che c’è già scritto, allora vuol dire che il punto che vogliamo scrivere è visibile perchè non c’è niente davanti. Se no, no. Questo ci consente di disegnare gli oggetti nell’ordine che vogliamo, senza che quelli disegnati dopo coprano quelli disegnati prima.

Dicevamo che la tecnica dello shadow mapping consiste fondamentalmente, quindi, nel disegnare dei punti derivati da una elaborazione dello Z-buffer.

Per fare questo, però, dobbiamo disegnare il mondo anche dal punto di vista della luce: operazione che si effettua su di una texture. Ovviamente, più questa texture è grande peggio è. Fortunatamente a noi interessa lo Z-Buffer di quel rendering, quindi possiamo anche disegnare tutti gli oggetti bianchi, senza pixel shader. Quando si fa così, gli hardware moderni fanno fare al rendering una specie di “corsia preferenziale”, saltando un bel po’ di stage nella pipeline, andando velocissimo.

Quali sono i problemi? Ci sono delle angolazioni sfortunate, che danno un sacco di problemi (li notate bene bene in Devil May Cry 4). In più ci sono sempre delle angolazioni cha fanno vedere i pixelloni giganti (lo notate bene sempre in Devil May Cry 4 :D). Nonostante questo, la ricerca continua perchè mediante questa tecnica è possibile fare delle buone ombre sfumate sui bordi (ne vediamo degli esempi in Crysis e GTA4), oltre che, mediante modifiche e moglioramenti, di fare delle buone ombre su tutti gli oggetti in scena.

Ma perchè ancora ad oggi le ombre non sono bellissime come quelle vere? O almeno come quelle nei film di animazione in CG?

Perchè ovviamente anche fare i conti di cui sopra costa. Quando fare le ombre inizia a costare, diciamo 5 millisecondi, allora può darsi che inizi a costare troppo. I rendering per quei film prendono bel più di 5 millisecondi 😀

Al momento il top della tecnologia è Crysis e ci mostra ombre di certo belle, ma con questi difetti:

  • Non è molto diversa la sfumatura tra oggetti lontani ed oggetti vicini
  • Qualche problema di aliasing sugli oggetti lontani
  • Puntini e dithering piuttosto evidenti da vicino
Perchè tutto questo? Perchè altrimenti non ci starebbe dentro con i tempi.
La ricerca prosegue sempre, e se solo confrontiamo le ombre di GTA4 con quelle di Assassin’s Creed notiamo un enorme incremento.
Quanto dovremo aspettare per non vedere più i puntini ed avere delle ombre perfette, magari da più sorgenti di luce e con un diverso grado di sfumatura? Non molto: le ombre sono uno dei campi più studiati della grafica real-time, quindi stimo che entro tre anni il problema dei puntini sarà sparito: credo che un aumento dinamico dei conti che si fanno per farlo possa ridurlo moltissimo, ma per eliminarlo bisognerà pensare in un altro modo, ad esempio un post processing dell’ombra stessa. Tutto questo richiede altro tempo, che al momento non c’è.
Non esiste niente di definitivo, quindi, per quanto riguarda questo argomento. Ma siate fiduciosi 🙂

La Parte più Difficile

In attesa di finire un grosso articolo, vi sollazzerò con qualche rilessione.

Vi siete mai chiesti quale sia la parte più complessa nel fare un gioco? Io, quando ero più giovine, talune volte me lo chiedevo.

In generale, ho sempre ritenuto che la parte complessa fosse l’engine 3D, o comunque tutta la parte grafica.

Questo dev’essere stato un po’ il pensiero di tutti quanti, difatti, cercando su Internet, si trovano una valanga di engine già fatti (tra cui mi viene in mente Ogre e Irrilicht), ma bel poco di tutto il resto.

Oggi è spuntato un nuovo tema: gli engine fisici. Sviluppare un engine fisico è una cosa praticamente impossibile se non si ha una formazione dal punto di vista della fisica, ma fattibile (se vengono dati il giusto tempo e le giuste risorse) da chi invece ce l’ha.

Io non ho una formazione fisica adeguata ed immagino che molti miei colleghi non l’abbiano, difatti ci sono molti engine fisici disponibili.

Ma il resto? Il resto è tanto banale? No, assolutamente. Spesso leggiamo sulle riviste frasi come: “Che ci voleva aggiungere questo e quello? E’ banale!”

Beh, purtroppo, non esiste niente di banale. Purtroppo, l’ultima frase non era una frase fatta.

Una cosa considerata semplice e sottovalutata (fino all’avvento del Wii) è l’input, ad esempio.

Proviamo a pensarci: l’input è una cosa che avviene in maniera del tutto asincrona (ossia, quando uno schiaccia, schiaccia 😀 ), in più cambia da piattaforma a piattaforma.

Su PS2, ad esempio, abbiamo due stick analogici, un po’ di tasti analogici e qualche tasto binario (schiacciato/non schiacciato).

Su 360 abbiamo una roba simile, ma sbilanciata sui tasti binari. Su PC abbiamo un milione di tasti binari ed un mouse.

Poi arriva il Wii con i suoi due accelerometri, il suo puntatore ed i suoi tasti.

In più l’Input varia a seconda di dove siamo: nei menù ci sarà un tipo di Input, mentre nel resto del gioco un’altro tipo.

La gestione non è banale.

Ho detto menù? Ecco un vero massacro.

Gestire i menù è un problema sottovalutato, difatti non si trovano molte librerie. Le uniche buone, tra l’altro, sono a pagamento. Un SONTUOSO pagamento.

Perchè?

Perchè creare menù non significa solo scegliere qualcosa su schermo, significa tutto questo:

  • – Dare ad un grafico uno strumento per fare i menù
  • – Agganciare gli eventi dei menù a del codice
  • – Gestire una serie di controlli standard.
  • – Andare su più piattaforme.

Un’idea è usare Flash: il formato è noto e i grafici lo conoscono già.

In più non bisogna farsi lo strumento per i grafici.

Purtroppo facendo così complica molto la possibilità di fare menù con elementi 3D: Flash non lo supporta.

Credo di poter stimare che una gestione dei menù e degli overprint (ossia la roba 2d che sta sul 3d, tipo le barre di energia) copra tranquillamente un buon 20% nel making di un gioco ed un 40% del making dei tools.

Un’altra cosa spesso sottovalutata è la faccenda dei save-game. I Save-game ad oggi sono considerati assolutamente standard ed un gioco che non li abbia è considerato “limitato”, almeno su PC.

Ma perchè su PC i save-game sono fattibili “in ogni momento” mentre su console in genere ci sono i checkpoint?

Semplice: anche i save-game non sono un problema banale 😀

Pensiamoci un secondo: se voglio salvare in ogni momento, in un FPS, dovrò salvare la mia posizione, il livello in cui sono, la posizione dei nemici ed il loro stato, tutti i miei dati e tutto quello che concerne la roba già fatta (oggetti, dialoghi già seguiti e cose simili).

Se ho i checkpoint, salvo in che checkpoint sono, i miei dati ed ho risolto 🙂

La differenza sta nella dimensione: sulle console vecchie (Ps2 in giù) lo spazio per i savegame era molto limitato.

Poi ci sono i casi in cui si può salvare dove si vuole anche su console. Questo dimostra che in questo caso i save-game sono stati oggetti di analisi e di ottimizzazione.

Niente di banale, quindi.

Purtroppo il mondo è così: guardiamo i poligoni e pensiamo che il subsurface scattering (che parolone 😀 Usatelo per stupire gli amici) sia la cosa più difficile che vediamo sullo schermo, mentre probabilmente ha richiesto 3 giorni.

Il sistema che disegna e gestisce il menù dei personaggi di Final Fantasy 6, invece, probabilmente di giorni ne ha richiesti almeno il triplo (se non il decuplo).

Beh, dopotutto tutti noi lo sappiamo: il mondo è ingiusto 🙂

L’Audio, il senso vituperato

Io amo la musica. Mi ci sono dedicato anima e corpo per più di quindici anni, raggiungendo risultati che non avrei mai sperato, tra cui il Master di Chitarra Rock di Begotti e il mio primo disco pubblicato.

La amo proprio, la musica. Pensate che talune volte, alcuni giochi non tanto belli vengono da me salvati perchè hanno una bella musica, o un bell’audio in generale.

Non la grafica: quella non mi è mai bastata per salvare un gioco mediocre.

Sembra che io sia l’unico, altrimenti non ci spiegheremmo perchè i computer vengono venduti con schede video pazzesche e schede audio poco migliori della Sound Blaster.

Non capisco: sono dell’idea che l’audio sappia creare tutti i sentimenti possibili, anche ad occhi chiusi. Proprio l’altro giorno giocavo a “Scratches”: bene, in questo gioco la grafica è poco più che mediocre, mentre l’aspetto tecnico è vecchio di almeno dieci anni.

Tutto è fermo, statico, come non mi capitava di vedere dai tempi di Myst. L’audio è splendido. Questo gioco mi piace.

Quello che noto molto spesso è che i giocatori, piuttosto spesso, nemmeno giocano con l’audio acceso. Senza senso.

Ultimamente ho provato un po’ di giochi con un audio molto buono: Persona 3, Mario Galaxy e Portal. Nel contempo ho provato una barca di giochi dove l’audio a stento c’è, ma la grafica quella sì che è una figata.

Prendo un caso ecclatante: Eternal Sonata. Belle le musiche, vero? Certo! Sono uguali a quelle di Tales of Symphonia, che erano belle, quindi sono belle.

Gears of War? Quattro effetti sonoro e due Jingle.

Halo 3? Belle le musiche, non c’è dubbio. Sono le stesse da dieci anni.

Dopotutto non posso neanche farne una colpa: quando i distributori rilasciano qualcosa, rilasciano degli screen, così i giocatori potranno trovarci tutti i difetti del mondo e dire che il gioco fa schifo.

Oppure potranno difenderlo a spada tratta perchè si chiama PES.

Di certo non si mettono a postare frammenti di audio: chi se ne frega? Gli utenti di oggi potrebbero anche non averle le orecchie: non noterebbero nessuna differenza.

La Lingua del Ludo

Talune volte frequento il newsgroup it.comp.giochi.sviluppo e spesso leggo l’annosa domanda: “Che linguaggio si usa per fare videogiochi?”

Un tempo la risposta sarebbe stata semplice: “Il C++, mio buon amico”. Anni ancora prima, la riposta sarebbe stata ancora più sgraziata “Il C, per l’amore diddio”.

Se andiamo ancora nel passato, allora sarebbe stata “L’assembly, you fuckin’ noob”.

E oggi? Finalmente possiamo dire “dipende”. Aaah la libertà! Tanto bella quanto pericolosa.

E da cosa dipende? Dipende da cosa dobbiamo fare. Non parlo della “complessità”, ma della “mansione”.

Dobbiamo fare Tool, l’Engine o il Gioco?

Nei tre casi le nostre competenze richieste saranno diverse. Se facciamo l’Engine, ad esempio, non si può scappare: tutti gli SDK di tutte le console sono in C o C++.

Ma se facciamo il gioco? Beh, in quel caso, immaginiamo di utilizzare “Virtools”: questo strumento si programma con degli script.

Oppure, ancora, Macromedia Director o il sistema “Vicious Engine“: tutti questi sistemi si programmano con degli script.

Oppure supponiamo che l’engine sia stato fatto in modo che tutte le chiamate si possa usare, chessò, in Phyton: così facendo il programmatore del gioco deve conoscere Phyton e non il C++.

Se devo fare i Tools, ho varie scelte: potrei dover conoscere il linguaggio che usa l’engine (il C++, o il binding il Phyton), oppure, se devo fare roba per 3d Studio Max, dovrei conoscere MaxScript.

Ed ecco fatto! Abbiamo un bel po’ di figure professionali che non devono sapere il C++, ma lavorano nei videogiochi.

Se poi aggiungiamo che magari le pagine di menù vengono fatte in Flash, opplà! Abbiamo miliardi di papabili Falshisti che potremmo usare per programmare le nostre pagine.

Porto qualche esempio, in modo da capirci: Valve software ha l’engine in C++, ma i tools in C#. In questo modo ha i tools fatti in un linguaggio che toglie moltissimi problemi di gestione della memoria, è molto produttivo e consente di fare interfacce grafiche in pochissimo tempo.

Battlefield è fatto in larga parte in Phyton, mentre l’AI e tutti gli eventi scriptati di Crysis e Far Cry sono fatti in LUA, un altro linguaggio di scripting molto in voga.

Tutte le avventure grafiche Lucas sono state fatte con lo storico SCUMM, un sistema a script.

Dopo tutto questo discorso, capiamo che, ad oggi, la risposta “Il C++ o Morte!” E’ obsoleta. Obsoletissima!

E l’ottimizzazione storica che fa il C++? Eeeh, ci ricolleghiamo al precedente articolo, ma temo che ci sarà ancora da parlare parecchio su questo argomento.