Arsludicast 202: Non siamo pavidi perché siamo Arsludici

Come il finale di un qualunque Nightmare, in cui Freddy sembra che sia morto ma invece no, torna ArsLudicast, in una puntata tutta dedicata al mondo dell’horror!

Dietro richiesta del nostro lettore “Il cane”, la redazione si cimenta in un’analisi del modo in cui i videogiochi cercano di farci paura. I nostri splendentissimi redattori Simone Tagliaferri, Alessandro Monopoli e Vittorio Bonzi, carenti dell’Anelli ma confortati dal Rud, passeranno in rassegna giochi horror vecchi e nuovi; e finalmente vi diranno cosa è bello e cosa no, ché loro sì che lo sanno.

Troverete un flame, una telefonata, introduzioni duplicate e misteriose alle recensioni singole e finalmente ne saprete di più sulle sorelle del Rud e del Monopoli.

In mezzo a tanto barbarico favellare, si parla di:

Vi ricordiamo che se volete assistere come ospiti al podcastproporre un argomento di discussioneo, perché no?, proporre un arrangiamento al Monopoli, potete farlo contattandoci a: arsludicast@arsludica.org o redazione@arsludica.org, oppure utilizzando l’apposito thread sul forum!

Se volete contribuire al nostro progetto potete fare qualche acquisto sul nostro aStore, garantendoci una donazione.

Scaricate l’episodio:

 

Brano in Apertura:
Baldur’s Gate 2, Dragonbattle music, di Michael Hoenig, Howard Drossin arrangiata ed eseguita da Alessandro Monopoli

Brano in Chiusura:
Theme of Laura di Akira Yamaoka

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.

operation-flashpoint-2-dragon-rising-1

Passato vs Presente

Quando iniziai a giocare coi videogiochi, essi erano così:

(Questo è Blue Print, uno dei primissimi titoli a cui ho giocato).

Oggi, invece, i giochi sono così:

È solo la grafica che è cambiata, oppure anche qualcos’altro? La risposta, per quanto possa essere banale, è “TUTTO”.
È cambiato tutto, appunto.

Ciò di cui desidero parlare con voi in questo noioso articolo è il modo in cui le feature e la produzione erano trattate un tempo e come vengono, invece, trattate oggi.

Prendiamo Zork, il primo. Come si faceva ad avere una  casa con una cassetta della posta davanti alla porta?
Si scriveva: “Vedi una casa  con una cassetta della posta” (per la precisione: “This is an open field west of a white house, with a boarded front door.
There is a small mail box here”).
Tutto qui. Quanto richiedeva? Probabilmente qualche secondo.

Andiamo avanti nel tempo e facciamo finta di essere negli anni ’90 e di stare facendo un’avventura grafica in 320×200 con 256 colori che girerà sui 286.
In quel caso, la stessa scena potrebbe richiedere più di una tavola 320×200. Diciamo che richiederebbe qualche giorno di lavoro.

Andiamo ancora avanti, ora siamo nel 2010. Dobbiamo fare la stessa cosa, ma per una console next gen, quindi tutto 3D e tutto al meglio.
Ci sarà il modello 3D della casa, i fondali, le animazioni dell’erba, qualche animalaccio volante per dare movimento, la musica e gli effetti sonori.
Quindi ne abbiamo per molti giorni, diciamo una settimana di lavoro spartito fra vari professionisti.

Capiamo da soli cosa voglia dire aggiungere un asset al giorno d’oggi rispetto a ieri.

Ma passiamo alle feature.

Ora vogliamo che il nostro personaggio possa interagire con l’ambiente, quindi vogliamo che possa aprire la cassetta e che dentro ci trovi una lettera, e che la lettera reciti qualcosa.

Passato remoto: io giocatore scrivo “apri cassetta della posta”, il programma riporta “c’è una busta”. Scrivo “prendi busta”, poi “leggi lettera” e il programma riporta sullo schermo il contenuto della lettera.

