architetture visualizzazione 3d

Architetture Visualizzazione 3D: Analisi Comparativa tra SSR e CSR

In questo articolo analizzeremo le principali architetture visualizzazione 3d sul web, mettendo a confronto i modelli Server-Side Rendering (SSR) e Client-Side Rendering (CSR).

Sicurezza e Proprietà Intellettuale

  • Server-Side Rendering (SSR): Questo modello garantisce l’integrità del dato originale e la protezione della proprietà intellettuale (IP). Poiché l’intero dataset volumetrico (es. file NRRD o DICOM) e il codice di rendering possono risiedere esclusivamente sull’infrastruttura server, l’utente finale non ha mai accesso ai dati grezzi. Al client vengono trasmessi esclusivamente flussi di pixel codificati, rendendo impossibile l’estrazione della geometria o dei metadati sensibili dal frontend.
  • Client-Side Rendering (CSR): In questo scenario, il server funge da fornitore di dati statici o API. Il dataset completo deve essere trasferito via rete al browser dell’utente. Sebbene esistano tecniche di offuscamento, i dati binari sono intrinsecamente esposti nel traffico di rete e nella memoria del client, aumentando il rischio di esfiltrazione o reverse engineering dell’algoritmo.

2. Astrazione Hardware e Accessibilità

  • Server-Side Rendering (SSR): L’onere della computazione grafica è delegato al backend. Il client necessita solo di una capacità di decodifica video standard e di una connessione WebSocket stabile. Questo permette la visualizzazione di dataset massivi anche su dispositivi con GPU limitata o assente, come tablet o workstation legacy, uniformando l’esperienza d’uso indipendentemente dall’hardware locale.
  • Client-Side Rendering (CSR): La fluidità della visualizzazione è direttamente proporzionale alla potenza di calcolo della GPU locale. L’utente deve disporre di hardware compatibile con le API WebGL o WebGPU. Dataset di grandi dimensioni possono causare saturazione della RAM di sistema o della VRAM video sul client, portando a instabilità del browser o a degradazione delle prestazioni (bassi FPS). Inoltre non si ha il pieno controllo dell’hardware su cui si girerà il rendering, costringendo a supportare diverse tecnologie e periferiche (gpu).

3. Paradigma Architetturale e Gestione dello Stato

  • Server-Side Rendering (SSR): È un’architettura stateful. Il server mantiene l’istanza della pipeline VTK e lo stato della camera per ogni sessione attiva. Ogni comando di interazione è una Remote Procedure Call (RPC) che aggiorna lo stato centralizzato. Questo garantisce una “singola fonte di verità” ma richiede una gestione complessa delle sessioni persistenti.
  • Client-Side Rendering (CSR): È un’architettura tendenzialmente stateless dal punto di vista del server. Una volta scaricati i dati, il client gestisce autonomamente lo stato della scena, la telecamera e le trasformazioni geometriche. Questo riduce la complessità del backend, ma può generare inconsistenze se più client devono collaborare sulla stessa vista in tempo reale senza un meccanismo di sincronizzazione esterno.

4. Scalabilità e Costi Infrastrutturali

  • Server-Side Rendering (SSR): La scalabilità è limitata dalla densità di istanze GPU e CPU gestibili sul server. Ogni utente attivo consuma risorse hardware dedicate per il rendering OpenGL all’interno di display virtuali. I costi operativi crescono linearmente con il numero di utenti simultanei, richiedendo infrastrutture cloud dotate di accelerazione grafica.
  • Client-Side Rendering (CSR): Offre una scalabilità quasi illimitata a costi infrastrutturali ridotti. Poiché il lavoro di rendering è distribuito sui dispositivi degli utenti, il server deve solo gestire il trasferimento dei file. Il costo per l’erogatore del servizio rimane basso anche all’aumentare esponenziale degli utenti, spostando l’investimento hardware sull’utente finale.

5. Implementazione tecnica delle architetture visualizzazione 3d

Implementazione SSR 

