Study of a solution to make megalopolis with Maya and some custom tool

Ricerca condotta nel corso del 2019 volto alla realizzazione di modelli tridimensionali procedurali di città metropolitane (milioni di abitanti), utilizzando Autodesk Maya, GLSL e alcuni script in Python.

Lo studio si è poi concluso con la realizzazione di un software proprietario sostitutivo (QBatica), per sopperire alle limitazini di performance e di predisposizione di Maya. Regole e step per generare una città del Sistema utilizzando i seguenti software:

  • Gimp
  • Autodesk Maya
  • Roadmap Generator (script Python)
  • JTYMainShader (shader GLSL)
  • JTYBlocksShader (shader GLSL)

Introduction

Per l'utilizzo ho realizzato lo shader JTYBlocksShader che sfrutta il Geometry Shader di GLSL per popolare gli isolati con vari edifici. In totale si rendono necessarie le seguenti texture (ogni 10 milioni di abitanti): Utilizzate dal Roadmap Generator

  •  Urban Map (10K): NON viene utilizzata dallo shader, ma solo dal Roadmap Generator.

Utilizzate dal GLSL Shader

  • Color Map (10K nell'Urban Layer e nel Tree Layer): texture principale generata dal Geometry Cube Roadmap Generator la quale indica al JTYBlocksShader come generare i blocchi in orizzontale. Viene utilizzata anche dal JTYTreeShader per delimitare le aree boschive. Per cui vi sarà un doppio caricamento in memoria di questa mappa.
    • Canale R e G: informazioni di rotazione orizzontale del cubo rispetto all'asse Nord-Sud.
    • Canale B: presenza di territorio edificato o meno.
  • Ripidity Map (7K nell'Urban Layer, 2K nel Tree Layer): indica ai cubes quanto devono essere scalati in verticale.
    • Canale R: il normale dislivello.
    • Canale G: il dislivello dei grattacieli o dislivello secondario.
    • Canale B: la Terrain Map.

Memory

In totale ci sarebbe l'utilizzo di 4 texture (+1 eventuale di territorio) per quadrante (10 milioni di abitanti) per un totale di circa 31K (ove il limite della GPU si aggira intorno ai 65/70 K). Per città sopra i 10 milioni di abitanti sarebbero necessari quindi circa 62K, che rientrano appena nel limite.

Features to be made

Fare test con spioventi e fattore di diffusione. Monumenti e cupole. Vedere se c'è un modo, anche in post, per computare le ombre.

Puntualizzare i city parameter da memorizzare (non solo i city parameters ma anche i colori).

Virtues of this shader

Implementato un algoritmo nel Geometry Shader che invece di eliminare il cube che incrocia una strada (solo se a incrociare sono gli estremi e non i medi) sposta gli estremi indietro fin quando non incrociano più la strada. Questo consente di avere degli isolati più pieni e a ridosso della strada. Nel Geometry Shader implementato algoritmo di clamping incrociato tra altezza dell'edificio e distanza (edifici distanti ma alti rimangono visibili).

Preparing

Creare Urban Map generale su una texture a 10K. Creare Terrain Map generale su una texture a 10K. Utilizzare il canale blu con 100 per livello normale e più scuro per fiumi, conche, più chiaro per i rilievi. Eseguire eventuali suddivisioni di quadranti (dovrebbe riguardare le città che superano, all'incirca, i 10 milioni di abitanti). Per esempio una città da 2 quadranti avrà 4 blocchi da 5K invece che uno da 10.

Color Map

Generare la Color Map con il Geometry Cube Roadmap Generator che registra per ogni blocco l'inclinazione che dovrà essere data ad ogni edificio. Parametri consigliati sono:

Size: 10.000 (per forza con la texture a 10K)
Groups: 200
Roads per group: 90000
Roads length: 13-21 (17)
Thick: 2
Rounds rarity per group: 600

Ripidity Map

Generare la Ripidity Map su Gimp secondo il workflow tradizionale utilizzando la griglia di pixel per le differenti altezze. La texture andrà privata dei canali G e B in quanto dovranno essere utilizzati per altre informazioni. Una volta terminata la Ripidity Map può essere scalata a 7K.

Main roads

Il Geometry Cube Workflow prevede l'inclinazione degli edifici basata sulle strade generate dal GC Roadmap Generator è qundi caldamente consigliato di disegnare i viali princiapli seguendo, per quanto possibile, i percorsi delle strade normali. Le strade principali dovranno essere disegnate con il colore R:0, G:255, B:0 ed esportate insieme alla Color Map che verrà utilizzata dal JTYBuildBlocks shader.

Skyscrapers

Sfrutta il canale G della Ripidity Map. Selezionare i blocchi su cui si vogliono generare i grattacieli, creare un layer separato in cui i valori delle aree selezionate sono rispecchiano le altezze. Utilizzare solo la grayscale del canale G. Questa riparametrizzazione della scala di grigi va scritto nel canale G della Ripidity Map, il quale andrà ad aggiungere un multiplier alla scalatura verticale, in modo da separare i grattacieli. Per metterlo nel canale G bisognerà mettere su Gimp il layer in Add sulla Ripidity Map che avrà occupato solo il canale R.

Terrain Map

Disegnare le aree in rilievo nel canale B della Ripidity Map. Andranno aggiunte in Add. Nel caso si necessiti di fare un cratere o una conca è bene mettere tutto il resto ad un livello più alto ed il cratere al livello 0 (blu = 0). La Ripidity Map va poi ulteriormente scalata ad una risoluzione di 2K ed applicata come Vertex Ripidity Map allo shader degli alberi, per far si che gli alberi seguano i rilievi montuosi o collinari.

Computation in Maya

Il plane deve essere posizionato in modo da avere tutti i poligoni positivi su X e Y. E' necessario indicare la dimensione del plane nell'attributo plane size per il corretto funzionamento. Parametri dello shader da registrare per ogni città I seguenti parametri devono essere registrati per ogni città al fine di garantirne la riproducitibilità esatta: Le suddivisioni del plane sul quale si sta proiettando la città (es. 350)

  • Horizontal Subdivider (es 6.7)
  • Plane Size (es.34)
  • Vertical Multiplier (es 1.0)
  • Tessel Level (es. 7)
  • Skyline Multiplier (es. 0.4)
  • Floor Height (es. 2000)
  • Windows Width (es. 1000)
  • Pure Kripbelium Threshold (es. 0.4)
  • Force Piramid Level (es. 0.5)
  • Yard Coords (es. 60) 

Poi i colori:

  • Road Color
  • Yard Color
  • Yard Map
  • Building Color
  • Roof Color.

E' quindi bene scriversi questi parametri per ogni città in modo da poterli reinserire. La salvaguardia dei dettagli cittadini dipende da questi parametri (oltre che dalle mappe). Il Distance Clamper può essere cambiato a discrezione in base alle performance della GPU nel renderizzare tutti i blocchi. Prendere gli altri parametri dello shader dallo script già esistente dei vecchi workflow.

Tree Layer

  • Aggiungere plane 34x34 con suddivisioni 300x300 sovrapposto a quello urbano ma spostato in Y di -0.002.
  • Applicarvi il JTYTreeShader.
  • Fornirgli come Color Map la stessa Color Map utilizzata per il layer urbano.
  • Memorizzare i parametri seguenti:
    • Yard Color
    • Displacement Sampling (es. 37)
    • Displacement Multiplier (es. 0.005)

Alternative solutions to Geometry Blocks

Texture LOD

Potrebbe non funzionare con ripidity in quanto il textureLod potrebbe funzionare solo in vertex e fragment.

Se verifico questa condizione è un problema perchè il sampling della displacement è necessario nel Tassellation Evaluate. Fare dei test per verificare. Se funziona e non va out of memory si potrebbe procedere ad un sistema in cui le strade principali sono descritte da una unica texture a 10K fatta normalmente ma le varie zone sarebbero comunque divise in quadranti 34x34. La differenza è che le singole texture dei quadranti sarebbero con coordinate normali, mentre quella main avrebbe coordinate ingrandite tante volte quanti sono i quadranti. Ovviamente bisognerebbe aggiungere un parametro di offset su x e y per la main texture in modo da poterla estendere su più quadranti.

In fact for most OpenGL implementation when you load texture data it doesn't even go directly to the GPU at first. Only when it's actually required for rendering it gets loaded into the GPU RAM. If there are more textures in use than fit into GPU RAM, textures are swapped in and out from GPU RAM as needed to complete the rendering.

OpenGL GPU memory exceeded possible scenarios - Stack Overflow

Osservo che le mie texture pesano 6MB, ma Maya le sta valutando come da 300MB e ovviamente così facendo la memoria si riempie velocemente. Il problema sembra essere Maya, non Open GL. Più ricerche faccio più mi sembra evidente che il problema sia che Maya cachea le texture nella memoria GPU della viewport, vanificando completamente il textureLod. Bisogna capire se questo atteggiamento di Maya innanzitutto è evitabile, se non lo è bisognerebbe capire il senso logico di tale pratica che, nel caso non fosse considerato necessario, ci costringerebbe a virare definitivamente nel software proprietario. La prova del nove che Maya esegua questa pratica aberrante sta nel fatto che il crash avviene anche quando le texture non vengono effettivamente chiamate dallo shader. Il problema NON E' un problema di GLSL e texture lod. Il problema è tutto di Maya. Fare una prova ad usare la default texture e creare circa 9 shader divers (solo il MainShader deve essere copiato) che puntano a 9 texture diverse. Se a quel punto non si rompe, procedere aggiungendoci le ripidity. Cominciamo a fare questo test e, se funziona, vediamo poi come mettere insieme i pezzi.

Terrain and Set Workflow

Per il Terrain è sufficiente l'utilizzo del JTYStandardShader estendendovi una logica di ripidity già usata su Houdini per i terreni di "Journey Through the Year", generando così il JTYTerrainShader. Per gli alberi è sufficiente estendere la logica del JTYBlockShader applicando la tassellizzazione ai rami e la generazione delle foglie (4 vertices) sui punti indicati dalla mappa. Dovrebbe essere molto più lite del JTYBlockShader. Si chiamerebbe JTYFoliageShader. Per quanto riguarda l'istancing sembra che il passaggio ad un software proprietario sia necessario. Non vi sono attualmente problematiche evidenti per quanto riguarda il set, mentre in un ottica di utilizzo per il city workflow (al fine di avere strutture non prettamente cubiche e limitate ai 24 vertici del Geometry Shader) va studiato un sistema per supportare e riapplicare l'algoritmo del Block Shader per non popolare le strade.

Instancing Set

E' fondamentale capire se Maya è in grado di avviare un instancing drawing, altrimenti il comando glInstanceID non funzionerà.

Probabilmente l'attributo che va impostato come true è HWS InstancedDraw. Dalla descrizione della documentazione che segue:

Specifies whether geometry instances are being drawn.

Semantics and annotations supported by the dx11Shader and glslShader plug-ins in Viewport 2.0 - Knowledge Autodesk

Il problema è: come si definisce il count?

Reading positions

Le informazioni sono registrate in una mappa con il metodo RGB (R: x, G: y, B: z). La quantità di pixel è la dimensione dell'array GLSL. Si sfrutta il gl InstanceID (offsets[gl InstanceID]) per fare un parse intelligente della texture alla relativa invocazione. L'offset dovrà essere calcolato in base alla risoluzione della texture e quindi allo spacing in coordinate UV occupato dal pixel. Il tutto ovviamente moltiplicato per il gl InstanceID. Tutti i modelli utilizzati dovranno essere storati in library.

Horizontal assets

Riguardo la distribuzione di persone/auto non ci sarà bisogno di avere delle mappe specifiche in quanto si sfrutteranno gli algoritmi di noise usati nel JTYBlockShader. Quindi non ci sarà bisogno di mappe. Questo algoritmo popolerà correttamente gli asset cosidetti orizzontali. Essi sono:

  • persone
  • auto
  • illuminazione stradale

Vertical assets

Anche per gli asset verticali si sfrutta l'algoritmo rondomico usato nel JTYBlockShader per fare i piani e le finestre.

Trees

La displacement verrà eseguita con le ciocche modellate. Ma come capire dove ci sono le chiome dove le ciocche dovranno essere posizionate?

Trees and forests

Si può sfruttare la stessa idea che sta dietro al BlocksShader per creare uno shader per alberi il quale suddivide il ramo con il tessellation e genera la foglia (sarebbero circa 4 vertici rispetto ai 20 e rotti del BlockShader). Nascerebbe il JTYFoliageShader.

Terrain

Per il Terrain è sufficiente l'utilizzo del JTYStandardShader estendendovi una logica di ripidity già usata su Houdini per i terreni di "Journey Through the Year", generando così il JTYTerrainShader. Ripidity automatica basata su normal. Per le foreste usare direttamente o il JTYFoliageShader o lo Standard Shader con alto livello di tassellizzazione.