Passato non così remoto: appaiono due frame di animazione per la cassetta delle lettere e la lettera appare nel mio inventario, con un disegnino. Scelgo “Leggi lettera” e un testo sulla testa del mio alter-ego (o avatar, o forse no) digitale la reciterà per me.

Presente: Il personaggio si avvicina alla cassetta, la apre con un’animazione. Scelgo “raccogli lettera”. Parte un’animazione con tanto di regia curata che mostra il mio personaggio prendere la busta. La busta compare nel mio inventario, dove posso selezionare tutti gli oggetti e farli ruotare sullo schermo, per vederli bene. Da lì scelgo “apri busta” e la voce di un attore professionista mi dice che c’è una lettera. Allora scelgo “leggi lettera”.
Parte una musica che descrive uno stato d’animo, mentre inizia l’animazione del personaggio che apre la busta e ne legge il contenuto, il tutto doppiato da un attore professionista.

Quindi una feature semplice come “raccogliere la lettera, aprirla e leggerla” ha dei costi così distribuiti:
Passato remoto: 3 minuti di tempo
Passato non così remoto: uno o due giorni
Presente: circa una settimana (o più) di vari professionisti ed esterni.

Tutto questo solo per uno degli oggetti del gioco e senza contare la parte di programmazione, che ovviamente al giorno d’oggi è infinitamente più complessa di quanto fosse in passato.

Da qui capiamo perché, al giorno d’oggi, i giochi diventano sempre più mirati e specifici nel loro scopo.
Ogni feature costa così tanto che si preferisce averne poche e tunate alla perfezione perché siano eccellenti sotto ogni aspetto, piuttosto che averne tante ma realizzate in maniera che non soddisferebbe né il pubblico né la critica.

Per capirci: oggi avere centomila animazioni di due frame come in passato porterebbe alla disintegrazione del titolo da parte della critica e del pubblico. Meglio averne meno, ma bellissime.
Avere, al giorno d’oggi, dei giochi con tantissime feature come i giochi degli anni ’90 e primi 2000 sarebbe difficile, costoso e probabilmente non conveniente dal punto di vista commerciale.
Dobbiamo quindi strapparci i capelli come Homer quando scopre che Marge è incinta? Dobbiamo giocare a Jagged Alliance in eterno perchè non ci sarà mai niente di altrettanto bello?

La risposta, miei amatissimi, è no; perché, piaccia o meno, i giochi di oggi sono migliori dei giochi del passato. L’attenzione che c’è oggi dietro ogni singolo aspetto di gioco non è nemmeno paragonabile a quella che c’era in passato: oggi un gioco viene finito quattromila volte prima che si dica che va bene e quando va bene, tutti gli elementi sono (solitamente) esattamente come il giocatore se li aspetta: comodi e al nostro servizio.

Pensiamo al passato: quante volte siamo passati sopra ai difetti perché il gioco era tanto bello da non lasciarsene offuscare?
Beh, se ci pensiamo, oggi i difetti non si accettano più: basta un piccolo elemento per rendere un gioco brutto, perchè al giorno d’oggi la competizione è talmente forte e le produzioni sono tanto curate che i difetti si notato all’istante.
In pratica, per ogni gioco che ha un difetto, ce ne sono dieci che non ce l’hanno, quindi lo notiamo subito.

Quindi, in conclusione, magari i giochi di oggi non consentono di fare tante cose diverse come quelli più vecchi, ma tutto quello che c’è è di qualità talmente alta da innalzarne il valore ben al di sopra dei vecchi titoli che tanto amavamo.

Non sarò l’unico che ricorda i capolavori del passato, poi li riprende e si chiede “Ma come faceva a piacermi?”, vero?

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? :D

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 :D 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ì :D

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!

Alta dinamica per le nostre retine

A chi tocca oggi? All’HDR, direi.

