In [7]:
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[7]:

Bézier-felület

Bevezetés

Miután megismerkedtünk számos különböző görbetípussal, ideje, hogy dimenziót lépve elkezdjünk a felületekkel is foglalkozni. Ebben a jegyzetben egy általános minta kerül bemutatásra, melynek segítségével paraméteres felületeket tudunk előállítani. Részletesen ezek közül a Bézier-felületet fogjuk szemügyre venni.

Hogyan készítsünk görbéből felületet?

A célunk, hogy az eddigi tudásunkra építve hozzunk létre felületeket. Az alapötlet ennek megfelelően rendkívül egyszerű lesz. Vegyünk egy tetszőleges térgörbét, melyet a $P_0, P_1, \ldots, P_n$ (háromdimenziós) kontrollpontok határoznak meg. Ha módosítjuk ezeknek a kontrollpontoknak a pozícióját a térben, akkor újabb görbét kapunk. Sorozatosan újabb görbéket képezve görbék egy családját kapjuk, melyek együttesen egy felületet definiálnak. A legegyszerűbb példa erre az, ha egy, az $xy$-síkon adott térgörbe kontrollpontjait a $z$-tengely mentén eltoljuk.

Felület létrehozása eltolással

Vegyünk az előzőnél egy általánosabb példát! Legyen adva egy tetszőleges paraméteres görbe a következő módon:

$$ \gamma(t) = \sum\limits_{i=0}^{n} b_i(t)P_i \qquad t \in [0, 1]. $$

Legyen továbbá adott egy $Q$ pont. A $\gamma(t)$ függvény által képzett görbét toljuk végig a $P_0$ és $Q$ pontok közötti szakaszon, így egy felületet képezve!

Ez azt jelenti, hogy a szakasz mentén haladva újabb és újabb görbéket kell létrehoznunk a $\gamma(t)$ függvény segítségével. Egy adott kontrollpontra alkalmazandó eltolás mértékét a következő képlettel számolhatjuk:

$$ p(s) = s(Q - P_0) \qquad s \in [0, 1] $$

Most már tehát a súlyfüggvénnyel nem az eredeti $P_i$ pontok valamelyikét, hanem mindig az eltolással képzett pontok egyikét kell megszoroznunk ahhoz, hogy valóban végighaladjunk a szakasz mentén. Ehhez definiáljuk az $i$-edik kontrollpont $s$ paraméter szerinti eltoltját a következőképpen:

$$ p_i(s) = P_i + s(Q - P_0) \qquad s \in [0, 1]. $$

$p_i$ birtokában az eredeti görbét már leírhatjuk

$$ \gamma(t) = \sum\limits_{i=0}^{n} b_i(t)p_i(0) \qquad t \in [0, 1] $$

formában. Vegyük észre, hogy $p_i$ paraméterét rögzítettük a $0$ értékre, mely azt jelenti, hogy az eltolás nem játszik szerepet. Ha bevezetünk egy új változót, ezzel kétváltozóssá téve a függvényt, akkor kapjuk a teljes felületet leíró kifejezést:

$$ \gamma(s, t) = \sum\limits_{i=0}^{n} b_i(t)p_i(s) \qquad s \in [0, 1], \quad t \in [0, 1]. $$

Az eredmény tehát nem más, mint görbék egy olyan családja, melyek kontrollpontjait egy függvény állítja elő.

Demonstráció

A demonstráció az előző ötletet szemléleti. A vezérléshez mind az egérre, mind a billentyűzetre szükség van. Ha rákattintunk a kék téglalapra, akkor az megkapja a fókuszt, és el tudja kapni a billentyűeseményeket. A kamera mozgatását a következő billentyűkkel vezérelhetjük:

  • W - a kamera mozgatása fölfele a henger palástján,
  • S - a kamera mozgatása lefele a henger palástján,
  • D - a kamera mozgatása jobbra a henger palástján,
  • A - a kamera mozgatása balra a henger palástján,
  • Numpad+ - a henger sugarának növelése (ha nincs kijelölt kontrollpont),
  • Numpad- - a henger sugarának csökkentése (ha nincs kijelölt kontrollpont).

Kattintással tudunk kontrollpontot kijelölni. Az éppen kijelölt kontrollpont zöld színnel lesz kirajzolva. Ha üres területre kattintunk, akkor eltűnik a kijelölés. Amennyiben van kiválasztott kontrollpont, akkor az X, Y és Z billentyűkkel tudjuk kijelölni a tengelyt, amelynek mentén mozgatni szeretnénk a pontot, és a Numpad+, Numpad- billentyűk használatával tudjuk a kontrollpontot a kijelölt tengely mentén elmozgatni.

