2011-01-23 14 views
15

In un motore 3D su cui sto lavorando Sono riuscito a disegnare un cubo in 3D. L'unico metodo per riempire i lati è usare un colore a tinta unita o una sfumatura per quanto mi riguarda. Per rendere le cose più eccitanti, mi piacerebbe davvero implementare il mapping delle texture usando una semplice bitmap.Manipolazione delle immagini e mappatura delle texture con Canvas HTML5?

Il punto è che difficilmente riesco a trovare articoli o esempi di codice sul tema della manipolazione delle immagini in JavaScript. Inoltre, il supporto delle immagini nella tela HTML5 sembra essere limitato al ritaglio.

Come è possibile eseguire lo stretching di una bitmap in modo che una bitmap rettangolare possa riempire una faccia cubica non regolare? In 2D, una faccia a cubo quadrato proiettata è, a causa della prospettiva, non di una forma quadrata, quindi dovrò allungarla per adattarla a qualsiasi quadrilatero.

Speriamo che questa immagine chiarisca il mio punto. La faccia sinistra ora è riempita con un gradiente bianco/nero. Come posso riempirlo con una bitmap, dopo che è stata mappata con texture?

Cube

Qualcuno ha qualche consiglio su mappatura prospettiva consistenza (o la manipolazione delle immagini affatto) utilizzando JavaScript e HTML5 Canvas?

Modifica: Ho funzionato, grazie a 6502!

È, tuttavia, piuttosto intensivo della CPU, quindi mi piacerebbe sentire le idee di ottimizzazione.

Result using 6502's technique-Texture image used

+0

Se si sta realizzando un vero motore 3D, questo potrebbe non essere il modo migliore per eseguire il texturing. Le coordinate della trama/colori/normali, ecc. Dovrebbero essere memorizzati per vertice per consentire una maggiore flessibilità. –

+1

È un semplice progetto per hobby, non diventerà molto più di questo cubo in realtà. – pimvdb

+0

@pimvdb c'è qualche possibilità di condividere un codice? –

risposta

37

penso che si potrà mai ottenere un risultato preciso ... ho trascorso qualche tempo indagando come fare grafica 3D utilizzando contesto tela 2d e l'ho trovato praticabile per fare texture mapping ombreggiatura Gouraud calcolando appropriate gradienti 2d e matrici:

  • poligoni solidi sono naturalmente facili
  • Gouraud riempimento è possibile solo su un componente (cioè non è possibile avere un triangolo in cui ogni vertice è RGB arbitrario riempito con interpolazione bilineare, ma y ou può fare riempimento usando per esempio tre tonalità arbitrari di un unico colore)
  • mappatura trama lineare può essere fatto utilizzando clipping e immagine Illustrazione

Vorrei implementare prospettiva corretta mappatura texture utilizzando suddivisione maglia (come su PS1).

Tuttavia ho riscontrato molti problemi ... ad esempio il disegno di un'immagine con una trasformazione di matrice (necessaria per la mappatura della trama) è piuttosto impreciso su chrome e IMO è impossibile ottenere un risultato accurato ai pixel; in generale non c'è modo di disattivare l'antialias quando si disegna su una tela e questo significa che si otterranno linee visibili visibili quando si suddividono in triangoli. Ho anche trovato che il rendering multipass funzionava davvero male su Chrome (probabilmente a causa del modo in cui il rendering hw-accellerated è implementato).

In generale questo tipo di rendering è sicuramente uno stress per i browser Web e apparentemente questi casi d'uso (strane matrici per esempio) non sono testati molto bene. Sono stato persino in grado di far sì che Firefox si bloccasse in modo così brutto da far cadere l'intero X susbsystem sulla mia Ubuntu.

È possibile visualizzare i risultati dei miei sforzi here o come video here ...IMO è sicuramente entusiasta del fatto che questo può essere fatto in un browser senza utilizzare le estensioni 3D, ma non penso che i problemi attuali verranno risolti in futuro.

In ogni caso l'idea di base utilizzata per disegnare un'immagine in modo che i 4 angoli finiscano in posizione di pixel specifici consiste nel disegnare due triangoli, ognuno dei quali utilizzerà l'interpolazione bilineare.

Nel codice seguente presumo di avere un oggetto immagine texture e 4 angoli ognuno dei quali è un oggetto con i campi x,y,u,v dove x,y sono coordinate in pixel sulla tela di destinazione e u,v sono coordinate pixel su texture:

function textureMap(ctx, texture, pts) { 
    var tris = [[0, 1, 2], [2, 3, 0]]; // Split in two triangles 
    for (var t=0; t<2; t++) { 
     var pp = tris[t]; 
     var x0 = pts[pp[0]].x, x1 = pts[pp[1]].x, x2 = pts[pp[2]].x; 
     var y0 = pts[pp[0]].y, y1 = pts[pp[1]].y, y2 = pts[pp[2]].y; 
     var u0 = pts[pp[0]].u, u1 = pts[pp[1]].u, u2 = pts[pp[2]].u; 
     var v0 = pts[pp[0]].v, v1 = pts[pp[1]].v, v2 = pts[pp[2]].v; 

     // Set clipping area so that only pixels inside the triangle will 
     // be affected by the image drawing operation 
     ctx.save(); ctx.beginPath(); ctx.moveTo(x0, y0); ctx.lineTo(x1, y1); 
     ctx.lineTo(x2, y2); ctx.closePath(); ctx.clip(); 

     // Compute matrix transform 
     var delta = u0*v1 + v0*u2 + u1*v2 - v1*u2 - v0*u1 - u0*v2; 
     var delta_a = x0*v1 + v0*x2 + x1*v2 - v1*x2 - v0*x1 - x0*v2; 
     var delta_b = u0*x1 + x0*u2 + u1*x2 - x1*u2 - x0*u1 - u0*x2; 
     var delta_c = u0*v1*x2 + v0*x1*u2 + x0*u1*v2 - x0*v1*u2 
         - v0*u1*x2 - u0*x1*v2; 
     var delta_d = y0*v1 + v0*y2 + y1*v2 - v1*y2 - v0*y1 - y0*v2; 
     var delta_e = u0*y1 + y0*u2 + u1*y2 - y1*u2 - y0*u1 - u0*y2; 
     var delta_f = u0*v1*y2 + v0*y1*u2 + y0*u1*v2 - y0*v1*u2 
         - v0*u1*y2 - u0*y1*v2; 

     // Draw the transformed image 
     ctx.transform(delta_a/delta, delta_d/delta, 
         delta_b/delta, delta_e/delta, 
         delta_c/delta, delta_f/delta); 
     ctx.drawImage(texture, 0, 0); 
     ctx.restore(); 
    } 
} 

Queste brutte e strane formule per tutte quelle variabili "delta" vengono utilizzate per risolvere due sistemi lineari di tre equazioni in tre incognite usando il metodo Cramer's e lo schema Sarrus per i determinanti 3x3.

In particolare stiamo cercando per i valori di a, b, ... f in modo che le seguenti equazioni sono soddisfatti

a*u0 + b*v0 + c = x0 
a*u1 + b*v1 + c = x1 
a*u2 + b*v2 + c = x2 

d*u0 + e*v0 + f = y0 
d*u1 + e*v1 + f = y1 
d*u2 + e*v2 + f = y2 

delta è il determinante della matrice

u0 v0 1 
u1 v1 1 
u2 v2 1 

e ad esempio delta_a è il determinante della stessa matrice quando si sostituisce la prima colonna con x0, x1, x2. Con questi puoi calcolare a = delta_a/delta.

+0

Wow, la tua applicazione torus è impressionante ... Non mi importa se non è esatta su base pixel. Grazie per la magnifica risposta. – pimvdb

+2

Ok ... Aggiungerò la parte di mappatura delle texture alla risposta (la fonte di torus.html è difficile da leggere perché "dovevo" ridurla a 4K). – 6502

+0

Basta guardare la fonte ed è stato davvero imperscrutabile, fatto molto bene! – pimvdb