L’HDR è stato per lungo tempo una specie di mito: la diatriba tra “HDR vero” ed “HDR falso” mi ha divertito a lungo. Ovviamente il tutto era portato avanti dalle riviste, che purtroppo parlano a vanvera di quello che non sanno. Non si distaccano minimamente dalla stampa generalista che tanto scherniscono, ma vabbè.

Vediamo un po’ cos’è questo HDR. In primo luogo “HDR” è l’acronimo di

High Dynamic Range

Eh? Ma che vuol dire? Non sembra nemmeno una tecnica, ma una definizione. E’ proprio questo infatti. Dire che un gioco “Ha l’HDR” non ha molto senso, così come dire “l’HDR è un effetto”. Vediamo perchè.

Noi sappiamo che il nostro amato e stimato monitor ci può far vedere milioni di colori. Quanti, esattamente?

Prima di rispondere, credo sia sensato spiegarvi come funziona il nostro bulbo oculare (a me ha sempre fatto ridere la parola “bulbo”, a voi no? :D ).

Il nostro amatissimo ha sul fondo una roba chiamata “retina”. Sulla retina abbiamo due tipi di recettori: i bastoncelli ed i coni. I primi sono sensibili alla Luminanza, ossia ci dicono quanto forte è la luce.

I secondi sono invece sensibili al colore. I coni sono divisi in tre gruppi, quelli sensibili alle radiazioni basse, medie ed alte. Per essere più chiari, sono sensibili al rosso, al verde ed al blu. Questi recettori sono tutti sul fondo della retina, tranne dove c’è il nervo ottico (dove c’è una cosa non può essercene un’altra): è lì che c’è la famosa “zona cieca”.

Da qui capiamo finalmente perchè tutto il mondo della grafica gira attorno alla triade (o tristimolo) RGB: perchè il nostro occhio è fatto così.

Dicevamo prima: quanti colori possiamo rappresentare con il sistema RGB dei computer? Un sistema moderno ha 8 bit per ogni componente, quindi rappresenta numeri da 0 a 255. Lo 0 significa “niente colore”, mentre il 255 significa “colore a piena potenza”.

Se mettiamo i tre colori in fila, otteniamo un numero da 24 bit. A questo punto è facile: 2 elevato alla 24 è il numero di colori.

Quindi: 16.777.216

Tanti, vero? L’occhio ne vede circa 16 milioni, pensate un po’: quindi meno di quelli rappresentabili. Il che non significa che il monitor visualizza più colori di quelli che vediamo, ma ne parleremo un’altra volta.

Ok, torniamo all’HDR: dicevamo che se diamo 8 bit, abbiamo un range da 0 a 255 per componente. Questo range, che notiamo essere in numeri interi, viene convertito per fare i nostri conti in un range che varia da 0 ad 1, dove si lavora con numeri con la virgola (tipo 0.5, 0.8 e così via).

E se noi dessimo un range molto più ampio per componente? Tipo 10 o 32 bit ognuno? Potremmo avere un numero di tipo “float” (quindi con la virgola) che potrebbe andare su tutto il range dei float, quindi, chessò potrei scriverci dentro 100, oppure 1.000,6 oppure anche 1.000.000 .

Ecco, questo è l’HDR: avere i componenti del colore con range ampio, non tra 0 ed 1, ma che possa assumere valori molto più alti (o bassi).

Capiamo subito che non è un effetto in sè: è una specie di proprietà.

Quindi HDR non significa “il bagliore quando guardo il sole” o “quando esco dalla canverna sono accecato” o “al buio vedo fondamentalmente il rosso”: tutti questi sono effetti possibile grazie al fatto che stiamo facendo il rendering su superfici HDR (ossia con componenti RGB belle grosse), ma l’HDR è la proprietà della superficie, non l’effetto.

Detto questo, cerchiamo di capire perchè si è ritenuto necessario fare i conti in alta dinamica.

Nella realtà abbiamo luci estremamente forti: il sole, ad esempio, è una luce dannatamente intensa. Senza andare a prendere il sommo imperatore, però, possiamo limitarci ai suoi riflessi: quelli sul mare, ad esempio.

Queste luci sono intensissime ed è impossibile rappresentarle adeguatamente con un range che va solo da 0 ad 1: dovremmo perdere tantissima precisione. Per “tantissima” non intendo “da 0 a 2″ ma circa 10 elevato alla 4 :D (se parliamo di candele, l’unità di misura dell’intensità della luce)

Quindi significa che con l’HDR il nostro monitor emetterà una luce intensa come quella vera? Certo che no, purtroppo (o per fortuna): alla fine di tutto, la nostra immagine dovrà finire su una superficie normale, in modo che l’hardware della scheda grafica possa prendere tale superficie e metterla sullo schermo.

Ma quindi? A che serve l’HDR? A niente? Detto così parrebbe di sì: se non si fa niente, l’immagine in alta dinamica viene semplicemente portata su quella a bassa per essere messa su schermo. Certo, tutti i valori maggiori di 1 verrebbero portati ad 1, creando una immagine inquietantemente sovraesposta.

E quindi? Ecco qui perchè “l’HDR” non è un effetto: di per sè non fa molto. Avendo però la superficie ad alta dinamica, possiamo fare tutta una serie di conti che non potremmo fare con una immagine a bassa dinamica. In nostro scopo è imitare il funzionamento della retina (o di una macchina fotografica, secondo quello che vogliamo fare), quindi per farlo abbiamo bisogno di una scena che abbia le luminanze belle alte, come nella realtà. Alla fine dei nostri conti, avremo una immagine in bassa dinamica, che potremo copiare nella memoria che andrà sullo schermo.

Ma che conti si fanno? Si possono fare dei conti per riprodurre l’abbagliamento dovuto al passare da una zona buia ad una zona chiara. Un’altro conto che possiamo fare serve a dare una colorazione differente alla scena, giacchè il nostro occhio si comporta diversamente a seconda della “quantità” di luce presente.

Il nostro occhio funziona diversamente a seconda della luce presente: ci sono fondamentalmente tre “funzionamenti” principali:

- Visione Fotopica: E’ la condizione standard, diciamo una giornata luminosa e allora, con le tipe che prendono il sole al parco ostentando le loro carni.

- Visione Scotopica: In questo caso, invece, la luce è molto bassa. Diciamo come quando tutta la stanza è buia e non filtra quasi niente dalle tapparelle ed è notte. Dopo qualche minuto (circa 10) il nostro occhio si abitua e ci farà vedere qualcosa. In questo caso i colori che vedremo maggiormente saranno quelli caldi (tipo il rosso).

- Visione Mesopica: Una media tra i due casi precedenti. Diciamo che a questo livello riusciamo ancora a distinguere i colori.

Questi tipi di “visione” possono essere simulati solamente tramite conti su superfici in HDR.

So già che mi verrà posta questa domanda: perchè alcune schede non consentono l’Antialiasing e l’HDR assieme? La risposta è semplice: basta pensare a come funziona l’antialiasing.

In genere funziona così: si disegna la scena su una superficie molto più grande, per poi riscalarla su quella che andrà visualizzata (quindi 1024×768 o quello che preferite). Il rescale, giacchè viene fatto tramite media tra i vari pixel, nasconderà le brusche transizioni di colore, che generano l’aliasing. Siccome abbiamo visto che in HDR si fa il rendering da un’altra parte e non sulla superficie di rendering, capiamo subito che la cosa o la si gestisce apposta oppure niente antialiasing.

Per lungo tempo ho riflettuto se fosse necessario mettere delle formule: ho deciso che qui non ne metterò, ma il prossimo articolo ne sarà profuso. Ovviamente per il vostro sollazzo :D

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 :D . 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 :D ).

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 :D

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 :)