In [5]:
from IPython.core.display import HTML
from string import Template
def jsConfig():
src = """
<script>require.config({ baseUrl: 'https://rawgit.com/kompgraf/course-material/master/assets/' });</script>
"""
return HTML(src)
def addScript(script, identifier):
src = Template("""
<div id="${identifier}-container"></div>
<script>require(['${script}'], main => main($$("#${identifier}-container"), '${identifier}'));</script>
""")
return HTML(src.substitute(script = script, identifier = identifier))
jsConfig()
Out[5]:
A feladat egy Doo-Sabin subdivision sémát alkalmazó program elkészítése, mely lehetőséget biztosít többszöri finomítás végrehajtására.
A házi feladat megvédése csak akkor lehet sikeres, ha a program a jellemzőit tekintve hiánytalan. Az elvárt jellemzőket teljesítő program
A megjelenítéshez a Bevezetés a számítógépi grafikába tárgy keretein belül megismert technikákat kell alkalmazni. Szükséges
használata. Fontos, hogy ellentétben a Bézier-felülettel, ebben a házi feladatban gondoskodni kell a hátsó lapok eldobásáról!
A kamera, a láthatóság és a transzformációs lánc megvalósítását a következő kiírásban szereplő módon tegyük:
A vázolt algoritmus a Doo-Sabin subdivision egy finomítását hajtja végre. Ellentétben az előző házi feladat algoritmusával, a mostani leírás inkább csak iránymutatásul szolgál, teret engedve az egyedi megoldások számára. Ennek következménye az is, hogy nincs szigorúan megkötve, hogy milyen módon kell implementálni a sémát.
Az itt leírtak az előző házi feladat algoritmusának a leírására, valamint a Half-Edge jegyzetre támaszkodnak.
Ezúttal is érdemes mutatók helyett indexeket alkalmazni, azonban annak következtében, hogy a lapok tetszőleges számú csúcsból állhatnak, nem tudunk olyan erőteljes kapcsolatot létrehozni a mutatók segítségével, mint a Loop subdivision esetén. A mesh tárolásához használt tömbök a következőek lesznek:
struct Vertex {
float x, y, z;
int edge; // egy olyan félél az indexe, mely ebből a csúcsból indul
};
struct Face {
int halfEdge;
}
struct HalfEdge {
int vertex
int next;
int pair;
int face;
}
További mezők is hozzáadhatók az egyes struktúrákhoz az algoritmus hatékony megvalósításának érdekében.
Mielőtt megkezdenénk a finomítást, hozzunk létre néhány segédtömböt:
EP
(Edge Points) - Az élek felezőpontjait tároló tömb.FP
(Face Points) - A lapok súlypontjait tároló tömb.NF
(New Faces) - Új lapok tömbje.NE
(New Edges) - Az új félélek tömbje.NV
(New Vertices) - Új csúcsok tömbje.Az NF
tömbbe rögtön másoljuk át a régi lapokat, az NE
tömbbe pedig a régi féléleket. Ennek az az oka, hogy az F-face-ek tárolását így már meg is valósítottuk, mindössze a félélek párjainak módosítására lesz szükség. Ez abból következik, hogy minden régi lap egy új F-face-t generál, ami pontosan ugyanannyi csúcsból és félélből áll, mint az eredeti lap.
Töltsük fel az EP
tömböt az élek által generált pontokkal! Ehhez menjünk végig a félélek tömbjén, és keressük meg minden félél kezdő- és végpontját. Utána vegyük ezek pozíciójának átlagát, megkapva e módon az él felezőpontját. Az előállított pontot helyezzük be az EP
tömbbe.
Mivel egy félél és a párja ugyanazt a pontot generálják, itt kisebb optimalizációra is lehetőség van, ha nem szeretnénk a szükséges pontok kétszeresét eltárolni.
A lapok súlypontjait a csúcsaik pozíciójának átlagaként számolhatjuk ki. Ezt úgy tudjuk megvalósítani, hogy az egyes lapokat alkotó féléleken cirkulálunk, melynek során összegezzük a csúcsok pozícióit, s az így kapott összeget végül elosztjuk az érintett csúcsok számával. A kapott pontokat tároljuk el a FP
tömbben!
Egy új csúcs pozíciójának előállításához négy pontra lesz szükség: $2$ edge point, $1$ face point és $1$ csúcs. Mivel egy csúcs minden olyan lapon generál egy új csúcsot, amiben részt vesz, érdemes a lapokon és a lapokon belül a csúcsokon végigfutni. Ennek megvalósításához ezúttal is a féléleken való cirkulálás lesz a megfelelő eszköz.
Ha tehát adott egy $f$ indexű lap, cirkuláljunk végig az összes félélen, mely a laphoz tartozik, és vegyük az adott lap $s$ súlypontját, a félél $v$ végpontját és a félél $f$, valamint a félél rákövetkezőjének $f^{\prime}$ felezőpontját. Az új csúcs $u$ pozíciója ezeknek átlaga lesz:
$$ u = \frac{1}{4}(v + f + f^{\prime} +s ) $$Minden él egy négyszöglapot fog képezni, melynek négy csúcsát az adott élben részt vevő két félél által képzett csúcsok adják. Ezekből tehát felépíthető egy új E-face, aminek két szomszédja az eredeti él két oldalán fekvő két F-face, másik két szomszédja pedig az él két végpontjából képzett két V-face.
Az előbbiek, azaz az F-face-ek szinte teljes egészében elkészültek az inicializációs lépésben szereplő másolása során, itt csak a félélek párjainak beállítására kell ügyelnünk.
Az eredeti csúcsokból képzett lapok összeállítása a legnehezebb feladat. Egy adott csúcs esetén végig kell mennünk az összes félélen, mely érintette a csúcsot. Minden ilyen félélből az előző lépés során lett egy E-face, mely a szomszédja lesz az újonnan létrehozott V-face-nek. A szomszédos E-face-ek segítségével be tudjuk állítani az új lapot alkotó félélek párjait és csúcsait.
A finomítás végrehajtásának utolsó lépése, hogy a mesht tároló adatszerkezetünkben a VA
, FA
és HA
tömböket kicseréljük az NV
, NF
és NE
tömbökre, azaz a régi mesht kicseréljük az új, simább változatra. A következő iterációban már ezt fogjuk tovább finomítani.
A demonstráció a séma működését mutatja be egy kockán. A kamerát a már ismert módon kezelhetjük (leírás itt). Újabb finomítást az ENTER leütésével hajthatunk végre.
In [7]:
addScript('js/doo-sabin-subdivision', 'doo-sabin-subdivision')
Out[7]:
In [6]:
def styling():
styles = open("../../styles/custom.html", "r").read()
return HTML(styles)
styling()
Out[6]: