Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!
Zadejte Vaši e-mailovou adresu:
Kamarád fotí rád?
Přihlas ho k odběru fotomagazínu!
Zadejte e-mailovou adresu kamaráda:
C/C++
Jak vyzrát na kolize 3.díl - Jak na to?
delphi_kolize
26. února 2002, 00.00 | V minulém článku jsme si řekli, jak zjistit několik druhů kolizí v prostorů, které sice nebyly moc přesné, ale zato byly rychlé. Dnes nadešel čas řící si, jak spočítat přesný průnik přímky a plochy, zda nastala kolize mezi plochou a koulí.
Každý začátek je těžký a já začátky
opravdu nesnáším, proto přikročím rovnou k věci. V minulém
článku jsme si řekli, jak zjistit pár druhů kolizí v prostorů,
které sice nebyly moc přesné, ale zato byly rychlé. Teď nadešel
čas řící si, jak spočítat přesný průnik přímky
a plochy, jak zjistit zda nastala kolize mezi plochou a koulí, popřípadě
plochou a kvádrem. Někdy se může také hodit zjišťování
kolizí dvou paprsků. Nechme tedy povídání a vrhněme se rovnou na věc.
Ze začátku si nadefinujeme pár základních datových typů. Kouli a
Kvádr použijeme z předešlého článku, a tak nám zbývá plocha a
paprsek. Jejich definice je velice jednoduchá.
|
Paprsek
V prostoru můžeme přímku vyjádřit pouze jedním způsobem
a to parametricky. K parametrickému zapsání přímky potřebujeme dva
údaje. Prvním je výchozí bod, označme si jej A a směrový
vektor v. Zápis bude vypadat takto : X=A + v*t Přičemž
X je libovolný bod přímky a t je parametr, kterým násobíme
směrový vektor v, aby jsme se z bodu A dostali do X.
Jak bude vypadat inicializační funkce :
|
Plocha
U plochy máme možností více. Buď můžeme zvolit parametrické
vyjádření (k němu potřebujeme minimálně dvě přímky)
nebo zvolíme zápis obecný ve tvaru ax + by + cz + d = 0. Co to znamená?
a,b,c jsou složky normálového vektoru a d je nějaké
číslo, které musíme dopočítat dosazením do rovnice. Do rovnice
dosadíme již zmíněné složky normálového vektoru a,b,c. Z x,y,z
dosadíme libovolný bod naší roviny a vypočítáme d. V praxi
vypadá celý výpočet asi takto : d = -ax - by -cz.
Nadefinujme si tedy funkci :
|
A co teď?
Když jsme si nadefinovali základní datové typy, můžeme se vrhnout na
zjišťování kolizí. Jako první by jsem rád zveřejnil způsob
jak otestovat zda koule narazila do plochy či ne. Tento způsob je
velice jednoduchý. Při zjišťování jestli koule leží v ploše o
rovnici ax+by+cz+d = 0 stačí zjistit jak daleko je střed koule od
roviny, pokud tato vzdálenost bude menší než poloměr koule, nastala
kolize.
x,y,z je střed kružnice, nx,ny,nz jsou složky normálového vektoru. A to je celé, nic složitého že? :-)
|
Jak na krychli?
Aby jsme mohli vyřešit i tento problémek, musíme si vlastně uvědomit kdy nastane kolize kvádr vs. rovina. Nejjednodušší způsob je, zjistit na jaké straně roviny leží všech osm bodů našeho kvádru. Pokud leží ve stejném poloprostoru, kolize nenastala (za předpokladu, že během jednoho kroku neprolétla krychle z jednoho poloprostoru do druhého). Jestliže alespoň jeden bod leží v druhém poloprostoru, kolize nastala. Jak určit poloprostory? Budeme li vycházet z obecné rovnice roviny ax+by+cz+d=0, logickou úvahou se dostaneme k závěru, že když bod leží v rovině, rovnice je rovna nule, pokud v ní neleží, bude ax+by+cz+d <> 0. Tímto rozlišíme dva poloprostory. Pro jeden platí nerovnice ax+by+cz < 0 a pro druhý se obrací znaménko nerovnosti. Jak jsem již řekl, potřebujeme zjistit v jakém poloprostoru leží body kvádru. Proto spočítáme rovnici ax+by+cz+d=0 pro libovolný bod kvádru (nejlépe vrchol) a zjistíme znaménko nerovnosti. Pro další body musí platit stejná nerovnost jako pro první. Pokud najdeme nějaký bod, pro který je znaménko otočené, máme vyhráno a z vesela můžeme oznámit, že ke kolizi došlo.
|
Co s paprskem?
To je docela dobrá otázka. K čemu ho budeme potřebovat? Představte si třeba tuto situaci : Vy jakožto programátor seriozní, zahraniční, prosperující firmy dostanete za úkol napsat prográmek, který zjistí místo zásahu zdi kulkou po jejím vystřelení. Tetnto problém se dá velice efektivně vyřešit pomocí testování průniku paprsek vs. plocha. Zde budeme opět potřebovat naši obecnou rovnici roviny a parametrické vyjádření přímky. Jak jsem již řekl, rovina má rovnici ax+by+cz+d=0 a přímka X=A+v*t. Z čehož vyplývá, že :
ax+by+cz+d=0
x = Ax + vx * t
y = Ay + vy * t
z = Az + vz * t
Tady máme pár rovnic, z nichž první vyjadřuje rovinu a zbylé tři paprsek / přímku. Když dosadíme x,y,z do rovnice roviny, zbude nám jediná a to t.
a*(Ax + vx*t) + b*(Ay + vy*t) + c*(Az + vz*t) + d=0
a*vx*t + b*vy*t + c*vz*t = -d - a*Ax - b*Ay - c*Az
t*(a*vx + b*vy + c*vz) = -d - a*Ax - b*Ay - c*Az
Z toho vyplývá, že rovnice nemá řešení, když a*vx + b*vy + c*vz je rovno nule. Kdy tento případ nastane? Nastane tehdy, když normálový vektor roviny bude kolmý na směrový vektor přímky. Důkaz je jednoduchý : a*vx + b*vy + c*vz je skalární součin normálového vektoru(a,b,c) a směrového vektoru přímky v. Skalární součin je roven 0 právě když odchylka vektorů je 90° z toho vyplývá, že přímka a rovina jsou rovnoběžné.
V případě, že nejsou rovnoběžné, můžeme velice
jednoduše spočítat místo průniku. Do rovnice paprsku / přímky
dosadíme t, které nám vyšlo a máme vyhráno!!!
x=Ax + v*t
...
Někdy se nám může také hodit zjištění průniku dvou paprsků. Této metodě bylo věnováno již mnoho článků, ale žádný jsem moc nepochopil, tak jsem si našel vlastní způsob, který není sice možná nejlepší, ale je snadno pochopitelný. Vezměme tedy dva paprsky, označíme je např. pap1,pap2. Jestli mají mít průnik, musí zákonitě ležet v jedné rovině. A nesmí být kolineární (tzn.rovnoběžné). K určení rovnoběžnosti nám postačí menší test. Stačí směrový vektor pap1 vynásobit určitým číslem a když dostaneme směrový vektor pap2, jsou kolineární. číslo=pap1.vx/pap2.vx
Jak určit jestli leží ve stejné rovině? K určení roviny potřebujeme bod a normálový vektor. Jestliže mají naše přímky ležet ve stejné rovině, musí mít společný normálový vektor. Z toho vyplývá, že když vektorovým součinem vynásobíme směrové vektory přímek, dostaneme normálový vektor, který je zákonitě kolmý na oba dva směrové vektory. n=pap1.v X pap2.v. Teď máme normálový vektor na směrové vektory obou přímek. Jenže vektory můžeme v prostoru libovolně posouvat. Jestliže tedy chceme zjistit jestli leží ve stejné rovině, stačí vytvořit rovnice dvou rovin s normálovým vektorem n, ale pokaždé pro jiný bod. První rovnici pro výchozí bod prvního paprsku a druhou rovnici pro výchozí bod druhého paprsku. Jelikož mají stejné normálové vektory, tak se celá rovnice bude lišit pouze koeficientem d (v případě, že neleží ve stejné rovině).
|
Dobře, těď jsme zjistili, jestli ke kolizi dojde. Ale co když chceme vědět, kde leží průnik? Stačí, když vytvoříme další rovinu, ve které leží první přímka, ale neleží v ní přímka druhá a spočítáme průnik druhé přímky s touto rovinou. Jak bude vypadat naše nová rovina? Aby v ní ležela přímka pap1 a přímka pap2 ne, musíme si najít takový normálový vektor n2, aby byl kolmý na pap1 a ne na pap2. Řešení je jednoduché vektorovým součinem mezi sebou vynásobíme směrový vektor pap1 a normálový vektor, který jsme si spočítali výše. Nyní určíme rovnici nové roviny, která má normálový vektor n2=pap1.v X n, bod [pap.x; pap.y; pap.z] a spočítáme průnik s přímkou pap2 (viz. Co s paprskem).
Jak odrazit objekt od plochy?
Jak známe ze základní školy : "úhel dopadu se rovná úhlu
odrazu". Jak toto provést v prostoru, když máme nadefinovanou odrazovou
plochu již dobře známou rovnicí ax+by+cz+d=0 a paprsek (zastupuje
nám objekt) X=A + v*t. V podstatě převrátíme směrový
vektor paprsku okolo normálového vektoru plochy. Aby jsme si ušetřili
starosti, budeme počítat s jednotkovými vektory. Označme si tedy
normálový vektor jako n a vektor dopadu jako d, vektor odrazu
jako o.
2) Jelikož počítáme s jednotkovými vektory, můžeme pomocí skalárního
součinu promítnout vektor -d do vektoru n, dostaneme vektor u
= (-d*n)*n (-d*n...je skalár)
3) Když teď vektor u vynásobíme 2x a přičteme vektor d
(obr.4), dostaneme vektor o !!!
Celý zápis vypadá takto o = 2*((-d*n)*n) + d
Jak to tedy použít?
Vytvořil jsem prográmek, který demonstruje dnes probrané případy a
případy z minulého článku (kolize koule x koule, koule x kvádr).
Jak tedy vypadá. V krychli nadefinované pomocí 6 ploch se pohybují 2 balóny,
které se odrážejí od stěn a sami od sebe. U jednoho balónu se počítá
bod dopadu (jeho středu) na nejbližší stěnu ve směru pohybu
(znázorňuje se úsečkou od středu koule do bodu dopadu). Pokud
chcete místo koulí použít kvádry, stačí do C_FYZ_OBJECT míst C_COLLISION_SPHERE
dát C_COLLISION_BOX a patřičně ho nastavit (viz. Testování
kolizí 2.díl). Prográmek je napsaný pod SDLkem (www.libsdl.org)
a OpenGL, pokusil jsem se dát popisky všude, kde bylo potřeba, aby odpůrci
OpeGL nebo SDL měli šanci program pochopit. K ovládání slouží následující
klávesy :
ESC ... ukončí program
S ... kamera se zastaví
R ... kamera se začne otáčet
P ... pauza
O ... zrušení pauzy
F ... nastaví koule na výchozí pozici a změní náhodně
vektory pohybu (obr1)
D ... nastaví koule na výchozí pozici a nastaví vektory tak, že se
koule odrazí od stěny, pak od sebe, od stěny, atd. (obr2.)
Download : kolizedemo.rar (295kB)
Co říci závěrem?
No asi jen to, že vám všem přeji hodně štěstí při vytváření vlastních prográmků!!! Pro zájemce, kteří by se chtěli dozvědět i další způsoby testování doporučuji stránku www.realtimerendering.com nebo www.acm.org/tog/GraphicsGems
Obsah seriálu (více o seriálu):
- Jak vyzrát na kolize díl. I
- Jak vyzrát na kolize 2.díl - kolize v prostoru
- Jak vyzrát na kolize 3.díl - Jak na to?
-
25. listopadu 2012
-
30. srpna 2002
-
10. října 2002
-
4. listopadu 2002
-
12. září 2002
-
25. listopadu 2012
-
28. července 1998
-
31. července 1998
-
28. srpna 1998
-
6. prosince 2000
-
27. prosince 2007
-
4. května 2007