Il sistema che abbiamo implementato insternamente utilizza un approccio di virtualizzazione grafica tramite Xvfb per creare un display virtuale (es. DISPLAY=:1) su cui VTK esegue il rendering in modalità headless.

  • Backend: La logica Python inizializza il renderer e i protocolli wslink. Il protocollo vtkWebPublishImageDelivery è responsabile della cattura dei frame e della loro trasmissione binaria tramite WebSocket.
  • Interazione: Gli eventi del mouse catturati nel browser tramite vtkRemoteView vengono inviati come messaggi RPC al server, che aggiorna la telecamera VTK e genera un nuovo frame. La latenza di rete influisce direttamente sul tempo di risposta tra l’input e l’aggiornamento visivo.

Implementazione CSR 

In un’architettura puramente client-side, l’implementazione seguirebbe un flusso differente:

  • Caricamento Dati: Il browser effettua richieste GET per ottenere i dati volumetrici (es. NRRD o pacchetti DICOM). Per ottimizzare il trasferimento, i dati vengono spesso compressi o suddivisi in sotto-volumi (streaming volumetrico).    
  • Pipeline Client: Viene utilizzata una libreria come vtk.js per istanziare direttamente nel browser la pipeline di rendering. I dati vengono caricati nella memoria del browser e decodificati (spesso tramite WebAssembly per massimizzare le prestazioni).
  • Rendering Locale: La libreria crea un contesto WebGL o WebGPU locale. Il calcolo dei pixel, l’applicazione delle funzioni di trasferimento (colore e opacità) e il ray-casting avvengono in tempo reale sulla GPU dell’utente.

Interazione: Poiché la geometria e il renderer risiedono localmente, l’interazione è immediata e non dipende dalla latenza di rete, garantendo una risposta istantanea ai comandi di rotazione o zoom.

Per approfondire l’implementazione, di seguito è riportata una guida tecnica passo-passo di un sistema di Server-Side Rendering (SSR) basato su vtk / vtk.js.

Fase 1: Configurazione dell’Ambiente Headless e Containerizzazione

Per eseguire il rendering grafico sul server senza disporre di un monitor fisico, l’ambiente di runtime deve essere configurato in modalità headless, emulando un server grafico in memoria.

1. Installazione delle dipendenze di sistema: Il container deve includere le utilità X11, i driver OpenGL virtuali (mesa-utils, xvfb), il framework VTK per Python e l’ambiente Node.js per la gestione delle dipendenze del frontend.

				
					# Estratto dal setup dell'ambiente di sistema
RUN apt update && apt install -y mesa-utils xvfb && pip install vtk==9.1.0 && \
    curl -fsSL https://deb.nodesource.com/setup_16.x | bash - && \
    apt-get install -y nodejs

				
			

2. Inizializzazione del Framebuffer Virtuale: Prima di lanciare qualsiasi processo grafico, è necessario allocare un display fittizio in background tramite Xvfb.

				
					# Start virtual fb server sul display :1
Xvfb :1 &

				
			

Fase 2: Implementazione del Server di Rendering (Backend Python)

Il backend gestisce la pipeline di computazione 3D e si occupa di esporre l’endpoint WebSocket per lo streaming dei frame e la ricezione dei comandi remoti.

1. Estensione del Protocollo di Comunicazione: Viene definita una classe che eredita da vtk_wslink.ServerProtocol per centralizzare la logica di sessione e configurare l’interfaccia di rete.

				
					class _WebCone(vtk_wslink.ServerProtocol):
    view = None
    authKey = "wslink-secret"

    def initialize(self):
        global renderer, renderWindow, renderWindowInteractor, cone, mapper, actor
				
			

2. Registrazione dei moduli Web VTK e Ottimizzazione: All’interno dell’inizializzazione della sessione, si registrano i protocolli nativi per l’interazione del mouse, la viewport e la consegna delle immagini, disattivando l’encoding nativo lato C++ per migliorare il throughput.

				
					# Registrazione dei protocolli di interazione e streaming
        self.registerVtkWebProtocol(protocols.vtkWebMouseHandler())
        self.registerVtkWebProtocol(protocols.vtkWebViewPort())
        self.registerVtkWebProtocol(protocols.vtkWebPublishImageDelivery(decode=False))
        self.registerVtkWebProtocol(protocols.vtkWebViewPortGeometryDelivery())

        # Aggiornamento chiave di autenticazione e ottimizzazione encoding
        self.updateSecret(_WebCone.authKey)
        self.getApplication().SetImageEncoding(0)
				
			

