Zobrazit předchozí téma :: Zobrazit následující téma |
Autor |
Zpráva |
perry
Založen: 28. 07. 2009 Příspěvky: 879
|
Zaslal: 7. říjen 2014, 19:24:06 Předmět: Pohyb objektů ve 3D pomocí myši |
|
|
Zdravím všechny,
posunuji engine na další level a přidávám dynamickou editaci scény. Mám funkční picking (testoval jsem na objektech i na průniku s rovinou). Na základě funkčnosti pickingu usuzuji, že mám asi dobře i projekci 2D -> 3D (akorát při debug výpisu z toho nic nepoznám, protože to vrací body někde v "nekonečnu" u far rovin).
Každopádně, chci teď udělat pohyb objektů. Když udělám řešení - nakliknu objekt a hýbu myší, tak mi objekt strašně uskakuje a neodpovídá pohybu myši
Mám to jako:
kód: |
1) Při kliku si uložím offset - kliknutá unProject pozice vs střed objektu
2) Hýbu myší a provádím unProject - pozici objektu nastavím na tuhle pozici a přičtu offset
|
Mám něco blbě, nebo je to prostě neuřiditelné takhle a musím udělat zámky pohybu na určitou rovinu (např. když jsem zkoušel pohyb rovnoběžně s plochou s normálou UnitY(), tak to běhalo) ?
Jinak kdyby se někdo chtěl podívat, tak tady je můj kód pro unProject (pouze pro perspektivní projekci, ale měl by fungovat - alespoň na papíře fungoval)
kód: |
ViewportCoordSystem Camera::GetViewportSystem() const
{
ViewportCoordSystem viewportCoord;
viewportCoord.w = this->cameraPos - this->lookAt;
viewportCoord.w.Normalize();
viewportCoord.u = MyMath::Vector3::Cross(viewportCoord.w, this->up);
viewportCoord.u.Normalize();
viewportCoord.v = MyMath::Vector3::Cross(viewportCoord.u, viewportCoord.w);
viewportCoord.v.Normalize();
float d = (this->viewportHeight * 0.5f) * (1.0f / tanf(this->fov * 0.5f));
viewportCoord.origin = this->cameraPos;
viewportCoord.origin -= d * viewportCoord.w;
return viewportCoord;
}
MyMath::Vector3 Camera::MapViewport2Dto3D(const ViewportCoordSystem & viewportSystem, const MyMath::Vector2 & point) const
{
MyMath::Vector3 res = viewportSystem.origin;
res += (point.X - this->viewport.Width * 0.5f) * viewportSystem.u;
res += (this->viewport.Height * 0.5f - point.Y) * viewportSystem.v;
return res;
}
|
_________________ Perry.cz |
|
Návrat nahoru |
|
|
mar
Založen: 16. 06. 2012 Příspěvky: 608
|
Zaslal: 7. říjen 2014, 22:27:05 Předmět: Re: Pohyb objektů ve 3D pomocí myši |
|
|
No nevím, to bude asi nějaká drobnost (ad 2: určitě to správně přičítáš/odečítáš?),
protože unproject s konstantním z by měl dávat body na rovině kolmé ke kameře, takže by to, co popisuješ, mělo fungovat, pokud je tedy ten unproject do world space
Jinak co se týká samotného unprojectu, proč nepoužiješ matici (inverzní mvp)? To pak nebudeš muset řešit typ projekce, jestli to je perspektivní nebo orto.
Hmm, nemůže být chyba ještě v něčem jiném?
A nebylo by video?
EDIT: a co se týká té kamery a systému, nebylo by lepší to mít taky jako matice?
Řádky 3x3 rotační matice (nebo sloupce, podle toho jak s tím pracuješ) ti určují jednotlivé osy (třeba doprava, nahoru, dopředu). A nemusíš pak ukládat lookAt bod,
každopádně máš originální řešení, tohle jsem ještě neviděl
EDIT2: jasně, když o otom přemýším, tak je to špatně, musíš to udělat jinak
ten posun není konstantní právě kvůli perspektivě
takže by to mělo fungovat pokud si pro grabu vytvoříš rovinu, která je kolmá ke kameře a prochází středem objektu. Tuhle rovinu protneš paprskem, který jde od kamery k unprojectnutému bodu v místě, kde grabuješ.
Tenhle bod si uložíš odečtený od středu objektu jako referenci (třeba A).
Pak jak se myš bude hýbat, musíš protnout rovinu s paprskem, který jde od kamery k unprojectnutému novému bodu (průsečík bude třeba B).
No a pak nová pozice bude A+B, v podstatě totéž, cos popisoval, jenom teď by to mělo (doufám) sedět. |
|
Návrat nahoru |
|
|
mar
Založen: 16. 06. 2012 Příspěvky: 608
|
Zaslal: 8. říjen 2014, 04:22:44 Předmět: Re: Pohyb objektů ve 3D pomocí myši |
|
|
Hmm tak ani tohle se mi nelíbí. Prostě potřebuješ referenční bod, kterým pak proložíš rovinu, to zůstane stejné.
Problém je, že tento ref. bod musí ležet uvnitř frusta.
Ideálně by to byl průsečík s objektem.
Spustil jsem Blender a koukám, že to zrovna tohle nedělají (proč asi
Takže je potřeba najít dobrý referenční bod.
To bude takový, že bude ležet na přímce prvního grabu, bude uvnitř frusta a bude co nejblíž středu objektu.
Takže řešení by měl být jednoduše kolmý průmět středu objektu na pick ray.
Pokud je uvnitř frusta (tj. v tomto případě leží mezi near a far plane), je to ref. bod.
V opačném případě prostě vzít průsečík paprsku buď s near nebo far plane frusta.
Teď už je jenom otázka, jestli to bude fungovat (bez záruky) |
|
Návrat nahoru |
|
|
perry
Založen: 28. 07. 2009 Příspěvky: 879
|
Zaslal: 8. říjen 2014, 07:12:33 Předmět: |
|
|
Díky... zkusím.
Ad ten výpočet - to jsem vzal z ray traceru. Inverzní matici mě nenapadlo použít, ale spočítat inverzi mžná bude pomalejší, než to co tam mám (i když to bude univerzální).
S tou rovinou kolmou ke kameře by to fungovat mělo asi. To mě napadlo udělat, ale prívě jsem si nebyl úplně jistý
citace: |
...unproject s konstantním z by měl dávat body na rovině kolmé ke kameře |
Tohle mi myslím vychází. Z mám pořád stejné a mění se mi X a Y toho unProject bodu _________________ Perry.cz |
|
Návrat nahoru |
|
|
mar
Založen: 16. 06. 2012 Příspěvky: 608
|
Zaslal: 8. říjen 2014, 08:35:54 Předmět: |
|
|
perry napsal: |
Inverzní matici mě nenapadlo použít, ale spočítat inverzi mžná bude pomalejší, než to co tam mám (i když to bude univerzální).
|
No to dost pochybuji, to je opět mýtus, že výpočet inverzní 4x4 je pomalý (viděl jsem tedy hodně prasácké implementace, typicky nesmyslně pomocí eliminace).
Navíc si to přece můžeš cachovat v té kameře, kolikrát to měníš za frame? Jednou?
Inverzi jsem si původně smolil sám, bylo to ok, ale pak jsem našel paper, kde to bylo mnohem elegantněji vyřešeno s o trochu menším počtem operací.
kód: |
Mat4 Mat4::GetInverse() const
{
// reference: http://www.geometrictools.com/Documentation/LaplaceExpansionTheorem.pdf
const Mat4 &t = *this;
Float s0 = t[0][0]*t[1][1] - t[0][1]*t[1][0];
Float c5 = t[2][2]*t[3][3] - t[2][3]*t[3][2];
Float s1 = t[0][0]*t[1][2] - t[0][2]*t[1][0];
Float c4 = t[2][1]*t[3][3] - t[2][3]*t[3][1];
Float s2 = t[0][0]*t[1][3] - t[0][3]*t[1][0];
Float c3 = t[2][1]*t[3][2] - t[2][2]*t[3][1];
Float s3 = t[0][1]*t[1][2] - t[0][2]*t[1][1];
Float c2 = t[2][0]*t[3][3] - t[2][3]*t[3][0];
Float s4 = t[0][1]*t[1][3] - t[0][3]*t[1][1];
Float c1 = t[2][0]*t[3][2] - t[2][2]*t[3][0];
Float s5 = t[0][2]*t[1][3] - t[0][3]*t[1][2];
Float c0 = t[2][0]*t[3][1] - t[2][1]*t[3][0];
Float d = s0*c5 - s1*c4 + s2*c3 + s3*c2 -s4*c1 + s5*c0;
CORE_ASSERT( !IsAlmostZero(d) );
d = 1.0f / d;
return Mat4(
(t[1][1]*c5 - t[1][2]*c4 + t[1][3]*c3) * d,
(t[0][2]*c4 - t[0][1]*c5 - t[0][3]*c3) * d,
(t[3][1]*s5 - t[3][2]*s4 + t[3][3]*s3) * d,
(t[2][2]*s4 - t[2][1]*s5 - t[2][3]*s3) * d,
(t[1][2]*c2 - t[1][0]*c5 - t[1][3]*c1) * d,
(t[0][0]*c5 - t[0][2]*c2 + t[0][3]*c1) * d,
(t[3][2]*s2 - t[3][0]*s5 - t[3][3]*s1) * d,
(t[2][0]*s5 - t[2][2]*s2 + t[2][3]*s1) * d,
(t[1][0]*c4 - t[1][1]*c2 + t[1][3]*c0) * d,
(t[0][1]*c2 - t[0][0]*c4 - t[0][3]*c0) * d,
(t[3][0]*s4 - t[3][1]*s2 + t[3][3]*s0) * d,
(t[2][1]*s2 - t[2][0]*s4 - t[2][3]*s0) * d,
(t[1][1]*c1 - t[1][0]*c3 - t[1][2]*c0) * d,
(t[0][0]*c3 - t[0][1]*c1 + t[0][2]*c0) * d,
(t[3][1]*s1 - t[3][0]*s3 - t[3][2]*s0) * d,
(t[2][0]*s3 - t[2][1]*s1 + t[2][2]*s0) * d
);
}
|
|
|
Návrat nahoru |
|
|
]semo[
Založen: 29. 07. 2007 Příspěvky: 1526 Bydliště: Telč
|
Zaslal: 8. říjen 2014, 08:37:17 Předmět: |
|
|
Jestli to chápu dobře, tak chceš chytit objekt a pohybovat s ním v rovině screenu? To je snadná věc. Delta kurzoru myši X je ve směru kemera "Right vektoru", a Y je ve směru "-Up vektoru". Ale aby to fungovalo správně, potřebuješ zjistit o kolik s tim objektem hnout tak, aby držel pod kurzorem.
Zkus tohle:
- promítni na screen bod vzdálený stejně jako tažený objekt (P->S)
- ten bod posuň o deltu myši (S->S')
- promítni ho zpátky (S'->P')
- udělej rozdíl původního bodu a posunutého bodu (Delta = P - P') a to je vzdálenost, o kterou posuneš tažený objekt
kód: |
QVector4D P = QVector4D(0, 0, -objectDistanceToCamera, 1);
QVector4D S = m_ProjMatrix * P; /* project to Screen */
S /= S.w();
QVector4D S2 = S;
S2.setX(S2.x() + (float) mouseDeltaX / (float) (viewportWidth/2)); /*note: S is in range -1.0 .. 1.0*/
S2.setY(S2.y() + (float) mouseDeltaY / (float) (viewportHeight/2));
QVector4D P2 = m_ProjMatrix.inverted() * S2; /* unproject to camera relative Point*/
P2 /= P2.w();
float deltaX = P.x() - P2.x();
float deltaY = P.y() - P2.y();
QVector3D right(m_CameraMatrix.column(0));
QVector3D up(m_CameraMatrix.column(2));
Object->Translate(right * deltaX + up * deltaY);
|
Tenhle ukázkový kód je odvozen z pohybu kamery v mým editoru. Tam je to funkční. Tohle by mělo být taky (až na nějaký směry pohybu a td). A jak už zmínil mar, tam kde já mám column(), ty můžeš mít row(). V takovým případě budeš mít i přehozený násobení matice a bodu. Ale to je detail.
Jo a ještě dodávám, že to určitě jde udělat edlegantnějc, třeba rovnou tu distanci vynásobit něčím z projekční matice. Možná bys to mohl omrknout. _________________ Kdo jede na tygru, nesmí sesednout.
---
http://www.inventurakrajiny.cz/sipka/
Aquadelic GT, Mafia II, simulátory |
|
Návrat nahoru |
|
|
perry
Založen: 28. 07. 2009 Příspěvky: 879
|
Zaslal: 9. říjen 2014, 10:54:10 Předmět: |
|
|
Tak semův kód funguje, díky. Teď už tam jen poladím nějaké detaily
Ad ta onverze. Zjistil, jsem, že to nehraje roli Ten můj původní kód měl navíc nějaký bug a body, které byly mimo obraz se divně transformovaly (zajímavý, že na papíru to šlo ) _________________ Perry.cz |
|
Návrat nahoru |
|
|
|