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
.
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à. –
È un semplice progetto per hobby, non diventerà molto più di questo cubo in realtà. – pimvdb
@pimvdb c'è qualche possibilità di condividere un codice? –