3. Costruzione della Pipeline e Mappatura della Vista: Se la vista non è presente, si istanziano gli oggetti core di VTK (vtkRenderer, vtkRenderWindow), si configura la geometria (in questo caso un cono) e si associa la finestra di rendering alla mappa globale degli oggetti attivi sotto la chiave "VIEW".

				
					if not _WebCone.view:
            renderer = vtk.vtkRenderer()
            renderWindow = vtk.vtkRenderWindow()
            renderWindow.AddRenderer(renderer)

            renderWindowInteractor = vtk.vtkRenderWindowInteractor()
            renderWindowInteractor.SetRenderWindow(renderWindow)
            renderWindowInteractor.GetInteractorStyle().SetCurrentStyleToTrackballCamera()

            cone = vtk.vtkConeSource()
            mapper = vtk.vtkPolyDataMapper()
            actor = vtk.vtkActor()

            mapper.SetInputConnection(cone.GetOutputPort())
            actor.SetMapper(mapper)
            renderer.AddActor(actor)

            renderer.ResetCamera()
            renderWindow.Render()

            # Registrazione della finestra di rendering per il modulo web
            _WebCone.view = renderWindow
            self.getApplication().GetObjectIdMap().SetActiveObject("VIEW", renderWindow)
				
			

4. Inizializzazione del Server Web: Nel punto di ingresso dell’applicazione, gli argomenti della riga di comando vengono parsati per configurare le porte e avviare il server di rete persistente.

				
					if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="VTK/Web Cone web-application")
    server.add_arguments(parser)
    args = parser.parse_args()

    _WebCone.authKey = args.authKey
    server.start_webserver(options=args, protocol=_WebCone)
				
			

Fase 3: Implementazione del Client di Visualizzazione (Frontend JavaScript)

Il client web si connette al WebSocket server, alloca una viewport nel DOM e instrada gli input utente verso il backend.

1. Inizializzazione dei moduli Client: Si estraggono i costruttori di vtk.js e wslink necessari per la gestione dello streaming e della vista remota.

				
					const vtkWSLinkClient = vtk.IO.Core.vtkWSLinkClient;
const vtkRemoteView = vtk.Rendering.Misc.vtkRemoteView;
const connectImageStream = vtk.Rendering.Misc.vtkRemoteView.connectImageStream;
const SmartConnect = wslink.SmartConnect;

vtkWSLinkClient.setSmartConnectClass(SmartConnect);
				
			

2. Configurazione di vtkRemoteView: Viene istanziata la viewport remota definendo l’evento RPC associato alla rotella del mouse e i parametri di compressione JPEG interattiva.

				
					const view = vtkRemoteView.newInstance({
  rpcWheelEvent: "viewport.mouse.zoom.wheel"
});
view.setContainer(divRenderer);
view.setInteractiveRatio(0.7); // Scala di risoluzione durante il movimento
view.setInteractiveQuality(50); // Qualità JPEG durante l'interazione
				
			

3. Instaurazione del Tunnel WebSocket: Viene definito l’oggetto di configurazione per la connessione. Al completamento del workflow di handshake, lo stream di immagini viene agganciato alla sessione attiva.

				
					const clientToConnect = vtkWSLinkClient.newInstance();
const config = {
  application: "cone",
  sessionURL: "ws://" + window.location.hostname + ":8765/ws"
};

clientToConnect
  .connect(config)
  .then(validClient => {
    // Abilita la ricezione del flusso binario delle immagini
    connectImageStream(validClient.getConnection().getSession());

    const session = validClient.getConnection().getSession();
    view.setSession(session);
    view.setViewId(-1); // Richiede la vista attiva registrata come di default
    view.render();
  })
  .catch(error => {
    console.error(error);
  });
				
			

Fase 4: Orchestrazione ed Esecuzione dei Processi

L’ultima fase prevede il coordinamento e l’esecuzione dei processi software sul server.

1. Esecuzione vincolata al Display Virtuale: Il server di rendering Python deve essere avviato anteponendo la variabile d’ambiente DISPLAY=:1, in modo da indirizzare i contesti grafici OpenGL verso il framebuffer di Xvfb precedentemente creato.

				
					# Avvio del server SSR in background vincolato al display virtuale
DISPLAY=:1 python3 /app/vtk_server.py --port 4321 --host 0.0.0.0 &
SERV_PID=$!
sleep 3
				
			

2. Distribuzione del Frontend: Parallelamente al backend, le risorse statiche del client (HTML, pacchetti JavaScript) vengono distribuite tramite un server HTTP standard per consentire l’accesso ai browser remoti.

				
					# Avvio del server HTTP per la distribuzione del client web
cd /app/frontend
python3 -m http.server

				
			

Conclusione

In sintesi, la selezione dell’architettura ottimale per la visualizzazione 3D avanzata non si riduce a una preferenza tecnologica, ma a un compromesso strategico tra requisiti vincolanti di sicurezza, infrastruttura e prestazioni. I due paradigmi rispondono a necessità operative diametralmente opposte.

Quando implementare il Server-Side Rendering (SSR)

L’approccio basato su VTK e wslink si dimostra la scelta ottimale nei seguenti scenari:

Vincoli di Sicurezza Rigidi (Zero-Data-Leak): Ambienti medicali o industriali regolamentati (es. conformità HIPAA o protezione di brevetti industriali CAD) in cui il trasferimento dei file binari originali al di fuori del perimetro del data center sia proibito o altamente rischioso. Con l’SSR, l’esposizione del dato è limitata ai soli pixel del flusso video.

Dataset Massivi ed Elevata Densità Volumetrica: Visualizzazione di dati che superano le capacità di allocazione della memoria RAM e VRAM tipiche dei dispositivi consumer. La pipeline di elaborazione su server consente di computare geometrie complesse e rendering volumetrici pesanti direttamente sulla memoria dell’infrastruttura di backend.

Eterogeneità dei Dispositivi Client (Hardware Abstraction): Necessità di distribuire l’applicazione a un’ampia platea di utenti dotati di thin client, dispositivi mobili o workstation legacy sprovviste di accelerazione hardware locale compatibile con WebGL/WebGPU.

Quando implementare il Client-Side Rendering (CSR)

L’architettura decentralizzata basata su librerie come vtk.js sul frontend è preferibile nei seguenti contesti:

Scalabilità di Massa a Basso Costo: Applicazioni destinate a migliaia di utenti simultanei in cui il costo di mantenimento di una server farm dotata di GPU dedicate (necessaria per il rendering headless e istanze Xvfb multiple) risulterebbe economicamente insostenibile. Il CSR distribuisce l’onere computazionale direttamente sull’hardware degli utenti finali.

Interazione Real-Time a Bassa Latenza: Sistemi in cui l’immediatezza della risposta all’input dell’utente (rotazione, manipolazione di filtri, navigazione multi-frame) sia un requisito critico per l’esperienza d’uso. Eseguendo il calcolo dei pixel localmente sulla GPU del client, si elimina il collo di bottiglia della latenza di rete associato alle chiamate RPC bidirezionali.

Disponibilità di Connessione Intermittente: Scenari in cui il client deve poter continuare a manipolare la scena 3D anche in condizioni di connettività di rete instabile o assente, situazione non supportata dal modello strettamente _stateful_ dell’SSR.

In conclusione, l’architettura ideale potrebbe configurarsi anche come un modello ibrido: l’utilizzo del CSR per l’esplorazione rapida e la diagnostica di routine su dataset standard, affiancato da sessioni SSR dedicate per l’analisi avanzata di grandi volumi di dati o per elaborazioni protette da elevati standard di riservatezza.

Ultimi articoli

Dal PACS alla segmentazione dentale AI: integrazione di TotalSegmentator + ToothFairy in DICOM Vision®

Guida tecnica alla generazione dei DICOM UID

Programma per visualizzare immagini DICOM

DICOM Standard: cos’è, come funziona e perché è fondamentale nell’imaging medico

area contatti

Per informazioni, progetti, idee, scrivici