A Quake3 bsp file-jával kapcsolatosan gondoltam írni néhány gondolatot ezekről, mivel előszeretettel használták pályaszerkesztéskor ezeket a felületeket. Eredetileg autók karosszériánának számítógépen történő tárolásához / feldolgozásához dolgozta ki Bezier 70-s évek elején. A bezier patch gyakorlatilag a b-spline 3 dimenziós kiterjesztése. Gyakorlati hasznát az adja, hogy görbült felületeket (pl. íveket és organikus elemeket) lehet vele könnyedén kezelni egy két dimenziós paramétertömb segítségével, amelyeket kontroll pontoknak hívunk. Mindössze ezek mozgatásával elérhető az egész felület módosítása.
Három alapvető elv érvényes ezekre a felületekre, hasonlóan a bezier ívekhez:
- a paramétertömb sarkait képező kontrollpontok mindig illeszkednek a felületre
- a felület folyamatos (törés nélküli)
- a felület mindig a kontroll pontok által alkotott konvex befoglaló testen belül helyezkedik el.
A legnagyobb hátránya azonban az, hogy közvetlenül(!) a mai gpukkal nem renderelhető. A legjobb megoldás ha mi magunk készítünk egy tesszelláló függvényt, amely egy ilyen felületet háromszögekkel közelít egy általunk beállított pontossági paraméter alapján. Természetesen minél nagyobb a pontosság, annál több háromszög és vertex lesz az eredmény.
A tesszelláció elve igen egyszerű: egy interpolációt vagy approximációt kell elvégezni a kontroll pontok adatai között (bizonyos terminológia az interpolációhoz a szerkesztési pontokat, míg az approximációhoz a kontroll pontokat rendeli). A pontok számától függően bi-quadratic (3x3), bi-cubic(4x4) bezier patchekről beszélhetünk (jelen példában 3x3 pontot használunk, de bármennyi kontroll pont használata lehetséges azonban ez a két eset a leggyakoribb). Ezen adatok közé tartozik többek között a pozíció is, de a műveletet elvégezhetjük textúra koordinátákra és normálokra is, amennyiben a kontroll pontok rendelkeznek velük (a normált természetesen közvetlenül a felületből is kiszámíthatjuk, de ez jóval lassabb megoldás, mint a normálok interpolálása). A tesszelláció eredménye tehát egy rács lesz legalább egy pozíció és egy vertexindex tömbbel.
A vertexindex tömb elkészítése nem igényel különösebb trükköt egyszerűen egy, rács szélességű és hosszuságú ponthalmazt kell háromszögekként indexelnünk.
A kontroll pontok approximálása ezzel szemben egy nagyobb kihívást jelentő feladat, amelyet két jól elkülöníthető algoritmusra oszhatunk fel.
Az első, három kontroll pont közötti közelítés egy paraméterrel (tovább optimalizálható a függvény):
vec4 approximate(float t, vec4 a, vec4 b, vec4 c)
A második függvény végzi magát a tesszellálást, amelyhez szükségünk van a kontroll pontokra és azok számára valamint arra, hogy milyen finom rácsot szeretnék végeredményül.
{
float t0=1.0f - 2.0f*t + t*t;
float t1=2.0f*t - 2.0f*t*t;
float t2=t*t;
return (a*t0 + b*t1 + c*t2);
}
void tesselateBPatch(
vec4* result, int grid_width, int grid_height,
vec4 *controls, int control_width, int control_height)
{
int size_x=control_width/2;
int size_y=control_height/2;
float diff_s=1.0f/grid_width;
float diff_t=1.0f/grid_height;
int stride=size_x*grid_width+1;
for(int j=0;j<size_y;j++)
{
int patch_j=(j<size_x-1)? grid_height : grid_height+1;
for(int i=0;i<size_x;i++)
{
int patch_i=(i<size_x-1)? grid_width : grid_width+1;
int index0=2*(j*grid_width+i);
int index1=index0+grid_width;
int index2=index1+grid_width;
vec4 &cp0=controls[index0++];
vec4 &cp1=controls[index1++];
vec4 &cp2=controls[index2++];
vec4 &cp3=controls[index0++];
vec4 &cp4=controls[index1++];
vec4 &cp5=controls[index2++];
vec4 &cp6=controls[index0++];
vec4 &cp7=controls[index1++];
vec4 &cp8=controls[index2++];
vec4 *dst=result+j*grid_height*stride+ i*grid_width;
float t=0.0f;
for (int k=0;k<patch_j;k++,t+=diff_t)
{
vec4 v0=approximate(t,p0,p1,p2);
vec4 v1=approximate(t,p3,p4,p5);
vec4 v2=approximate(t,p6,p7,p8);
float s=0.0f;
for(int l=0;l<patch_i;l++,s+=diff_s)
{
dst[x]=approximate(s,v0,v1,v2);
}
dst+=stride;
}
}
}
}
A result tömbben kiszámolt vertexek a vertexindex tömbbel együtt már közvetlenül felhasználhatók rendereléshez, ütközéshez stb.
Manapság egyre kevesebb helyen használnak bezier patchet és egyéb parametrikus felületeket, helyüket lassan kiszorítja a sík felületekhez használt ún. subdivision.