3D-s számítógépes geometria és alakzatrekonstrukció 3c. Tesztkörnyezet III http://cg.iit.bme.hu/portal/node/312 https://portal.vik.bme.hu/kepzes/targyak/viiima01 Dr. Várady Tamás, Salvi Péter BME, Villamosmérnöki és Informatikai Kar Irányítástechnika és Informatika Tanszék
Tartalom Átlaggörbület-approximáció Mesh-navigáció [OpenMesh] Dialógusablakok felépítése [Qt] Layoutok Színezés [OpenGL] Fényvonalak Fényvonal-textúra kiszámítása Resource-ok használata [Qt] Textúrák használata [OpenGL] Környezeti textúrák
Harmadik fázis Átlaggörbület Színkódolás [kék zöld piros] Szélsőértékek meghatározása Fényvonalak [bővebben később] Tartomány szélének levágása Explicit megadás Dinamikus textúra Kimutat apró egyenetlenségeket Ehhez: Qt [menü, dialóguskészítés] OpenGL [színezés, textúra] OpenMesh [háromszögháló bejárás]
Átlaggörbület (ismétlés) Becslés háromszöglegyező alapján 1 βi e i i 4 H p = 1 A p 3 Feladatok: A(p) kiszámítása (és eltárolása) βi szögek kiszámítása H(p) eltárolása
Figyelem! A kódrészletek nem pontosak/teljesek Csak a lényeges részeket mutatják Gyakran kimaradnak... Scope-operátorok Deklarációk #include utasítások Stb. Teljes verzió A honlapon levő zip fájlokban Érdemes óra után részletesen átnézni
Háromszögterületek MyViewer.h: struct MyTraits : public OpenMesh::DefaultTraits { VertexTraits { double area; // total area of the surrounding triangles double mean; // approximated mean curvature ; FaceTraits { double area; ; ; typedef OpenMesh::TriMesh_ArrayKernelT<MyTraits> MyMesh; MyViewer.cpp: for(constfaceiter i = mesh.faces_begin(), ie = mesh.faces_end(); i!= ie; ++i) { HalfedgeHandle h1 = mesh.halfedge_handle(*i); HalfedgeHandle h2 = mesh.next_halfedge_handle(h1); mesh.data(*i).area = (halfedgevector(h1) % halfedgevector(h2)).norm() / 2; for(vertexiter i = mesh.vertices_begin(), ie = mesh.vertices_end(); i!= ie; ++i) { mesh.data(*i).area = 0; mesh.data(*i).mean = 0; for(constvertexfaceiter j(mesh, *i); j.is_valid(); ++j) mesh.data(*i).area += mesh.data(*j).area;
MyViewer.cpp Segédfüggvény: Vector MyViewer::halfedgeVector(HalfedgeHandle const &h) const { return mesh.point(mesh.to_vertex_handle(h)) mesh.point(mesh.from_vertex_handle(h)); Átlaggörbület-számítás: for(vertexiter i = mesh.vertices_begin(), ie = mesh.vertices_end(); i!= ie; ++i) { for(constvertexedgeiter j(mesh, *i); j.is_valid(); ++j) { double angle; HalfedgeHandle h1 = mesh.halfedge_handle(*j, 0); HalfedgeHandle h2 = mesh.halfedge_handle(*j, 1); i i Vector v = halfedgevector(h1); 1 β e i 4 H p = 1 mesh.is_boundary(h2)) A p 3 if(mesh.is_boundary(h1) angle = 0.0; else { Vector n1 = mesh.normal(mesh.face_handle(h1)); Vector n2 = mesh.normal(mesh.face_handle(h2)); angle = acos(std::min(std::max(n1 n2, -1.0f), 1.0f)); angle *= ((n1 % n2) v) >= 0.0? 1.0 : -1.0; mesh.data(*i).mean += angle * v.norm(); mesh.data(*i).mean *= 3.0 / 4.0 / mesh.data(*i).area;
A program szervezése MyViewer osztály Görbületszámítás double mean_min, mean_max, cutoff_ratio getmeanmin(), setmeanmin(double) stb. enum {COLOR_PLAIN, COLOR_MEAN coloring updatemeancurvature(bool update_min_max) Min/max update nem kell pl. fairing után Színezés meanmapcolor(double, double *) GUI Menü, dialógusablakok Billentyűleütésre (P/M) coloring változtatása
MyViewer.cpp void MyViewer::updateMeanCurvature(bool update_min_max) {... // Mean curvature computation if(update_min_max) updatemeanminmax(); void MyViewer::updateMeanMinMax() { size_t n = mesh.n_vertices(); if(n == 0) return; std::vector<double> mean; mean.reserve(n); for(vertexiter i = mesh.vertices_begin(), ie = mesh.vertices_end(); i!= ie; ++i) mean.push_back(mesh.data(*i).mean); std::sort(mean.begin(), mean.end()); size_t k = (double)n * cutoff_ratio; mean_min = std::min(mean[k-1], 0.0); mean_max = std::max(mean[n-k], 0.0);
MyViewer.cpp void MyViewer::meanMapColor(double d, double *color) const { if(d <= mean_min) { color[0] = 0.0; color[1] = 0.0; color[2] = 1.0; else if(d >= mean_max) { color[0] = 1.0; color[1] = 0.0; color[2] = 0.0; else if(d < 0) { double alpha = d / mean_min; color[0] = 0.0; color[1] = 1.0 - alpha; color[2] = alpha; else { double alpha = d / mean_max; color[0] = alpha; color[1] = 1.0 - alpha; color[2] = 0; void MyViewer::draw() {... std::vector<double> color(3, 1.0); for(constfaceiter i = mesh.faces_begin(), ie = mesh.faces_end(); i!= ie; ++i) { glbegin(gl_polygon); for(constfacevertexiter j(mesh, *i); j.is_valid(); ++j) { if(coloring == COLOR_MEAN) { meanmapcolor(mesh.data(*j).mean, &color[0]); glcolor3dv(&color[0]); glnormal3fv(mesh.normal(*j).data()); glvertex3fv(mesh.point(*j).data()); glend();...
GUI MyViewer.cpp: void MyViewer::keyPressEvent(QKeyEvent *e) {... case Qt::Key_P: coloring = COLOR_PLAIN; updategl(); break; case Qt::Key_M: coloring = COLOR_MEAN; updategl(); break;... MyWindow.cpp: MyWindow::MyWindow(QApplication *parent) : QMainWindow(), parent(parent) {... QAction *cutoffaction = new QAction(tr("Set &cutoff ratio"), this); cutoffaction->setstatustip(tr("set mean map cutoff ratio")); connect(cutoffaction, SIGNAL(triggered()), this, SLOT(setCutoff())); QAction *rangeaction = new QAction(tr("Set &range"), this); rangeaction->setstatustip(tr("set mean map range")); connect(rangeaction, SIGNAL(triggered()), this, SLOT(setRange())); QMenu *vismenu = menubar()->addmenu(tr("&visualization")); vismenu->addaction(cutoffaction); vismenu->addaction(rangeaction);
Kitérő Layoutok Widgetek elhelyezése QWidget::setLayout [a legkülső layout] QLayout::addWidget, QLayout::addLayout QBoxLayout, QGridLayout, QFormLayout Jó alapméretek, átméretezés kezelése Saját widgetekhez sizehint()
setcutoff() QDialog *dlg = new QDialog(this); QHBoxLayout *hb1 = new QHBoxLayout, *hb2 = new QHBoxLayout; QVBoxLayout *vb = new QVBoxLayout; QLabel *text = new QLabel(tr("Cutoff ratio:")); QDoubleSpinBox *sb = new QDoubleSpinBox; QPushButton *cancel = new QPushButton(tr("Cancel")); QPushButton *ok = new QPushButton(tr("Ok")); sb->setdecimals(3); sb->setrange(0.001, 0.5); sb->setsinglestep(0.01); sb->setvalue(viewer->getcutoffratio()); connect(cancel, SIGNAL(pressed()), dlg, SLOT(reject())); connect(ok, SIGNAL(pressed()), dlg, SLOT(accept())); ok->setdefault(true); hb1->addwidget(text); hb1->addwidget(sb); hb2->addwidget(cancel); hb2->addwidget(ok); vb->addlayout(hb1); vb->addlayout(hb2); dlg->setwindowtitle(tr("set ratio")); dlg->setlayout(vb); if(dlg->exec() == QDialog::Accepted) { viewer->setcutoffratio(sb->value()); viewer->updategl();
setrange() QDialog *dlg = new QDialog(this); QGridLayout *grid = new QGridLayout; QLabel *text1 = new QLabel(tr("Min:")), *text2 = new QLabel(tr("Max:")); QDoubleSpinBox *sb1 = new QDoubleSpinBox, *sb2 = new QDoubleSpinBox; QPushButton *cancel = new QPushButton(tr("Cancel")); QPushButton *ok = new QPushButton(tr("Ok")); double max = std::numeric_limits<double>::max(); sb1->setdecimals(5); sb2->setdecimals(5); sb1->setrange(-max, 0.0); sb2->setrange(0.0, max); sb1->setsinglestep(0.01); sb2->setsinglestep(0.01); sb1->setvalue(viewer->getmeanmin()); sb2->setvalue(viewer->getmeanmax()); connect(cancel, SIGNAL(pressed()), dlg, SLOT(reject())); connect(ok, SIGNAL(pressed()), dlg, SLOT(accept())); ok->setdefault(true); grid->addwidget( text1, 1, 1, Qt::AlignRight); grid->addwidget( sb1, 1, 2); grid->addwidget( text2, 2, 1, Qt::AlignRight); grid->addwidget( sb2, 2, 2); grid->addwidget(cancel, 3, 1); grid->addwidget( ok, 3, 2); dlg->setwindowtitle(tr("set range")); dlg->setlayout(grid); if(dlg->exec() == QDialog::Accepted) { viewer->setmeanmin(sb1->value()); viewer->setmeanmax(sb2->value()); viewer->updategl();
Tükröződési vonalak Struktúrált fények visszaverődései Intuitív, G1/G2 hiba Folytonos, sima: G2 Folytonos, tört: G1
Fényvonalak (Isophotes) Egyszerűbben számolható Referencia pontból sugarak Beesési szög a normálvektorral: p ref p α = cos n p ref p 1 Megjelenítés: piros/fehér 5 fokonként Probléma: Referencia pont változik textúra változik Új textúrát generálni lassú
OpenGL trükk Környezeti térkép (environment map) Dinamikus textúra A felületi pont textúra pont hozzárendelés a nézőponttól és a normálvektortól függ Ötlet: referenciapont = nézőpont OpenGL gömb leképezés
Gömb leképezés (x,y,z) a visszaverődés iránya [szem-koordinátarendszerben] Ehhez rendelt textúra ([0,1]x[0,1]-ben): s, t = x 1 y 1 2 2 2,, ahol m = 2 x y 1 z m 2 m 2 Lekódolja az összes irányt 3 koordináta, de egységvektor két szabadságfok Ez a kódolás látványos (projekció) Könnyen rátehető panoráma is
Fényvonalak kiszámítása Feladat: (s,t) textúrakoordináta α szög (x,y,z) egységvektor, tehát 8 x 2 y 2 2 2 z = 1 = 8 s s t t 3 2 m cos 1 z α= 2 és, mivel z épp a referenciairány Ez alapján kiszínezhető a textúra Képfájlként fogjuk beolvasni
A Qt resource rendszer Fájlok tárolása a futtatható állományban Tipikusan képek, szövegek (HTML stb.) XML-jellegű leíró fájl (.qrc) Ebből adat-cpp fájl generálódik Formátum: <!DOCTYPE RCC><RCC version="1.0"> <qresource> <file>isophotes.png</file> </qresource> </RCC> Projekt fájl: RESOURCES = sample-framework.qrc Qt-s fájlműveleteknél :/ elérési út
OpenGL textúra készítés Textúra ID Paraméterek (gltexparameter[if]v?) Külső pontra mit csináljon (pl. csempézés) Minőségjavító min/mag filterek stb. Környezeti beállítások (gltexenv[if]v?) Generálás (n db.): glgentextures(n, addr) Hozzárendelés: glbindtexture(type, id) Törlés (n db.): gldeletetextures(n, addr) Textúra, árnyalás és anyag (szín) kapcsolata Textúra készítese glteximage2d(...sok paraméter...)
OpenGL textúra koordináták glenable(gl_texture_2d) Megadható pontonként gltexcoord[1234][dfis]v? Az aktuális (bind-olt) textúrára vonatkozik Generált koordináták gltexgen[dfi]v? : generálás paraméterei GL_TEXTURE_GEN_MODE : mód beállítása GL_OBJECT_LINEAR GL_EYE_LINEAR GL_SPHERE_MAP GL_OBJECT_PLANE / GL_EYE_PLANE : sík megadása glenable(gl_texture_gen_s) glenable(gl_texture_gen_t)
Textúra inicializálás MyViewer.cpp: void MyViewer::init() { gllightmodeli(gl_light_model_two_side, 1); QImage img(":/isophotes.png"); glgentextures(1, &isophote_texture); glbindtexture(gl_texture_2d, isophote_texture); gltexparameteri(gl_texture_2d, GL_TEXTURE_MIN_FILTER, GL_LINEAR); gltexparameteri(gl_texture_2d, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gltexparameteri(gl_texture_2d, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); gltexparameteri(gl_texture_2d, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glteximage2d(gl_texture_2d, 0, GL_RGBA8, img.width(), img.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, img.converttoformat(qimage::format_argb32).bits()); GL_LINEAR: (lineáris interpoláció) Távoli felületek [MIN] Közeli felületek [MAG] GL_CLAMP_TO_EDGE: Kívül eső pontokat a szélére húzza
Textúra a háromszöghálón MyViewer.cpp: void MyViewer::draw() {... if(coloring == COLOR_PLAIN) glcolor3dv(&color[0]); else if(coloring == COLOR_ISOPHOTES) { glbindtexture(gl_texture_2d, isophote_texture); gltexenvf(gl_texture_env, GL_TEXTURE_ENV_MODE, GL_DECAL); glenable(gl_texture_2d); gltexgeni(gl_s, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); gltexgeni(gl_t, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); glenable(gl_texture_gen_s); glenable(gl_texture_gen_t); for(constfaceiter i = mesh.faces_begin(), ie = mesh.faces_end(); i!= ie; ++i) {... // Drawing the triangles if(coloring == COLOR_ISOPHOTES) { gldisable(gl_texture_gen_s); gldisable(gl_texture_gen_t); gldisable(gl_texture_2d); gltexenvf(gl_texture_env, GL_TEXTURE_ENV_MODE, GL_MODULATE);...