Az öt kontrollpont közül négy egy Bézier-görbét határoz meg, az ötödik pont pedig az eltolás nagyságának és irányának kijelöléséért felel.


In [8]:
addScript("js/bezier-along-line", "bezier-along-line")


Out[8]:

Tenzorszorzat-felületek

Ha jobban megnézzük az eltolással előállított felületet leíró képletet, akkor láthatjuk, hogy a kontrollpontok helyére bevezetett $p_i(s)$ függvény gyakorlatilag tetszőleges vektorértékű függvény lehet. Visszatérve az eredeti ötlethez, mi lenne, ha egy szakasz helyett most egy görbe mentén mozgatnánk el az eredeti görbénk kontrollpontjait? Az így kapott felületeket tenzorszorzat-felületeknek nevezzük (tensor product spline patch).

Vezessük le a tenzorszorzat-felületek általános alakját kiindulva az ismert $\gamma(t)$ függvényből, azonban ezúttal $j$-t használva indexeléshez:

$$ \gamma(t) = \sum\limits_{j} b_j(t)P_j \qquad t \in [0, 1]. $$

Cseréljük le a $P_j$ kontrollpontokat

$$ p_j: [0, 1] \rightarrow \mathbb{R}^3 $$

függvényekre. E módon egy görbecsaládot kapunk, melynek első

$$ \gamma(t) = \sum\limits_{j} b_j(t)p_j(0) \qquad t \in [0, 1] $$

tagja adja az eredeti görbét. Az egész felületet ezúttal is egy kétváltozós függvény fogja előállítani:

$$ \gamma: [0, 1] \times [0, 1] \rightarrow \mathbb{R}^3, $$

ahol

$$ \gamma(s, t) = \sum\limits_{j} b_j(t)p_j(s). $$

Eddig ugyanott tartunk, mint a szakasz mentén eltolt felület esetében. Azonban most a görbecsalád egyes tagjait meghatározó kontrollpontokat görbéken adott pontokból származtatjuk. Például az első kontrollpont a

$$ p_0(s) = \sum\limits_{i}Q_{i0}q_i(s) $$

görbe mentén fog mozogni, ahol a $Q_{i0}$ pontok az ezt a görbét meghatározó kontrollpontok, $q(s)$ pedig valamilyen súlyfüggvény. Általánosan tehát

$$ p_j(s) = \sum\limits_{i}Q_{ij}q_i(s). $$

Bontsuk ki ennek ismeretében a $\gamma(s, t)$ függvényt:

$$ \begin{align*} \gamma(s, t) &= \sum\limits_{j} b_j(t)p_j(s) \\ &= \sum\limits_{j} b_j(t) \bigg(\sum\limits_{i}Q_{ij}q_i(s)\bigg) \\ &= \sum\limits_{j}\bigg(\sum\limits_{i} Q_{ij}q_i(s)b_j(t)\bigg) \\ \end{align*} $$

Ezzel készen vagyunk, megkaptuk a tenzorszorzat-felület általános formuláját. Láthatjuk, hogy a felület létrehozásához meg kell adnunk a $Q_{ij}$ kontrollpontokat, valamint egy $s$ és egy $t$ irányú görbét. Azonban ezek a görbék nem szükségszerűen azonosak. Dolgozhatunk $s$ irányban egy Bézier-görbével, $t$ irányban pedig B-Spline-nal. Ez hatalmas rugalmasságot biztosít, azonban leggyakrabban megegyező görbéket alkalmazunk (például harmadfokú Bézier-görbéket).

Demonstráció

A demonstrációk rendre bikubikus ($s$ és $t$ irányban is harmadfokú) Bézier-, B-Spline és Catmull-Rom Spline-felületeket mutatnak be. A kamerát kezelni és a kontrollpontokat manipulálni az előző példával azonos módon tudjuk.

Hasonlítsuk össze a különböző típusú felületeket, és vizsgáljuk meg, hogy milyen tulajdonságokat örököltek a görbetípustól, melyből származtatva lettek!

Bézier


In [9]:
addScript("js/bezier-surface", "bezier-surface")


Out[9]:

B-Spline


In [10]:
addScript("js/b-spline-surface", "b-spline-surface")


Out[10]:

Catmull-Rom


In [11]:
addScript("js/catmull-rom-spline-surface", "catmull-rom-spline-surface")


Out[11]:

In [12]:
def styling():
    styles = open("../../styles/custom.html", "r").read()
    return HTML(styles)
styling()


Out[12]: